From f0e23a5a6c22a5f0de98b19d6454d717c2b964e4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 17 Oct 2017 12:29:08 +0530 Subject: [PATCH] [refactor] domain and domain settings (#4163) * [refactor] domain and domain settings * [fix] test_domain.py * [fix] patches * [fix] domain activation after setup * [fix] tests and lint * [fix] tests and lint * [enhance] better prompt naming * [fix] setup wizard test * [fix] testing * [minor] new item in quick entry from form dashboard --- frappe/__init__.py | 16 +++- frappe/build.js | 10 +-- frappe/core/doctype/domain/domain.py | 84 ++++++++++++++++++- .../domain_settings/domain_settings.py | 43 +++++++++- .../system_settings/system_settings.py | 2 +- .../doctype/custom_field/custom_field.py | 4 + frappe/desk/page/setup_wizard/setup_wizard.js | 29 ++++--- frappe/desk/reportview.py | 2 +- frappe/patches.txt | 5 +- frappe/public/js/frappe/db.js | 7 ++ .../js/frappe/form/controls/base_control.js | 4 + .../js/frappe/form/controls/base_input.js | 2 +- frappe/public/js/frappe/form/controls/data.js | 4 +- .../js/frappe/form/controls/password.js | 12 ++- frappe/public/js/frappe/form/layout.js | 24 +++++- frappe/public/js/frappe/form/quick_entry.js | 13 +-- frappe/public/js/frappe/form/save.js | 69 ++++----------- frappe/public/js/frappe/misc/common.js | 9 ++ frappe/public/js/frappe/model/model.js | 14 +++- frappe/public/js/frappe/request.js | 1 + frappe/public/js/frappe/socketio_client.js | 24 +++--- frappe/public/js/frappe/ui/page.js | 12 +-- .../js/frappe/ui/toolbar/search_utils.js | 13 +-- frappe/public/js/legacy/clientscriptAPI.js | 3 +- frappe/public/js/legacy/form.js | 2 +- frappe/sessions.py | 2 +- frappe/tests/ui/data/test_lib.js | 12 +-- frappe/utils/selenium_testdriver.py | 22 +++-- 28 files changed, 299 insertions(+), 145 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 2e0b543a06..694cb30e93 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -475,13 +475,26 @@ def only_for(roles): if not roles.intersection(myroles): raise PermissionError +def get_domain_data(module): + try: + domain_data = get_hooks('domains') + if module in domain_data: + return _dict(get_attr(get_hooks('domains')[module][0] + '.data')) + else: + return _dict() + except ImportError: + if local.flags.in_test: + return _dict() + else: + raise + + def clear_cache(user=None, doctype=None): """Clear **User**, **DocType** or global cache. :param user: If user is given, only user cache is cleared. :param doctype: If doctype is given, only DocType cache is cleared.""" import frappe.sessions - from frappe.core.doctype.domain_settings.domain_settings import clear_domain_cache if doctype: import frappe.model.meta frappe.model.meta.clear_cache(doctype) @@ -493,7 +506,6 @@ def clear_cache(user=None, doctype=None): frappe.sessions.clear_cache() translate.clear_cache() reset_metadata_version() - clear_domain_cache() local.cache = {} local.new_doc_templates = {} diff --git a/frappe/build.js b/frappe/build.js index 9a4f0c2fc9..de12e31ca0 100644 --- a/frappe/build.js +++ b/frappe/build.js @@ -60,11 +60,11 @@ function watch() { io.emit('reload_css', filename); } }); - watch_js(function (filename) { - if(socket_connection) { - io.emit('reload_js', filename); - } - }); + // watch_js(function (filename) { + // if(socket_connection) { + // io.emit('reload_js', filename); + // } + // }); watch_build_json(); }); diff --git a/frappe/core/doctype/domain/domain.py b/frappe/core/doctype/domain/domain.py index d8a571537c..c6b1766235 100644 --- a/frappe/core/doctype/domain/domain.py +++ b/frappe/core/doctype/domain/domain.py @@ -4,7 +4,89 @@ from __future__ import unicode_literals import frappe + from frappe.model.document import Document +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields class Domain(Document): - pass + '''Domain documents are created automatically when DocTypes + with "Restricted" domains are imported during + installation or migration''' + def setup_domain(self): + '''Setup domain icons, permissions, custom fields etc.''' + self.setup_data() + self.setup_roles() + self.setup_properties() + self.set_values() + if not int(frappe.db.get_single_value('System Settings', 'setup_complete') or 0): + # if setup not complete, setup desktop etc. + self.setup_sidebar_items() + self.setup_desktop_icons() + self.set_default_portal_role() + + if self.data.custom_fields: + create_custom_fields(self.data.custom_fields) + + if self.data.on_setup: + # custom on_setup method + frappe.get_attr(self.data.on_setup)() + + + def setup_roles(self): + '''Enable roles that are restricted to this domain''' + if self.data.restricted_roles: + for role_name in self.data.restricted_roles: + role = frappe.get_doc('Role', role_name) + role.disabled = 0 + role.save() + + def setup_data(self, domain=None): + '''Load domain info via hooks''' + self.data = frappe.get_domain_data(self.name) + + def get_domain_data(self, module): + return frappe.get_attr(frappe.get_hooks('domains')[self.name] + '.data') + + def set_default_portal_role(self): + '''Set default portal role based on domain''' + if self.data.get('default_portal_role'): + frappe.db.set_value('Portal Settings', None, 'default_role', + self.data.get('default_portal_role')) + + def setup_desktop_icons(self): + '''set desktop icons form `data.desktop_icons`''' + from frappe.desk.doctype.desktop_icon.desktop_icon import set_desktop_icons + if self.data.desktop_icons: + set_desktop_icons(self.data.desktop_icons) + + def setup_properties(self): + if self.data.properties: + for args in self.data.properties: + frappe.make_property_setter(args) + + + def set_values(self): + '''set values based on `data.set_value`''' + if self.data.set_value: + for args in self.data.set_value: + doc = frappe.get_doc(args[0], args[1] or args[0]) + doc.set(args[2], args[3]) + doc.save() + + def setup_sidebar_items(self): + '''Enable / disable sidebar items''' + if self.data.allow_sidebar_items: + # disable all + frappe.db.sql('update `tabPortal Menu Item` set enabled=0') + + # enable + frappe.db.sql('''update `tabPortal Menu Item` set enabled=1 + where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.allow_sidebar_items]))) + + if self.data.remove_sidebar_items: + # disable all + frappe.db.sql('update `tabPortal Menu Item` set enabled=1') + + # enable + frappe.db.sql('''update `tabPortal Menu Item` set enabled=0 + where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.remove_sidebar_items]))) diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index cfe5010835..b3e1b133ae 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -7,8 +7,46 @@ import frappe from frappe.model.document import Document class DomainSettings(Document): + def set_active_domains(self, domains): + self.active_domains = [] + for d in domains: + self.append('active_domains', dict(domain=d)) + self.save() + def on_update(self): - clear_domain_cache() + for d in self.active_domains: + domain = frappe.get_doc('Domain', d.domain) + domain.setup_domain() + + self.restrict_roles_and_modules() + frappe.clear_cache() + + def restrict_roles_and_modules(self): + '''Disable all restricted roles and set `restrict_to_domain` property in Module Def''' + active_domains = frappe.get_active_domains() + all_domains = (frappe.get_hooks('domains') or {}).keys() + + def remove_role(role): + frappe.db.sql('delete from `tabHas Role` where role=%s', role) + frappe.set_value('Role', role, 'disabled', 1) + + for domain in all_domains: + data = frappe.get_domain_data(domain) + if not frappe.db.get_value('Domain', domain): + frappe.get_doc(dict(doctype='Domain', domain=domain)).insert() + if 'modules' in data: + for module in data.get('modules'): + frappe.db.set_value('Module Def', module, 'restrict_to_domain', domain) + + if 'restricted_roles' in data: + for role in data['restricted_roles']: + if not frappe.db.get_value('Role', role): + frappe.get_doc(dict(doctype='Role', role_name=role)).insert() + frappe.db.set_value('Role', role, 'restrict_to_domain', domain) + + if domain not in active_domains: + remove_role(role) + def get_active_domains(): """ get the domains set in the Domain Settings as active domain """ @@ -33,6 +71,3 @@ def get_active_modules(): return active_modules return frappe.cache().get_value('active_modules', _get_active_modules) - -def clear_domain_cache(): - frappe.cache().delete_key(['active_domains', 'active_modules']) diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index ef2863fd46..bd35edb880 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -14,7 +14,7 @@ from frappe.twofactor import toggle_two_factor_auth class SystemSettings(Document): def validate(self): enable_password_policy = cint(self.enable_password_policy) and True or False - minimum_password_score = cint(self.minimum_password_score) or 0 + minimum_password_score = cint(getattr(self, 'minimum_password_score', 0)) or 0 if enable_password_policy and minimum_password_score <= 0: frappe.throw(_("Please select Minimum Password Score")) elif not enable_password_policy: diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 6eb3eef544..a94608f25d 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -111,6 +111,10 @@ def create_custom_fields(custom_fields): :param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`''' for doctype, fields in custom_fields.items(): + if isinstance(fields, dict): + # only one field + fields = [fields] + for df in fields: field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) if not field: diff --git a/frappe/desk/page/setup_wizard/setup_wizard.js b/frappe/desk/page/setup_wizard/setup_wizard.js index f83f1c57ed..bc4ce4f043 100644 --- a/frappe/desk/page/setup_wizard/setup_wizard.js +++ b/frappe/desk/page/setup_wizard/setup_wizard.js @@ -508,19 +508,22 @@ frappe.setup.utils = { bind_language_events: function(slide) { slide.get_input("language").unbind("change").on("change", function() { - var lang = $(this).val() || "English"; - frappe._messages = {}; - frappe.call({ - method: "frappe.desk.page.setup_wizard.setup_wizard.load_messages", - freeze: true, - args: { - language: lang - }, - callback: function(r) { - frappe.setup._from_load_messages = true; - frappe.wizard.refresh_slides(); - } - }); + clearTimeout (slide.language_call_timeout); + slide.language_call_timeout = setTimeout (() => { + var lang = $(this).val() || "English"; + frappe._messages = {}; + frappe.call({ + method: "frappe.desk.page.setup_wizard.setup_wizard.load_messages", + freeze: true, + args: { + language: lang + }, + callback: function(r) { + frappe.setup._from_load_messages = true; + frappe.wizard.refresh_slides(); + } + }); + }, 500); }); }, diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 09afea8b3d..8b6f32536c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -352,7 +352,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with for f in filters: if isinstance(f[1], string_types) and f[1][0] == '!': flt.append([doctype, f[0], '!=', f[1][1:]]) - elif isinstance(f[1], list) and \ + elif isinstance(f[1], (list, tuple)) and \ f[1][0] in (">", "<", ">=", "<=", "like", "not like", "in", "not in", "between"): flt.append([doctype, f[0], f[1][0], f[1][1]]) diff --git a/frappe/patches.txt b/frappe/patches.txt index 86015c001c..ce5cc760dd 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -7,10 +7,11 @@ frappe.patches.v7_1.rename_scheduler_log_to_error_log frappe.patches.v6_1.rename_file_data frappe.patches.v7_0.re_route #2016-06-27 frappe.patches.v7_2.remove_in_filter -execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-03-09 -frappe.patches.v8_0.drop_in_dialog +frappe.patches.v8_0.drop_in_dialog #2017-09-22 +execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2017-03-03 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2017-03-03 +execute:frappe.reload_doc('core', 'doctype', 'module_def') #2017-09-22 frappe.patches.v8_0.drop_is_custom_from_docperm frappe.patches.v8_0.update_records_in_global_search #11-05-2017 frappe.patches.v8_0.update_published_in_global_search diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index 101228de97..3cc34e3f79 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -2,6 +2,13 @@ // MIT License. See license.txt frappe.db = { + exists: function(doctype, name) { + return new Promise ((resolve) => { + frappe.db.get_value(doctype, {name: name}, 'name').then((r) => { + (r.message && r.message.name) ? resolve(true) : resolve(false); + }); + }); + }, get_value: function(doctype, filters, fieldname, callback) { return frappe.call({ method: "frappe.client.get_value", diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index 68cd4b0da9..68a1138584 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -47,6 +47,10 @@ frappe.ui.form.Control = Class.extend({ // returns "Read", "Write" or "None" // as strings based on permissions get_status: function(explain) { + if (this.df.get_status) { + return this.df.get_status(this); + } + if(!this.doctype && !this.docname) { // like in case of a dialog box if (cint(this.df.hidden)) { diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js index 5e93009202..99edde9936 100644 --- a/frappe/public/js/frappe/form/controls/base_input.js +++ b/frappe/public/js/frappe/form/controls/base_input.js @@ -71,7 +71,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ } }; - if(me.disp_status != "None") { + if (me.disp_status != "None") { // refresh value if(me.doctype && me.docname) { me.value = frappe.model.get_value(me.doctype, me.docname, me.df.fieldname); diff --git a/frappe/public/js/frappe/form/controls/data.js b/frappe/public/js/frappe/form/controls/data.js index 5751451f27..aa78f2df8e 100644 --- a/frappe/public/js/frappe/form/controls/data.js +++ b/frappe/public/js/frappe/form/controls/data.js @@ -28,9 +28,9 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ setup_autoname_check: function() { if (!this.df.parent) return; this.meta = frappe.get_meta(this.df.parent); - if (this.meta && this.meta.autoname + if (this.meta && ((this.meta.autoname && this.meta.autoname.substr(0, 6)==='field:' - && this.meta.autoname.substr(6) === this.df.fieldname) { + && this.meta.autoname.substr(6) === this.df.fieldname) || this.df.fieldname==='__newname') ) { this.$input.on('keyup', () => { this.set_description(''); if (this.doc && this.doc.__islocal) { diff --git a/frappe/public/js/frappe/form/controls/password.js b/frappe/public/js/frappe/form/controls/password.js index 5ea940d577..8a25642737 100644 --- a/frappe/public/js/frappe/form/controls/password.js +++ b/frappe/public/js/frappe/form/controls/password.js @@ -12,13 +12,11 @@ frappe.ui.form.ControlPassword = frappe.ui.form.ControlData.extend({ this.indicator = this.$wrapper.find('.password-strength-indicator'); this.message = this.$wrapper.find('.help-box'); - this.$input.on('input', () => { - var $this = $(this); - clearTimeout($this.data('timeout')); - $this.data('timeout', setTimeout(() => { - var txt = me.$input.val(); - me.get_password_strength(txt); - }), 300); + this.$input.on('keyup', () => { + clearTimeout(this.check_password_timeout); + this.check_password_timeout = setTimeout (() => { + me.get_password_strength(me.$input.val()); + }, 500); }); }, get_password_strength: function(value) { diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 2334ab9876..ea04faa865 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -25,7 +25,7 @@ frappe.ui.form.Layout = Class.extend({ this.wrapper = $('
').appendTo(this.parent); this.message = $('').appendTo(this.wrapper); if(!this.fields) { - this.fields = frappe.meta.sort_docfields(frappe.meta.docfield_map[this.doctype]); + this.fields = this.get_doctype_fields(); } this.setup_tabbing(); this.render(); @@ -35,6 +35,28 @@ frappe.ui.form.Layout = Class.extend({ this.show_message(__("This form does not have any input")); } }, + get_doctype_fields: function() { + let fields = [ + { + parent: this.frm.doctype, + fieldtype: 'Data', + fieldname: '__newname', + reqd: 1, + hidden: 1, + label: __('Name'), + get_status: function(field) { + if (field.frm && field.frm.is_new() + && field.frm.meta.autoname + && ['prompt', 'name'].includes(field.frm.meta.autoname.toLowerCase())) { + return 'Write'; + } + return 'None'; + } + } + ]; + fields = fields.concat(frappe.meta.sort_docfields(frappe.meta.docfield_map[this.doctype])); + return fields; + }, show_message: function(html) { if(html) { if(html.substr(0, 1)!=='<') { diff --git a/frappe/public/js/frappe/form/quick_entry.js b/frappe/public/js/frappe/form/quick_entry.js index 015cc27ded..f3f5e55d71 100644 --- a/frappe/public/js/frappe/form/quick_entry.js +++ b/frappe/public/js/frappe/form/quick_entry.js @@ -1,6 +1,6 @@ frappe.provide('frappe.ui.form'); -frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback) => { +frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback, doc) => { var trimmed_doctype = doctype.replace(/ /g, ''); var controller_name = "QuickEntryForm"; @@ -8,15 +8,16 @@ frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback) => { controller_name = trimmed_doctype + "QuickEntryForm"; } - frappe.quick_entry = new frappe.ui.form[controller_name](doctype, after_insert, init_callback); + frappe.quick_entry = new frappe.ui.form[controller_name](doctype, after_insert, init_callback, doc); return frappe.quick_entry.setup(); }; frappe.ui.form.QuickEntryForm = Class.extend({ - init: function(doctype, after_insert, init_callback){ + init: function(doctype, after_insert, init_callback, doc) { this.doctype = doctype; this.after_insert = after_insert; this.init_callback = init_callback; + this.doc = doc; }, setup: function() { @@ -40,7 +41,9 @@ frappe.ui.form.QuickEntryForm = Class.extend({ this.mandatory = $.map(frappe.get_meta(this.doctype).fields, function(d) { return (d.reqd || d.bold && !d.read_only) ? d : null; }); this.meta = frappe.get_meta(this.doctype); - this.doc = frappe.model.get_new_doc(this.doctype, null, null, true); + if (!this.doc) { + this.doc = frappe.model.get_new_doc(this.doctype, null, null, true); + } }, is_quick_entry: function(){ @@ -108,7 +111,7 @@ frappe.ui.form.QuickEntryForm = Class.extend({ this.dialog.onhide = () => frappe.quick_entry = null; this.dialog.show(); this.set_defaults(); - + if (this.init_callback) { this.init_callback(this.dialog); } diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 4fd5aab626..3aadb8c314 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -19,27 +19,24 @@ frappe.ui.form.save = function (frm, action, callback, btn) { var save = function () { remove_empty_rows(); - check_name(function () { - $(frm.wrapper).addClass('validated-form'); - if (check_mandatory()) { - _call({ - method: "frappe.desk.form.save.savedocs", - args: { doc: frm.doc, action: action }, - callback: function (r) { - $(document).trigger("save", [frm.doc]); - callback(r); - }, - error: function (r) { - callback(r); - }, - btn: btn, - freeze_message: freeze_message - }); - } else { - $(btn).prop("disabled", false); - } - }); - + $(frm.wrapper).addClass('validated-form'); + if (check_mandatory()) { + _call({ + method: "frappe.desk.form.save.savedocs", + args: { doc: frm.doc, action: action }, + callback: function (r) { + $(document).trigger("save", [frm.doc]); + callback(r); + }, + error: function (r) { + callback(r); + }, + btn: btn, + freeze_message: freeze_message + }); + } else { + $(btn).prop("disabled", false); + } }; var remove_empty_rows = function() { @@ -107,36 +104,6 @@ frappe.ui.form.save = function (frm, action, callback, btn) { }); }; - var check_name = function (callback) { - var doc = frm.doc; - var meta = locals.DocType[doc.doctype]; - if (doc.__islocal && (meta && meta.autoname - && meta.autoname.toLowerCase() == 'prompt')) { - var d = frappe.prompt(__("Name"), function (values) { - var newname = values.value; - if (newname) { - doc.__newname = strip(newname); - } else { - frappe.msgprint(__("Name is required")); - throw "name required"; - } - - callback(); - - }, __('Enter the name of the new {0}', [doc.doctype]), __("Create")); - - if (doc.__newname) { - d.set_value("value", doc.__newname); - } - - d.onhide = function () { - $(btn).prop("disabled", false); - } - } else { - callback(); - } - }; - var check_mandatory = function () { var me = this; var has_errors = false; diff --git a/frappe/public/js/frappe/misc/common.js b/frappe/public/js/frappe/misc/common.js index c01921f6aa..264ca8aae0 100644 --- a/frappe/public/js/frappe/misc/common.js +++ b/frappe/public/js/frappe/misc/common.js @@ -50,6 +50,15 @@ frappe.avatar = function(user, css_class, title) { } } +frappe.ui.scroll = function(element, animate, additional_offset) { + var header_offset = $(".navbar").height() + $(".page-head").height(); + var top = $(element).offset().top - header_offset - cint(additional_offset); + if (animate) { + $("html, body").animate({ scrollTop: top }); + } else { + $(window).scrollTop(top); + } +}; frappe.get_palette = function(txt) { return '#fafbfc'; diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 0a58c410b5..81f0249480 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -327,17 +327,23 @@ $.extend(frappe.model, { set_value: function(doctype, docname, fieldname, value, fieldtype) { /* help: Set a value locally (if changed) and execute triggers */ - var doc = locals[doctype] && locals[doctype][docname]; + var doc; + if ($.isPlainObject(doctype)) { + // first parameter is the doc, shift parameters to the left + doc = doctype; fieldname = docname; value = fieldname; + } else { + doc = locals[doctype] && locals[doctype][docname]; + } - var to_update = fieldname; + let to_update = fieldname; let tasks = []; if(!$.isPlainObject(to_update)) { to_update = {}; to_update[fieldname] = value; } - $.each(to_update, function(key, value) { - if(doc && doc[key] !== value) { + $.each(to_update, (key, value) => { + if (doc && doc[key] !== value) { if(doc.__unedited && !(!doc[key] && !value)) { // unset unedited flag for virgin rows doc.__unedited = false; diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 1d4bfdddfc..de474538ab 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -145,6 +145,7 @@ frappe.request.call = function(opts) { frappe.msgprint({message:__("Server Error: Please check your server logs or contact tech support."), title:__('Something went wrong'), indicator: 'red'}); try { opts.error_callback && opts.error_callback(); + frappe.request.report_error(xhr, opts); } catch (e) { frappe.request.report_error(xhr, opts); } diff --git a/frappe/public/js/frappe/socketio_client.js b/frappe/public/js/frappe/socketio_client.js index 05be95e9fc..2c4477e1e6 100644 --- a/frappe/public/js/frappe/socketio_client.js +++ b/frappe/public/js/frappe/socketio_client.js @@ -222,17 +222,19 @@ frappe.socketio = { }, 5); }); // js files show alert - frappe.socketio.file_watcher.on('reload_js', function(filename) { - filename = "assets/" + filename; - var msg = $(` - ${filename} changed Click to Reload - `) - msg.find('a').click(frappe.ui.toolbar.clear_cache); - frappe.show_alert({ - indicator: 'orange', - message: msg - }, 5); - }); + + // commenting as this kills a branch change + // frappe.socketio.file_watcher.on('reload_js', function(filename) { + // filename = "assets/" + filename; + // var msg = $(` + // ${filename} changed Click to Reload + // `) + // msg.find('a').click(frappe.ui.toolbar.clear_cache); + // frappe.show_alert({ + // indicator: 'orange', + // message: msg + // }, 5); + // }); }, process_response: function(data, method) { if(!data) { diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index 21fcc9e46b..5fde62d898 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -495,14 +495,4 @@ frappe.ui.Page = Class.extend({ this.wrapper.trigger('view-change'); }, -}); - -frappe.ui.scroll = function(element, animate, additional_offset) { - var header_offset = $(".navbar").height() + $(".page-head").height(); - var top = $(element).offset().top - header_offset - cint(additional_offset); - if (animate) { - $("html, body").animate({ scrollTop: top }); - } else { - $(window).scrollTop(top); - } -} +}); \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/search_utils.js b/frappe/public/js/frappe/ui/toolbar/search_utils.js index f355e8547b..c434bef88f 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_utils.js +++ b/frappe/public/js/frappe/ui/toolbar/search_utils.js @@ -50,24 +50,27 @@ frappe.search.utils = { find(values, keywords, function(match) { var out = { route: match[1] - } - if(match[1][0]==='Form' && match[1][2]) { - if(match[1][1] !== match[1][2]) { + }; + if (match[1][0]==='Form') { + if (match[1].length > 2 && match[1][1] !== match[1][2]) { out.label = __(match[1][1]) + " " + match[1][2].bold(); out.value = __(match[1][1]) + " " + match[1][2]; } else { out.label = __(match[1][1]).bold(); out.value = __(match[1][1]); } - } else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0])) { + } else if (in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0]) && (match[1].length > 1)) { var type = match[1][0], label = type; if(type==='modules') label = 'Module'; else if(type==='query-report') label = 'Report'; out.label = __(match[1][1]).bold() + " " + __(label); out.value = __(match[1][1]) + " " + __(label); - } else { + } else if (match[0]) { out.label = match[0].bold(); out.value = match[0]; + } else { + // eslint-disable-next-line + console.log('Illegal match', match); } out.index = 80; return out; diff --git a/frappe/public/js/legacy/clientscriptAPI.js b/frappe/public/js/legacy/clientscriptAPI.js index c7590c5ae5..dc715defe8 100644 --- a/frappe/public/js/legacy/clientscriptAPI.js +++ b/frappe/public/js/legacy/clientscriptAPI.js @@ -492,7 +492,8 @@ _f.Frm.prototype.make_new = function(doctype) { } }); - frappe.set_route('Form', doctype, new_doc.name); + frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); + // frappe.set_route('Form', doctype, new_doc.name); }); } } diff --git a/frappe/public/js/legacy/form.js b/frappe/public/js/legacy/form.js index 413e1e594a..b526f9b6c8 100644 --- a/frappe/public/js/legacy/form.js +++ b/frappe/public/js/legacy/form.js @@ -225,7 +225,7 @@ _f.Frm.prototype.watch_model_updates = function() { }; _f.Frm.prototype.setup_std_layout = function() { - this.form_wrapper = $('
').appendTo(this.layout_main); + this.form_wrapper = $('
').appendTo(this.layout_main); this.body = $('
').appendTo(this.form_wrapper); // only tray diff --git a/frappe/sessions.py b/frappe/sessions.py index c25ccadced..379385ac2f 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -53,7 +53,7 @@ def clear_global_cache(): frappe.model.meta.clear_cache() frappe.cache().delete_value(["app_hooks", "installed_apps", "app_modules", "module_app", "notification_config", 'system_settings' - 'scheduler_events', 'time_zone', 'webhooks']) + 'scheduler_events', 'time_zone', 'webhooks', 'active_domains', 'active_modules']) frappe.setup_module_map() diff --git a/frappe/tests/ui/data/test_lib.js b/frappe/tests/ui/data/test_lib.js index 71ba61efaa..a6ca8a4628 100644 --- a/frappe/tests/ui/data/test_lib.js +++ b/frappe/tests/ui/data/test_lib.js @@ -1,15 +1,11 @@ frappe.tests = { data: {}, - get_fixture_names: (doctype) => { - return Object.keys(frappe.test_data[doctype]); - }, make: function(doctype, data) { return frappe.run_serially([ () => frappe.set_route('List', doctype), () => frappe.new_doc(doctype), () => { - if (frappe.quick_entry) - { + if (frappe.quick_entry) { frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(); return frappe.timeout(1); } @@ -79,13 +75,13 @@ frappe.tests = { }); return frappe.run_serially(grid_row_tasks); }, - setup_doctype: (doctype) => { + setup_doctype: (doctype, data) => { return frappe.run_serially([ () => frappe.set_route('List', doctype), () => frappe.timeout(1), () => { frappe.tests.data[doctype] = []; - let expected = frappe.tests.get_fixture_names(doctype); + let expected = Object.keys(data); cur_list.data.forEach((d) => { frappe.tests.data[doctype].push(d.name); if(expected.indexOf(d.name) !== -1) { @@ -98,7 +94,7 @@ frappe.tests = { expected.forEach(function(d) { if(d) { tasks.push(() => frappe.tests.make(doctype, - frappe.test_data[doctype][d])); + data[d])); } }); diff --git a/frappe/utils/selenium_testdriver.py b/frappe/utils/selenium_testdriver.py index f19d42d593..3f3d82fe43 100644 --- a/frappe/utils/selenium_testdriver.py +++ b/frappe/utils/selenium_testdriver.py @@ -84,16 +84,19 @@ class TestDriver(object): time.sleep(0.2) def set_field(self, fieldname, text): - elem = self.find(xpath='//input[@data-fieldname="{0}"]'.format(fieldname)) - elem[0].send_keys(text) + elem = self.wait_for(xpath='//input[@data-fieldname="{0}"]'.format(fieldname)) + time.sleep(0.2) + elem.send_keys(text) def set_select(self, fieldname, text): - elem = self.find(xpath='//select[@data-fieldname="{0}"]'.format(fieldname)) - elem[0].send_keys(text) + elem = self.wait_for(xpath='//select[@data-fieldname="{0}"]'.format(fieldname)) + time.sleep(0.2) + elem.send_keys(text) def set_text_editor(self, fieldname, text): - elem = self.find(xpath='//div[@data-fieldname="{0}"]//div[@contenteditable="true"]'.format(fieldname)) - elem[0].send_keys(text) + elem = self.wait_for(xpath='//div[@data-fieldname="{0}"]//div[@contenteditable="true"]'.format(fieldname)) + time.sleep(0.2) + elem.send_keys(text) def find(self, selector=None, everywhere=False, xpath=None): if xpath: @@ -164,7 +167,11 @@ class TestDriver(object): self.wait_for(xpath='//div[@data-page-route="{0}"]'.format('/'.join(args)), timeout=4) def click(self, css_selector, xpath=None): - self.wait_till_clickable(css_selector, xpath).click() + element = self.wait_till_clickable(css_selector, xpath) + self.scroll_to(css_selector) + time.sleep(0.5) + element.click() + return element def click_primary_action(self): selector = ".page-actions .primary-action" @@ -201,6 +208,7 @@ class TestDriver(object): return self.get_wait().until(EC.element_to_be_clickable( (by, selector))) + def execute_script(self, js): self.driver.execute_script(js)