From 54d0b777a7ae028f698fe25f3d49bb48309409f4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 16:06:50 +0530 Subject: [PATCH 1/9] user role as child table in profile. checkboxes and user roles managed directly using addchild on client side --- core/doctype/profile/profile.js | 94 ++++++++++++++++++--------- core/doctype/profile/profile.py | 53 +++++---------- core/doctype/profile/profile.txt | 25 ++++++- core/doctype/userrole/userrole.py | 9 ++- core/doctype/userrole/userrole.txt | 5 +- public/js/legacy/widgets/form/form.js | 3 + 6 files changed, 116 insertions(+), 73 deletions(-) diff --git a/core/doctype/profile/profile.js b/core/doctype/profile/profile.js index 9c11f1b8ce..75c84e9e69 100644 --- a/core/doctype/profile/profile.js +++ b/core/doctype/profile/profile.js @@ -1,8 +1,14 @@ cur_frm.cscript.onload = function(doc) { - if(!cur_frm.roles_editor && has_common(user_roles, ["Administrator", "System Manager"])) { - var role_area = $('
') - .appendTo(cur_frm.fields_dict.roles_html.wrapper); - cur_frm.roles_editor = new wn.RoleEditor(role_area); + if(has_common(user_roles, ["Administrator", "System Manager"])) { + if(!cur_frm.roles_editor) { + var role_area = $('
') + .appendTo(cur_frm.fields_dict.roles_html.wrapper); + cur_frm.roles_editor = new wn.RoleEditor(role_area); + } else { + // called when creating a new profile + // and need to clear previous profile's roles + cur_frm.roles_editor.show(); + } } } @@ -21,7 +27,8 @@ cur_frm.cscript.refresh = function(doc) { } cur_frm.cscript.enabled(doc); - cur_frm.roles_editor && cur_frm.roles_editor.show(doc.name); + cur_frm.roles_editor && cur_frm.roles_editor.show(); + if(user==doc.name) { // update display settings wn.ui.set_theme(doc.theme); @@ -53,9 +60,7 @@ cur_frm.cscript.enabled = function(doc) { cur_frm.cscript.validate = function(doc) { if(cur_frm.roles_editor) { - doc.__temp = JSON.stringify({ - roles:cur_frm.roles_editor.get_roles() - }); + cur_frm.roles_editor.set_roles_in_table() } } @@ -69,6 +74,12 @@ wn.RoleEditor = Class.extend({ callback: function(r) { me.roles = r.message; me.show_roles(); + + // refresh call could've already happened + // when all role checkboxes weren't created + if(cur_frm.doc) { + cur_frm.roles_editor.show(); + } } }); }, @@ -90,38 +101,63 @@ wn.RoleEditor = Class.extend({ return false; }) }, - show: function(uid) { + show: function() { var me = this; - this.uid = uid; - // set user roles - wn.call({ - method:'core.doctype.profile.profile.get_user_roles', - args: {uid:uid}, - callback: function(r, rt) { - $(me.wrapper).find('input[type="checkbox"]').attr('checked', false); - for(var i in r.message) { - $(me.wrapper) - .find('[data-user-role="'+r.message[i] - +'"] input[type="checkbox"]').attr('checked',true); - } - } - }) + + // uncheck all roles + $(this.wrapper).find('input[type="checkbox"]').removeAttr("checked"); + + // set user roles as checked + $.each(wn.model.get("UserRole", {parent: cur_frm.doc.name, + parentfield: "user_roles"}), function(i, user_role) { + $(me.wrapper) + .find('[data-user-role="'+user_role.role + +'"] input[type="checkbox"]').attr('checked', 'checked'); + }); }, + set_roles_in_table: function() { + var opts = this.get_roles(); + var existing_roles_map = {}; + var existing_roles_list = []; + + $.each(wn.model.get("UserRole", {parent: cur_frm.doc.name, + parentfield: "user_roles"}), function(i, user_role) { + existing_roles_map[user_role.role] = user_role.name; + existing_roles_list.push(user_role.role); + }); + + // remove unchecked roles + $.each(opts.unchecked_roles, function(i, role) { + if(existing_roles_list.indexOf(role)!=-1) { + wn.model.clear_doc("UserRole", existing_roles_map[role]); + } + }); + + // add new roles that are checked + $.each(opts.checked_roles, function(i, role) { + if(existing_roles_list.indexOf(role)==-1) { + var user_role = wn.model.add_child(cur_frm.doc, "UserRole", "user_roles"); + user_role.role = role; + } + }); + + refresh_field("user_roles"); + }, get_roles: function() { - var set_roles = []; - var unset_roles = []; + var checked_roles = []; + var unchecked_roles = []; $(this.wrapper).find('[data-user-role]').each(function() { var $check = $(this).find('input[type="checkbox"]'); if($check.attr('checked')) { - set_roles.push($(this).attr('data-user-role')); + checked_roles.push($(this).attr('data-user-role')); } else { - unset_roles.push($(this).attr('data-user-role')); + unchecked_roles.push($(this).attr('data-user-role')); } }); return { - set_roles: set_roles, - unset_roles: unset_roles + checked_roles: checked_roles, + unchecked_roles: unchecked_roles } }, show_permissions: function(role) { diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py index e1a6788fe4..b5eaeecd58 100644 --- a/core/doctype/profile/profile.py +++ b/core/doctype/profile/profile.py @@ -49,7 +49,7 @@ class DocType: del self.doc.fields['__temp'] self.validate_max_users() - self.update_roles() + self.check_one_system_manager() # do not allow disabling administrator/guest if not cint(self.doc.enabled) and self.doc.name in ["Administrator", "Guest"]: @@ -88,44 +88,21 @@ class DocType: 1. Upgrade to the unlimited users plan, or
\ 2. Disable one or more of your existing users and try again""" \ % {'active_users': active_users}, raise_exception=1) - - def update_roles(self): - """update roles if set""" - - if self.temp.get('roles'): - from webnotes.model.doc import Document - - # remove roles - webnotes.conn.sql("""delete from tabUserRole where parent='%s' - and role in ('%s')""" % (self.doc.name, - "','".join(self.temp['roles']['unset_roles']))) - - if "System Manager" in self.temp['roles']['unset_roles']: - self.check_one_system_manager() - - # add roles - user_roles = webnotes.get_roles(self.doc.name) - for role in self.temp['roles']['set_roles']: - if not role in user_roles: - self.add_role(role) - - def add_role(self, role): - d = webnotes.doc('UserRole') - d.role = role - d.parenttype = 'Profile' - d.parentfield = 'user_roles' - d.parent = self.doc.name - d.save() - + def check_one_system_manager(self): - if not webnotes.conn.sql("""select parent from tabUserRole where role='System Manager' and docstatus<2 and parent!='Administrator'"""): - if webnotes.conn.sql("""select count(*) from `tabProfile` - where name not in ('Administrator', 'Guest')""")[0][0] == 0: - self.temp["roles"]["set_roles"].append("System Manager") - return - - webnotes.msgprint("""Cannot un-select as System Manager as there must - be atleast one 'System Manager'.""", raise_exception=1) + # if adding system manager, do nothing + if "System Manager" in [user_role.role for user_role in + self.doclist.get({"parentfield": "user_roles"})]: + return + + if not webnotes.conn.sql("""select parent from tabUserRole where role='System Manager' and docstatus<2 and parent not in ('Administrator', %s)""", (self.doc.name,)): + webnotes.msgprint("""Adding System Manager Role as there must + be atleast one 'System Manager'.""") + self.doclist.append({ + "doctype": "UserRole", + "parentfield": "user_roles", + "role": "System Manager" + }) def on_update(self): # owner is always name diff --git a/core/doctype/profile/profile.txt b/core/doctype/profile/profile.txt index 8badd31ffd..7cd3212781 100644 --- a/core/doctype/profile/profile.txt +++ b/core/doctype/profile/profile.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-06 16:11:18", + "creation": "2013-02-11 12:30:10", "docstatus": 0, - "modified": "2013-02-11 11:43:26", + "modified": "2013-02-13 09:35:48", "modified_by": "Administrator", "owner": "Administrator" }, @@ -168,6 +168,7 @@ "fieldtype": "Password", "hidden": 0, "label": "New Password", + "no_copy": 1, "print_hide": 1 }, { @@ -377,6 +378,26 @@ "oldfieldname": "file_list", "oldfieldtype": "Text" }, + { + "doctype": "DocField", + "fieldname": "roles_assigned_to_user", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Roles Assigned To User", + "no_copy": 0, + "print_hide": 1, + "read_only": 1 + }, + { + "doctype": "DocField", + "fieldname": "user_roles", + "fieldtype": "Table", + "hidden": 1, + "label": "Roles Assigned", + "options": "UserRole", + "print_hide": 1, + "read_only": 1 + }, { "cancel": 1, "create": 1, diff --git a/core/doctype/userrole/userrole.py b/core/doctype/userrole/userrole.py index e669908514..d16fcfc88b 100644 --- a/core/doctype/userrole/userrole.py +++ b/core/doctype/userrole/userrole.py @@ -21,7 +21,14 @@ from __future__ import unicode_literals import webnotes +from webnotes.utils import cint class DocType: def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file + self.doc, self.doclist = d, dl + + def validate(self): + if cint(self.doc.fields.get("__islocal")) and webnotes.conn.exists("UserRole", { + "parent": self.doc.parent, "role": self.doc.role}): + webnotes.msgprint("Role Already Exists", raise_exception=True) + \ No newline at end of file diff --git a/core/doctype/userrole/userrole.txt b/core/doctype/userrole/userrole.txt index 15f0962eba..29e86c6157 100644 --- a/core/doctype/userrole/userrole.txt +++ b/core/doctype/userrole/userrole.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-10 16:34:04", + "creation": "2013-02-06 11:30:13", "docstatus": 0, - "modified": "2013-02-06 11:43:05", + "modified": "2013-02-13 07:51:57", "modified_by": "Administrator", "owner": "Administrator" }, @@ -12,7 +12,6 @@ "allow_print": 0, "autoname": "UR.#####", "doctype": "DocType", - "document_type": "Master", "hide_heading": 0, "hide_toolbar": 0, "issingle": 0, diff --git a/public/js/legacy/widgets/form/form.js b/public/js/legacy/widgets/form/form.js index 2585242989..c48d395aa3 100644 --- a/public/js/legacy/widgets/form/form.js +++ b/public/js/legacy/widgets/form/form.js @@ -898,6 +898,9 @@ _f.Frm.prototype.save = function(save_action, callback, btn, on_error) { $(document.activeElement).blur(); var me = this; + if((!this.meta.in_dialog || this.in_form) && !this.meta.istable) + scroll(0, 0); + // validate if(save_action!="Cancel") { validated = true; From 05c167e22cb09ec9dbab7796818789ac2ad90d1e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 16:16:06 +0530 Subject: [PATCH 2/9] fixes in profile one system manager validation --- core/doctype/profile/profile.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py index f93f889d88..971bc3779d 100644 --- a/core/doctype/profile/profile.py +++ b/core/doctype/profile/profile.py @@ -91,11 +91,15 @@ class DocType: def check_one_system_manager(self): # if adding system manager, do nothing - if "System Manager" in [user_role.role for user_role in - self.doclist.get({"parentfield": "user_roles"})]: + if not cint(self.doc.enabled) or ("System Manager" in [user_role.role for user_role in + self.doclist.get({"parentfield": "user_roles"})]): return - if not webnotes.conn.sql("""select parent from tabUserRole where role='System Manager' and docstatus<2 and parent not in ('Administrator', %s)""", (self.doc.name,)): + if not webnotes.conn.sql("""select distinct parent from tabUserRole user_role + where role='System Manager' and docstatus<2 + and parent not in ('Administrator', %s) and exists + (select * from `tabProfile` profile + where profile.name=user_role.parent and enabled=1)""", (self.doc.name,)): webnotes.msgprint("""Adding System Manager Role as there must be atleast one 'System Manager'.""") self.doclist.append({ From ffd3c8082e030b0a150cb67c37452da81e8c449e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 17:48:47 +0530 Subject: [PATCH 3/9] fixes in db.py --- webnotes/db.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webnotes/db.py b/webnotes/db.py index b87e235e84..b29e7e3428 100644 --- a/webnotes/db.py +++ b/webnotes/db.py @@ -255,10 +255,10 @@ class Database: """Get a single / multiple value from a record. For Single DocType, let filters be = None""" - if fieldname!="*" and isinstance(fieldname, basestring): - fieldname = "`" + fieldname + "`" - if filters is not None and (filters!=doctype or filters=='DocType'): + if fieldname!="*" and isinstance(fieldname, basestring): + fieldname = "`" + fieldname + "`" + fl = isinstance(fieldname, basestring) and fieldname or \ ("`" + "`, `".join(fieldname) + "`") conditions, filters = self.build_conditions(filters) From 22e129caf9cfd03177e944e08cbb847a23a5a0be Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 18:04:21 +0530 Subject: [PATCH 4/9] fixes in parentfield of userrole --- core/doctype/doctype/doctype.js | 2 +- webnotes/install_lib/install.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/doctype/doctype/doctype.js b/core/doctype/doctype/doctype.js index 4bfeda6a27..8fde2a34e9 100644 --- a/core/doctype/doctype/doctype.js +++ b/core/doctype/doctype/doctype.js @@ -41,7 +41,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.perm = [[1,0,0]] // make help heading - webnotes.msgprint('Cannot Edit DocType directly: \ + msgprint('Cannot Edit DocType directly: \ To edit DocType properties, \ create / update Custom Field, \ Custom Script \ diff --git a/webnotes/install_lib/install.py b/webnotes/install_lib/install.py index d6e1e499c7..5a58a0018e 100755 --- a/webnotes/install_lib/install.py +++ b/webnotes/install_lib/install.py @@ -135,9 +135,9 @@ class Installer: # userroles {'doctype':'UserRole', 'parent': 'Administrator', 'role': 'Administrator', - 'parenttype':'Profile', 'parentfield':'userroles'}, + 'parenttype':'Profile', 'parentfield':'user_roles'}, {'doctype':'UserRole', 'parent': 'Guest', 'role': 'Guest', - 'parenttype':'Profile', 'parentfield':'userroles'} + 'parenttype':'Profile', 'parentfield':'user_roles'} ] webnotes.conn.begin() From 43061118a6e766b12b9c024e1e256109cfd5f39d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 18:35:33 +0530 Subject: [PATCH 5/9] added add_role method in profile --- webnotes/model/wrapper.py | 2 +- webnotes/profile.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/webnotes/model/wrapper.py b/webnotes/model/wrapper.py index a704e71a0c..0d9fde0923 100644 --- a/webnotes/model/wrapper.py +++ b/webnotes/model/wrapper.py @@ -300,7 +300,7 @@ class ModelWrapper: def no_permission_to(self, ptype): webnotes.msgprint(("%s (%s): " % (self.doc.name, _(self.doc.doctype))) + \ _("No Permission to ") + ptype, raise_exception=True) - + # clone def clone(source_wrapper): diff --git a/webnotes/profile.py b/webnotes/profile.py index 051059a895..e978ee7677 100644 --- a/webnotes/profile.py +++ b/webnotes/profile.py @@ -191,7 +191,7 @@ class Profile: d['in_create'] = self.in_create return d - + def get_user_fullname(user): fullname = webnotes.conn.sql("SELECT CONCAT_WS(' ', first_name, last_name) FROM `tabProfile` WHERE name=%s", user) return fullname and fullname[0][0] or '' @@ -206,3 +206,12 @@ def get_system_managers(): where ur.parent = p.name and ur.role="System Manager")""") return [p[0] for p in system_managers] + +def add_role(profile, role): + profile_wrapper = webnotes.model_wrapper("Profile", profile) + profile_wrapper.doclist.append({ + "doctype": "UserRole", + "parentfield": "user_roles", + "role": role + }) + profile_wrapper.save() From 649f97967c29b502f03203ca48974e29fdbf7194 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 19:48:41 +0530 Subject: [PATCH 6/9] in SelectField's get_value, options can be a dict or string. use appropriate when comparing. Allow's SelectField having different label and value --- public/js/legacy/widgets/form/fields.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/public/js/legacy/widgets/form/fields.js b/public/js/legacy/widgets/form/fields.js index 48243acdb4..72c8839e47 100644 --- a/public/js/legacy/widgets/form/fields.js +++ b/public/js/legacy/widgets/form/fields.js @@ -1075,7 +1075,7 @@ SelectField.prototype.make_input = function() { me.options_list = me.df.options || [""]; else me.options_list = me.df.options?me.df.options.split('\n'):['']; - + // add options if(me.in_filter && me.options_list[0]!='') { me.options_list = add_lists([''], me.options_list); @@ -1124,8 +1124,18 @@ SelectField.prototype.make_input = function() { if(me.input.options[i].value && inList(typeof(v)=='string'?v.split(","):v, me.input.options[i].value)) me.input.options[i].selected = 1; } - } else if(in_list(me.options_list, v)){ - me.input.value = v; + } else { + // use option's value if dict, else use string for comparison and setting + for(var i in me.options_list) { + var option = me.options_list[i]; + if(typeof(option)!=="string") { + option = option.value; + } + if(option === v) { + me.input.value = v; + break; + } + } } } } From 56fc922e5a276d86fb0eaa832499ef880d04dd60 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 19:57:58 +0530 Subject: [PATCH 7/9] fixes in test runner and add role for profile --- webnotes/test_runner.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webnotes/test_runner.py b/webnotes/test_runner.py index 7032c76eed..34cf8ef135 100644 --- a/webnotes/test_runner.py +++ b/webnotes/test_runner.py @@ -134,6 +134,9 @@ if __name__=="__main__": args = parser.parse_args() webnotes.print_messages = args.verbose + if not webnotes.conn: + webnotes.connect() + if args.doctype: run_unittest(args.doctype[0]) else: From d5ad376a04cca9d259070d2336eb364289597de8 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 20:26:20 +0530 Subject: [PATCH 8/9] updated profile test records --- core/doctype/profile/test_profile.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/doctype/profile/test_profile.py b/core/doctype/profile/test_profile.py index a041818853..4aca25bc46 100644 --- a/core/doctype/profile/test_profile.py +++ b/core/doctype/profile/test_profile.py @@ -1,12 +1,18 @@ test_records = [[{ "doctype":"Profile", - "email": "test@erpnext.com", + "email": "test@example.com", "first_name": "_Test", "new_password": "testpassword" }], [{ "doctype":"Profile", - "email": "test1@erpnext.com", + "email": "test1@example.com", "first_name": "_Test1", "new_password": "testpassword" +}], +[{ + "doctype":"Profile", + "email": "test2@example.com", + "first_name": "_Test2", + "new_password": "testpassword" }]] \ No newline at end of file From 751202e639b9bb1a4513669bd98032d00f2a542d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 13 Feb 2013 21:57:34 +0530 Subject: [PATCH 9/9] fix in format_number --- public/js/wn/misc/number_format.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/wn/misc/number_format.js b/public/js/wn/misc/number_format.js index 56dbd752f0..67681a7295 100644 --- a/public/js/wn/misc/number_format.js +++ b/public/js/wn/misc/number_format.js @@ -23,10 +23,10 @@ window.format_number = function(v, format, decimals){ if(isNaN(+v)) { v=0; }; - + // remove group separators (if any) - if(typeof v=="string") { - v = replace_all(info.group_sep, ""); + if(typeof(v)==="string") { + v = replace_all(v, info.group_sep, ""); } if(v<0) var is_negative = true; @@ -64,7 +64,7 @@ window.format_number = function(v, format, decimals){ if(part[0]+""=="") { part[0]="0"; } - + // join decimal part[1] = part[1] ? (info.decimal_str + part[1]) : "";