diff --git a/frappe/boot.py b/frappe/boot.py index f7b817a12e..bb2663cb1c 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -54,7 +54,6 @@ def get_bootinfo(): add_allowed_pages(bootinfo) load_translations(bootinfo) load_conf_settings(bootinfo) - load_startup_js(bootinfo) # ipinfo if frappe.session['data'].get('ipinfo'): @@ -120,10 +119,11 @@ def get_fullnames(): return d -def load_startup_js(bootinfo): - bootinfo.startup_js = "" +def get_startup_js(): + startup_js = [] for method in frappe.get_hooks().startup_js or []: - bootinfo.startup_js += frappe.get_attr(method)() + startup_js.append(frappe.get_attr(method)() or "") + return "\n".join(startup_js) def get_user(bootinfo): """get user info""" diff --git a/frappe/core/doctype/event/event.py b/frappe/core/doctype/event/event.py index 1c802baf46..aa4312e686 100644 --- a/frappe/core/doctype/event/event.py +++ b/frappe/core/doctype/event/event.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import getdate, cint, add_months, date_diff, add_days, nowdate +from frappe.core.doctype.user.user import STANDARD_USERS weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] @@ -52,7 +53,9 @@ def send_event_digest(): today = nowdate() for user in frappe.db.sql("""select name, email, language from tabUser where ifnull(enabled,0)=1 - and user_type='System User' and name not in ('Guest', 'Administrator')""", as_dict=1): + and user_type='System User' and name not in ({})""".format(", ".join(["%s"]*len(STANDARD_USERS))), + STANDARD_USERS, as_dict=1): + events = get_events(today, today, user.name, for_reminder=True) if events: text = "" diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 5bd3bce4fe..b528640b8f 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -7,6 +7,8 @@ from frappe.utils import cint, now, cstr from frappe import throw, msgprint, _ from frappe.auth import _update_password +STANDARD_USERS = ("Guest", "Administrator") + class DocType: def __init__(self, doc, doclist): self.doc = doc @@ -14,7 +16,7 @@ class DocType: def autoname(self): """set name as email id""" - if self.doc.name not in ('Guest','Administrator'): + if self.doc.name not in STANDARD_USERS: self.doc.email = self.doc.email.strip() self.doc.name = self.doc.email @@ -23,31 +25,16 @@ class DocType: def validate(self): self.in_insert = self.doc.fields.get("__islocal") - if self.doc.name not in ('Guest','Administrator'): + if self.doc.name not in STANDARD_USERS: self.validate_email_type(self.doc.email) - self.validate_max_users() self.add_system_manager_role() self.check_enable_disable() - if self.in_insert: - if self.doc.name not in ("Guest", "Administrator"): - if self.doc.new_password: - # new password given, no email required - _update_password(self.doc.name, self.doc.new_password) - if not getattr(self, "no_welcome_mail", False): - self.send_welcome_mail() - msgprint(_("Welcome Email Sent")) - else: - try: - self.email_new_password() - except frappe.OutgoingEmailError: - pass # email server not set, don't send email - self.doc.new_password = "" self.update_gravatar() def check_enable_disable(self): # do not allow disabling administrator/guest - if not cint(self.doc.enabled) and self.doc.name in ["Administrator", "Guest"]: + if not cint(self.doc.enabled) and self.doc.name in STANDARD_USERS: throw("{msg}: {name}".format(**{ "msg": _("Hey! You cannot disable user"), "name": self.doc.name @@ -60,26 +47,6 @@ class DocType: if not cint(self.doc.enabled) and getattr(frappe, "login_manager", None): frappe.local.login_manager.logout(user=self.doc.name) - def validate_max_users(self): - """don't allow more than max users if set in conf""" - from frappe import conf - # check only when enabling a user - if 'max_users' in conf and self.doc.enabled and \ - self.doc.name not in ["Administrator", "Guest"] and \ - cstr(self.doc.user_type).strip() in ("", "System User"): - active_users = frappe.db.sql("""select count(*) from tabUser - where ifnull(enabled, 0)=1 and docstatus<2 - and ifnull(user_type, "System User") = "System User" - and name not in ('Administrator', 'Guest', %s)""", (self.doc.name,))[0][0] - if active_users >= conf.max_users and conf.max_users: - throw(""" - You already have %(active_users)s active users, \ - which is the maximum number that you are currently allowed to add.

\ - So, to add more users, you can:
\ - 1. Upgrade to the unlimited users plan, or
\ - 2. Disable one or more of your existing users and try again""" \ - % {'active_users': active_users}) - def add_system_manager_role(self): # if adding system manager, do nothing if not cint(self.doc.enabled) or ("System Manager" in [user_role.role for user_role in @@ -107,6 +74,21 @@ class DocType: frappe.db.set(self.doc, 'owner', self.doc.name) frappe.clear_cache(user=self.doc.name) + try: + if self.in_insert: + if self.doc.name not in STANDARD_USERS: + if self.doc.new_password: + # new password given, no email required + _update_password(self.doc.name, self.doc.new_password) + if not getattr(self, "no_welcome_mail", False): + self.send_welcome_mail() + msgprint(_("Welcome Email Sent")) + else: + self.email_new_password() + + except frappe.OutgoingEmailError: + pass # email server not set, don't send email + def update_gravatar(self): import md5 if not self.doc.user_image: @@ -168,7 +150,7 @@ class DocType: args.update(add_args) - sender = frappe.session.user not in ("Administrator", "Guest") and frappe.session.user or None + sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None frappe.sendmail(recipients=self.doc.email, sender=sender, subject=subject, message=frappe.get_template(template).render(args)) @@ -179,7 +161,7 @@ class DocType: def on_trash(self): frappe.clear_cache(user=self.doc.name) - if self.doc.name in ["Administrator", "Guest"]: + if self.doc.name in STANDARD_USERS: throw("{msg}: {name}".format(**{ "msg": _("Hey! You cannot delete user"), "name": self.doc.name @@ -215,7 +197,7 @@ class DocType: def validate_rename(self, olddn, newdn): # do not allow renaming administrator and guest - if olddn in ["Administrator", "Guest"]: + if olddn in STANDARD_USERS: throw("{msg}: {name}".format(**{ "msg": _("Hey! You are restricted from renaming the user"), "name": olddn @@ -357,35 +339,50 @@ def reset_password(user): def user_query(doctype, txt, searchfield, start, page_len, filters): from frappe.widgets.reportview import get_match_cond + txt = "%{}%".format(txt) return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name) from `tabUser` where ifnull(enabled, 0)=1 and docstatus < 2 - and name not in ('Administrator', 'Guest') + and name not in ({standard_users}) and user_type != 'Website User' - and (%(key)s like "%(txt)s" - or concat_ws(' ', first_name, middle_name, last_name) like "%(txt)s") - %(mcond)s + and ({key} like %s + or concat_ws(' ', first_name, middle_name, last_name) like %s) + {mcond} order by - case when name like "%(txt)s" then 0 else 1 end, - case when concat_ws(' ', first_name, middle_name, last_name) like "%(txt)s" + case when name like %s then 0 else 1 end, + case when concat_ws(' ', first_name, middle_name, last_name) like %s then 0 else 1 end, name asc - limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt, - 'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len}) + limit %s, %s""".format(standard_users=", ".join(["%s"]*len(STANDARD_USERS)), + key=searchfield, mcond=get_match_cond(doctype)), + tuple(list(STANDARD_USERS) + [txt, txt, txt, txt, start, page_len])) -def get_total_users(): +def get_total_users(exclude_users=None): """Returns total no. of system users""" - return frappe.db.sql("""select count(*) from `tabUser` - where enabled = 1 and user_type != 'Website User' - and name not in ('Administrator', 'Guest')""")[0][0] + return len(get_system_users(exclude_users=exclude_users)) + +def get_system_users(exclude_users=None): + if not exclude_users: + exclude_users = [] + elif not isinstance(exclude_users, (list, tuple)): + exclude_users = [exclude_users] + + exclude_users += list(STANDARD_USERS) + + system_users = frappe.db.sql_list("""select name from `tabUser` + where enabled=1 and user_type != 'Website User' + and name not in ({})""".format(", ".join(["%s"]*len(exclude_users))), + exclude_users) + + return system_users def get_active_users(): """Returns No. of system users who logged in, in the last 3 days""" return frappe.db.sql("""select count(*) from `tabUser` where enabled = 1 and user_type != 'Website User' - and name not in ('Administrator', 'Guest') - and hour(timediff(now(), last_login)) < 72""")[0][0] + and name not in ({}) + and hour(timediff(now(), last_login)) < 72""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS)[0][0] def get_website_users(): """Returns total no. of website users""" diff --git a/frappe/core/page/messages/messages.py b/frappe/core/page/messages/messages.py index 0e29cd0697..9c9363915e 100644 --- a/frappe/core/page/messages/messages.py +++ b/frappe/core/page/messages/messages.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for - +from frappe.core.doctype.user.user import STANDARD_USERS @frappe.whitelist() def get_list(arg=None): @@ -51,8 +51,8 @@ def get_active_users(): from tabUser where ifnull(enabled,0)=1 and ifnull(user_type, '')!='Website User' and - name not in ('Administrator', 'Guest') - order by first_name""", as_dict=1) + name not in ({}) + order by first_name""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS, as_dict=1) @frappe.whitelist() def post(arg=None): diff --git a/frappe/core/page/user_properties/user_properties.py b/frappe/core/page/user_properties/user_properties.py index f78c412dd8..e4aa0799a8 100644 --- a/frappe/core/page/user_properties/user_properties.py +++ b/frappe/core/page/user_properties/user_properties.py @@ -5,13 +5,12 @@ from __future__ import unicode_literals import frappe import frappe.defaults import frappe.permissions +from frappe.core.doctype.user.user import get_system_users @frappe.whitelist() def get_users_and_links(): return { - "users": frappe.db.sql_list("""select name from tabUser where - ifnull(enabled,0)=1 and - name not in ("Administrator", "Guest")"""), + "users": get_system_users(), "link_fields": get_restrictable_doctypes() } diff --git a/frappe/model/bean.py b/frappe/model/bean.py index 93c18719cf..4a2c8f56b6 100644 --- a/frappe/model/bean.py +++ b/frappe/model/bean.py @@ -68,9 +68,6 @@ class Bean: self.set_doclist(doclist) - if dt == dn: - self.convert_type(self.doc) - def __iter__(self): return self.doclist.__iter__() @@ -91,6 +88,10 @@ class Bean: self.doclist = frappe.doclist(doclist) self.doc = self.doclist[0] + + if self.doc.get_meta().issingle: + self.doc.cast_floats_and_ints() + if self.obj: self.obj.doclist = self.doclist self.obj.doc = self.doc @@ -444,16 +445,6 @@ class Bean: raise frappe.MandatoryError, ", ".join([fieldname for msg, fieldname in missing]) - def convert_type(self, doc): - if doc.doctype==doc.name and doc.doctype!="DocType": - for df in self.meta.get({"doctype": "DocField", "parent": doc.doctype}): - if df.fieldtype in ("Int", "Check"): - doc.fields[df.fieldname] = cint(doc.fields.get(df.fieldname)) - elif df.fieldtype in ("Float", "Currency"): - doc.fields[df.fieldname] = flt(doc.fields.get(df.fieldname)) - - doc.docstatus = cint(doc.docstatus) - def extract_images_from_text_editor(self): from frappe.utils.file_manager import extract_images_from_html if self.doc.doctype != "DocType": diff --git a/frappe/model/doc.py b/frappe/model/doc.py index 1912a75452..18890b0e43 100755 --- a/frappe/model/doc.py +++ b/frappe/model/doc.py @@ -161,7 +161,18 @@ class Document: def _loadsingle(self): self.name = self.doctype self.fields.update(getsingle(self.doctype)) - + self.cast_floats_and_ints() + + def cast_floats_and_ints(self): + for df in frappe.get_doctype(self.doctype).get_docfields(): + if df.fieldtype in ("Int", "Check"): + self.fields[df.fieldname] = cint(self.fields.get(df.fieldname)) + elif df.fieldtype in ("Float", "Currency"): + self.fields[df.fieldname] = flt(self.fields.get(df.fieldname)) + + if self.docstatus is not None: + self.docstatus = cint(self.docstatus) + def __setattr__(self, name, value): # normal attribute if not self.__dict__.has_key('_Document__initialized'): diff --git a/frappe/model/doctype.py b/frappe/model/doctype.py index 425ff5416e..6d5802a246 100644 --- a/frappe/model/doctype.py +++ b/frappe/model/doctype.py @@ -380,10 +380,12 @@ class DocTypeDocList(frappe.model.doclist.DocList): return fieldname in self.get_fieldnames() def get_fieldnames(self, filters=None): + return map(lambda df: df.fieldname, self.get_docfields(filters)) + + def get_docfields(self, filters=None): if not filters: filters = {} filters.update({"doctype": "DocField", "parent": self[0].name}) - - return map(lambda df: df.fieldname, self.get(filters)) + return self.get(filters) def get_options(self, fieldname, parent=None, parentfield=None): return self.get_field(fieldname, parent, parentfield).options diff --git a/frappe/sessions.py b/frappe/sessions.py index fdc19b29db..156953d8b9 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -68,24 +68,26 @@ def get(): """get session boot info""" from frappe.core.doctype.notification_count.notification_count import \ get_notification_info_for_boot, get_notifications + from frappe.boot import get_bootinfo, get_startup_js bootinfo = None - if not getattr(frappe.conf,'disable_session_cache',None): + if not getattr(frappe.conf,'disable_session_cache', None): # check if cache exists bootinfo = frappe.cache().get_value('bootinfo:' + frappe.session.user) if bootinfo: bootinfo['from_cache'] = 1 bootinfo["user"]["recent"] = json.dumps(frappe.cache().get_value("recent:" + frappe.session.user)) bootinfo["notification_info"].update(get_notifications()) + bootinfo["startup_js"] = get_startup_js() if not bootinfo: if not frappe.cache().get_stats(): frappe.msgprint("memcached is not working / stopped. Please start memcached for best results.") # if not create it - from frappe.boot import get_bootinfo bootinfo = get_bootinfo() bootinfo["notification_info"] = get_notification_info_for_boot() + bootinfo["startup_js"] = get_startup_js() frappe.cache().set_value('bootinfo:' + frappe.session.user, bootinfo) return bootinfo diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 8d827accfc..81d843d635 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -5,12 +5,14 @@ from __future__ import unicode_literals import unittest import frappe from frappe.test_runner import make_test_records +from frappe.core.doctype.user.user import STANDARD_USERS class TestDB(unittest.TestCase): def test_get_value(self): from frappe.utils import now_datetime import time - frappe.db.sql("""delete from `tabUser` where name not in ('Administrator', 'Guest')""") + frappe.db.sql("""delete from `tabUser` where name not in ({})""".format(", ".join(["%s"]*len(STANDARD_USERS))), + STANDARD_USERS) now = now_datetime() diff --git a/frappe/utils/email_lib/smtp.py b/frappe/utils/email_lib/smtp.py index 576a30eab2..9ba7d9e94a 100644 --- a/frappe/utils/email_lib/smtp.py +++ b/frappe/utils/email_lib/smtp.py @@ -13,7 +13,7 @@ def send(email, as_bulk=False): if frappe.flags.mute_emails or frappe.conf.get("mute_emails") or False: frappe.msgprint("Emails are muted") return - + try: smtpserver = SMTPServer() if hasattr(smtpserver, "always_use_login_id_as_sender") and \ diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index 985e016507..f4d743aaea 100644 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -18,6 +18,9 @@ from datetime import datetime DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' def enqueue_events(site): + if is_scheduler_disabled(): + return + # lock before queuing begins try: lock = create_lock('scheduler') @@ -103,6 +106,15 @@ def log(method, message=None): return message +def is_scheduler_disabled(): + return frappe.utils.cint(frappe.db.get_global("disable_scheduler")) + +def enable_scheduler(): + frappe.db.set_global("disable_scheduler", 0) + +def disable_scheduler(): + frappe.db.set_global("disable_scheduler", 1) + def get_errors(from_date, to_date, limit): errors = frappe.db.sql("""select modified, method, error from `tabScheduler Log` where date(modified) between %s and %s @@ -127,6 +139,6 @@ def get_error_report(from_date=None, to_date=None, limit=10): limit=limit, url=get_url(), errors="
".join(errors)) else: return 0, "

Scheduler didn't encounter any problems.

" - + if __name__=='__main__': execute() diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 32e85b8cae..0dee77b9e1 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -179,13 +179,15 @@ def get_user_fullname(user): def get_system_managers(only_name=False): """returns all system manager's user details""" import email.utils + from frappe.core.doctype.user.user import STANDARD_USERS system_managers = frappe.db.sql("""select distinct name, concat_ws(" ", if(first_name="", null, first_name), if(last_name="", null, last_name)) as fullname from tabUser p where docstatus < 2 and enabled = 1 - and name not in ("Administrator", "Guest") + and name not in ({}) and exists (select * from tabUserRole ur - where ur.parent = p.name and ur.role="System Manager")""", as_dict=True) + where ur.parent = p.name and ur.role="System Manager")""".format(", ".join(["%s"]*len(STANDARD_USERS))), + STANDARD_USERS, as_dict=True) if only_name: return [p.name for p in system_managers] diff --git a/frappe/website/render.py b/frappe/website/render.py index 1f6022c86b..694afc5477 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -16,16 +16,28 @@ def render(path): """render html page""" frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest" path = resolve_path(path.lstrip("/")) + http_status_code = 200 try: data = render_page(path) + + except frappe.DoesNotExistError, e: + path = "404" + data = render_page(path) + http_status_code = e.http_status_code + except Exception: path = "error" data = render_page(path) + http_status_code = 500 + return build_response(path, data, http_status_code) + +def build_response(path, data, http_status_code): # build response response = Response() response.data = set_content_type(response, data, path) + response.status_code = http_status_code response.headers[b"X-Page-Name"] = path.encode("utf-8") response.headers[b"X-From-Cache"] = frappe.local.response.from_cache or False return response @@ -53,14 +65,16 @@ def build(path): frappe.connect() build_method = (build_json if is_ajax() else build_page) + try: return build_method(path) except frappe.DoesNotExistError: hooks = frappe.get_hooks() if hooks.website_catch_all: - return build_method(hooks.website_catch_all[0]) + path = hooks.website_catch_all[0] + return build_method(path) else: - return build_method("404") + raise def build_json(path): return get_context(path).data