瀏覽代碼

[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
version-14
Rushabh Mehta 7 年之前
committed by GitHub
父節點
當前提交
f0e23a5a6c
共有 28 個文件被更改,包括 299 次插入145 次删除
  1. +14
    -2
      frappe/__init__.py
  2. +5
    -5
      frappe/build.js
  3. +83
    -1
      frappe/core/doctype/domain/domain.py
  4. +39
    -4
      frappe/core/doctype/domain_settings/domain_settings.py
  5. +1
    -1
      frappe/core/doctype/system_settings/system_settings.py
  6. +4
    -0
      frappe/custom/doctype/custom_field/custom_field.py
  7. +16
    -13
      frappe/desk/page/setup_wizard/setup_wizard.js
  8. +1
    -1
      frappe/desk/reportview.py
  9. +3
    -2
      frappe/patches.txt
  10. +7
    -0
      frappe/public/js/frappe/db.js
  11. +4
    -0
      frappe/public/js/frappe/form/controls/base_control.js
  12. +1
    -1
      frappe/public/js/frappe/form/controls/base_input.js
  13. +2
    -2
      frappe/public/js/frappe/form/controls/data.js
  14. +5
    -7
      frappe/public/js/frappe/form/controls/password.js
  15. +23
    -1
      frappe/public/js/frappe/form/layout.js
  16. +8
    -5
      frappe/public/js/frappe/form/quick_entry.js
  17. +18
    -51
      frappe/public/js/frappe/form/save.js
  18. +9
    -0
      frappe/public/js/frappe/misc/common.js
  19. +10
    -4
      frappe/public/js/frappe/model/model.js
  20. +1
    -0
      frappe/public/js/frappe/request.js
  21. +13
    -11
      frappe/public/js/frappe/socketio_client.js
  22. +1
    -11
      frappe/public/js/frappe/ui/page.js
  23. +8
    -5
      frappe/public/js/frappe/ui/toolbar/search_utils.js
  24. +2
    -1
      frappe/public/js/legacy/clientscriptAPI.js
  25. +1
    -1
      frappe/public/js/legacy/form.js
  26. +1
    -1
      frappe/sessions.py
  27. +4
    -8
      frappe/tests/ui/data/test_lib.js
  28. +15
    -7
      frappe/utils/selenium_testdriver.py

+ 14
- 2
frappe/__init__.py 查看文件

@@ -475,13 +475,26 @@ def only_for(roles):
if not roles.intersection(myroles): if not roles.intersection(myroles):
raise PermissionError 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): def clear_cache(user=None, doctype=None):
"""Clear **User**, **DocType** or global cache. """Clear **User**, **DocType** or global cache.


:param user: If user is given, only user cache is cleared. :param user: If user is given, only user cache is cleared.
:param doctype: If doctype is given, only DocType cache is cleared.""" :param doctype: If doctype is given, only DocType cache is cleared."""
import frappe.sessions import frappe.sessions
from frappe.core.doctype.domain_settings.domain_settings import clear_domain_cache
if doctype: if doctype:
import frappe.model.meta import frappe.model.meta
frappe.model.meta.clear_cache(doctype) frappe.model.meta.clear_cache(doctype)
@@ -493,7 +506,6 @@ def clear_cache(user=None, doctype=None):
frappe.sessions.clear_cache() frappe.sessions.clear_cache()
translate.clear_cache() translate.clear_cache()
reset_metadata_version() reset_metadata_version()
clear_domain_cache()
local.cache = {} local.cache = {}
local.new_doc_templates = {} local.new_doc_templates = {}




+ 5
- 5
frappe/build.js 查看文件

@@ -60,11 +60,11 @@ function watch() {
io.emit('reload_css', filename); 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(); watch_build_json();
}); });




+ 83
- 1
frappe/core/doctype/domain/domain.py 查看文件

@@ -4,7 +4,89 @@


from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe

from frappe.model.document import Document from frappe.model.document import Document
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields


class Domain(Document): 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])))

+ 39
- 4
frappe/core/doctype/domain_settings/domain_settings.py 查看文件

@@ -7,8 +7,46 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document


class DomainSettings(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): 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(): def get_active_domains():
""" get the domains set in the Domain Settings as active domain """ """ get the domains set in the Domain Settings as active domain """
@@ -33,6 +71,3 @@ def get_active_modules():
return active_modules return active_modules


return frappe.cache().get_value('active_modules', _get_active_modules) return frappe.cache().get_value('active_modules', _get_active_modules)

def clear_domain_cache():
frappe.cache().delete_key(['active_domains', 'active_modules'])

+ 1
- 1
frappe/core/doctype/system_settings/system_settings.py 查看文件

@@ -14,7 +14,7 @@ from frappe.twofactor import toggle_two_factor_auth
class SystemSettings(Document): class SystemSettings(Document):
def validate(self): def validate(self):
enable_password_policy = cint(self.enable_password_policy) and True or False 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: if enable_password_policy and minimum_password_score <= 0:
frappe.throw(_("Please select Minimum Password Score")) frappe.throw(_("Please select Minimum Password Score"))
elif not enable_password_policy: elif not enable_password_policy:


+ 4
- 0
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')]}`''' :param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`'''
for doctype, fields in custom_fields.items(): for doctype, fields in custom_fields.items():
if isinstance(fields, dict):
# only one field
fields = [fields]

for df in fields: for df in fields:
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]})
if not field: if not field:


+ 16
- 13
frappe/desk/page/setup_wizard/setup_wizard.js 查看文件

@@ -508,19 +508,22 @@ frappe.setup.utils = {


bind_language_events: function(slide) { bind_language_events: function(slide) {
slide.get_input("language").unbind("change").on("change", function() { 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);
}); });
}, },




+ 1
- 1
frappe/desk/reportview.py 查看文件

@@ -352,7 +352,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with
for f in filters: for f in filters:
if isinstance(f[1], string_types) and f[1][0] == '!': if isinstance(f[1], string_types) and f[1][0] == '!':
flt.append([doctype, f[0], '!=', f[1][1:]]) 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"): f[1][0] in (">", "<", ">=", "<=", "like", "not like", "in", "not in", "between"):


flt.append([doctype, f[0], f[1][0], f[1][1]]) flt.append([doctype, f[0], f[1][0], f[1][1]])


+ 3
- 2
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.v6_1.rename_file_data
frappe.patches.v7_0.re_route #2016-06-27 frappe.patches.v7_0.re_route #2016-06-27
frappe.patches.v7_2.remove_in_filter 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', 'docfield', force=True) #2017-03-03
execute:frappe.reload_doc('core', 'doctype', 'docperm') #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.drop_is_custom_from_docperm
frappe.patches.v8_0.update_records_in_global_search #11-05-2017 frappe.patches.v8_0.update_records_in_global_search #11-05-2017
frappe.patches.v8_0.update_published_in_global_search frappe.patches.v8_0.update_published_in_global_search


+ 7
- 0
frappe/public/js/frappe/db.js 查看文件

@@ -2,6 +2,13 @@
// MIT License. See license.txt // MIT License. See license.txt


frappe.db = { 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) { get_value: function(doctype, filters, fieldname, callback) {
return frappe.call({ return frappe.call({
method: "frappe.client.get_value", method: "frappe.client.get_value",


+ 4
- 0
frappe/public/js/frappe/form/controls/base_control.js 查看文件

@@ -47,6 +47,10 @@ frappe.ui.form.Control = Class.extend({
// returns "Read", "Write" or "None" // returns "Read", "Write" or "None"
// as strings based on permissions // as strings based on permissions
get_status: function(explain) { get_status: function(explain) {
if (this.df.get_status) {
return this.df.get_status(this);
}

if(!this.doctype && !this.docname) { if(!this.doctype && !this.docname) {
// like in case of a dialog box // like in case of a dialog box
if (cint(this.df.hidden)) { if (cint(this.df.hidden)) {


+ 1
- 1
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 // refresh value
if(me.doctype && me.docname) { if(me.doctype && me.docname) {
me.value = frappe.model.get_value(me.doctype, me.docname, me.df.fieldname); me.value = frappe.model.get_value(me.doctype, me.docname, me.df.fieldname);


+ 2
- 2
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() { setup_autoname_check: function() {
if (!this.df.parent) return; if (!this.df.parent) return;
this.meta = frappe.get_meta(this.df.parent); 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(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.$input.on('keyup', () => {
this.set_description(''); this.set_description('');
if (this.doc && this.doc.__islocal) { if (this.doc && this.doc.__islocal) {


+ 5
- 7
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.indicator = this.$wrapper.find('.password-strength-indicator');
this.message = this.$wrapper.find('.help-box'); 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) { get_password_strength: function(value) {


+ 23
- 1
frappe/public/js/frappe/form/layout.js 查看文件

@@ -25,7 +25,7 @@ frappe.ui.form.Layout = Class.extend({
this.wrapper = $('<div class="form-layout">').appendTo(this.parent); this.wrapper = $('<div class="form-layout">').appendTo(this.parent);
this.message = $('<div class="form-message text-muted small hidden"></div>').appendTo(this.wrapper); this.message = $('<div class="form-message text-muted small hidden"></div>').appendTo(this.wrapper);
if(!this.fields) { 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.setup_tabbing();
this.render(); this.render();
@@ -35,6 +35,28 @@ frappe.ui.form.Layout = Class.extend({
this.show_message(__("This form does not have any input")); 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) { show_message: function(html) {
if(html) { if(html) {
if(html.substr(0, 1)!=='<') { if(html.substr(0, 1)!=='<') {


+ 8
- 5
frappe/public/js/frappe/form/quick_entry.js 查看文件

@@ -1,6 +1,6 @@
frappe.provide('frappe.ui.form'); 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 trimmed_doctype = doctype.replace(/ /g, '');
var controller_name = "QuickEntryForm"; var controller_name = "QuickEntryForm";


@@ -8,15 +8,16 @@ frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback) => {
controller_name = trimmed_doctype + "QuickEntryForm"; 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(); return frappe.quick_entry.setup();
}; };


frappe.ui.form.QuickEntryForm = Class.extend({ 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.doctype = doctype;
this.after_insert = after_insert; this.after_insert = after_insert;
this.init_callback = init_callback; this.init_callback = init_callback;
this.doc = doc;
}, },


setup: function() { setup: function() {
@@ -40,7 +41,9 @@ frappe.ui.form.QuickEntryForm = Class.extend({
this.mandatory = $.map(frappe.get_meta(this.doctype).fields, this.mandatory = $.map(frappe.get_meta(this.doctype).fields,
function(d) { return (d.reqd || d.bold && !d.read_only) ? d : null; }); function(d) { return (d.reqd || d.bold && !d.read_only) ? d : null; });
this.meta = frappe.get_meta(this.doctype); 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(){ is_quick_entry: function(){
@@ -108,7 +111,7 @@ frappe.ui.form.QuickEntryForm = Class.extend({
this.dialog.onhide = () => frappe.quick_entry = null; this.dialog.onhide = () => frappe.quick_entry = null;
this.dialog.show(); this.dialog.show();
this.set_defaults(); this.set_defaults();
if (this.init_callback) { if (this.init_callback) {
this.init_callback(this.dialog); this.init_callback(this.dialog);
} }


+ 18
- 51
frappe/public/js/frappe/form/save.js 查看文件

@@ -19,27 +19,24 @@ frappe.ui.form.save = function (frm, action, callback, btn) {


var save = function () { var save = function () {
remove_empty_rows(); 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() { 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 check_mandatory = function () {
var me = this; var me = this;
var has_errors = false; var has_errors = false;


+ 9
- 0
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) { frappe.get_palette = function(txt) {
return '#fafbfc'; return '#fafbfc';


+ 10
- 4
frappe/public/js/frappe/model/model.js 查看文件

@@ -327,17 +327,23 @@ $.extend(frappe.model, {
set_value: function(doctype, docname, fieldname, value, fieldtype) { set_value: function(doctype, docname, fieldname, value, fieldtype) {
/* help: Set a value locally (if changed) and execute triggers */ /* 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 = []; let tasks = [];
if(!$.isPlainObject(to_update)) { if(!$.isPlainObject(to_update)) {
to_update = {}; to_update = {};
to_update[fieldname] = value; 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)) { if(doc.__unedited && !(!doc[key] && !value)) {
// unset unedited flag for virgin rows // unset unedited flag for virgin rows
doc.__unedited = false; doc.__unedited = false;


+ 1
- 0
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'}); frappe.msgprint({message:__("Server Error: Please check your server logs or contact tech support."), title:__('Something went wrong'), indicator: 'red'});
try { try {
opts.error_callback && opts.error_callback(); opts.error_callback && opts.error_callback();
frappe.request.report_error(xhr, opts);
} catch (e) { } catch (e) {
frappe.request.report_error(xhr, opts); frappe.request.report_error(xhr, opts);
} }


+ 13
- 11
frappe/public/js/frappe/socketio_client.js 查看文件

@@ -222,17 +222,19 @@ frappe.socketio = {
}, 5); }, 5);
}); });
// js files show alert // js files show alert
frappe.socketio.file_watcher.on('reload_js', function(filename) {
filename = "assets/" + filename;
var msg = $(`
<span>${filename} changed <a data-action="reload">Click to Reload</a></span>
`)
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 = $(`
// <span>${filename} changed <a data-action="reload">Click to Reload</a></span>
// `)
// msg.find('a').click(frappe.ui.toolbar.clear_cache);
// frappe.show_alert({
// indicator: 'orange',
// message: msg
// }, 5);
// });
}, },
process_response: function(data, method) { process_response: function(data, method) {
if(!data) { if(!data) {


+ 1
- 11
frappe/public/js/frappe/ui/page.js 查看文件

@@ -495,14 +495,4 @@ frappe.ui.Page = Class.extend({


this.wrapper.trigger('view-change'); 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);
}
}
});

+ 8
- 5
frappe/public/js/frappe/ui/toolbar/search_utils.js 查看文件

@@ -50,24 +50,27 @@ frappe.search.utils = {
find(values, keywords, function(match) { find(values, keywords, function(match) {
var out = { var out = {
route: match[1] 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.label = __(match[1][1]) + " " + match[1][2].bold();
out.value = __(match[1][1]) + " " + match[1][2]; out.value = __(match[1][1]) + " " + match[1][2];
} else { } else {
out.label = __(match[1][1]).bold(); out.label = __(match[1][1]).bold();
out.value = __(match[1][1]); 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; var type = match[1][0], label = type;
if(type==='modules') label = 'Module'; if(type==='modules') label = 'Module';
else if(type==='query-report') label = 'Report'; else if(type==='query-report') label = 'Report';
out.label = __(match[1][1]).bold() + " " + __(label); out.label = __(match[1][1]).bold() + " " + __(label);
out.value = __(match[1][1]) + " " + __(label); out.value = __(match[1][1]) + " " + __(label);
} else {
} else if (match[0]) {
out.label = match[0].bold(); out.label = match[0].bold();
out.value = match[0]; out.value = match[0];
} else {
// eslint-disable-next-line
console.log('Illegal match', match);
} }
out.index = 80; out.index = 80;
return out; return out;


+ 2
- 1
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);
}); });
} }
} }


+ 1
- 1
frappe/public/js/legacy/form.js 查看文件

@@ -225,7 +225,7 @@ _f.Frm.prototype.watch_model_updates = function() {
}; };


_f.Frm.prototype.setup_std_layout = function() { _f.Frm.prototype.setup_std_layout = function() {
this.form_wrapper = $('<div></div>').appendTo(this.layout_main);
this.form_wrapper = $('<div></div>').appendTo(this.layout_main);
this.body = $('<div></div>').appendTo(this.form_wrapper); this.body = $('<div></div>').appendTo(this.form_wrapper);


// only tray // only tray


+ 1
- 1
frappe/sessions.py 查看文件

@@ -53,7 +53,7 @@ def clear_global_cache():
frappe.model.meta.clear_cache() frappe.model.meta.clear_cache()
frappe.cache().delete_value(["app_hooks", "installed_apps", frappe.cache().delete_value(["app_hooks", "installed_apps",
"app_modules", "module_app", "notification_config", 'system_settings' "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() frappe.setup_module_map()






+ 4
- 8
frappe/tests/ui/data/test_lib.js 查看文件

@@ -1,15 +1,11 @@
frappe.tests = { frappe.tests = {
data: {}, data: {},
get_fixture_names: (doctype) => {
return Object.keys(frappe.test_data[doctype]);
},
make: function(doctype, data) { make: function(doctype, data) {
return frappe.run_serially([ return frappe.run_serially([
() => frappe.set_route('List', doctype), () => frappe.set_route('List', doctype),
() => frappe.new_doc(doctype), () => frappe.new_doc(doctype),
() => { () => {
if (frappe.quick_entry)
{
if (frappe.quick_entry) {
frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(); frappe.quick_entry.dialog.$wrapper.find('.edit-full').click();
return frappe.timeout(1); return frappe.timeout(1);
} }
@@ -79,13 +75,13 @@ frappe.tests = {
}); });
return frappe.run_serially(grid_row_tasks); return frappe.run_serially(grid_row_tasks);
}, },
setup_doctype: (doctype) => {
setup_doctype: (doctype, data) => {
return frappe.run_serially([ return frappe.run_serially([
() => frappe.set_route('List', doctype), () => frappe.set_route('List', doctype),
() => frappe.timeout(1), () => frappe.timeout(1),
() => { () => {
frappe.tests.data[doctype] = []; frappe.tests.data[doctype] = [];
let expected = frappe.tests.get_fixture_names(doctype);
let expected = Object.keys(data);
cur_list.data.forEach((d) => { cur_list.data.forEach((d) => {
frappe.tests.data[doctype].push(d.name); frappe.tests.data[doctype].push(d.name);
if(expected.indexOf(d.name) !== -1) { if(expected.indexOf(d.name) !== -1) {
@@ -98,7 +94,7 @@ frappe.tests = {
expected.forEach(function(d) { expected.forEach(function(d) {
if(d) { if(d) {
tasks.push(() => frappe.tests.make(doctype, tasks.push(() => frappe.tests.make(doctype,
frappe.test_data[doctype][d]));
data[d]));
} }
}); });




+ 15
- 7
frappe/utils/selenium_testdriver.py 查看文件

@@ -84,16 +84,19 @@ class TestDriver(object):
time.sleep(0.2) time.sleep(0.2)


def set_field(self, fieldname, text): 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): 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): 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): def find(self, selector=None, everywhere=False, xpath=None):
if xpath: if xpath:
@@ -164,7 +167,11 @@ class TestDriver(object):
self.wait_for(xpath='//div[@data-page-route="{0}"]'.format('/'.join(args)), timeout=4) self.wait_for(xpath='//div[@data-page-route="{0}"]'.format('/'.join(args)), timeout=4)


def click(self, css_selector, xpath=None): 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): def click_primary_action(self):
selector = ".page-actions .primary-action" selector = ".page-actions .primary-action"
@@ -201,6 +208,7 @@ class TestDriver(object):
return self.get_wait().until(EC.element_to_be_clickable( return self.get_wait().until(EC.element_to_be_clickable(
(by, selector))) (by, selector)))



def execute_script(self, js): def execute_script(self, js):
self.driver.execute_script(js) self.driver.execute_script(js)




Loading…
取消
儲存