wn.pages['permission-manager'].onload = function(wrapper) { wn.ui.make_app_page({ parent: wrapper, title: wn._('Permission Manager'), single_column: true }); $(wrapper).find(".layout-main").html("
\ \ \ \ \ \ \
\

"+wn._("Quick Help for Setting Permissions")+":

\
    \
  1. "+wn._("Permissions are set on Roles and Document Types (called DocTypes) by restricting read, edit, make new, submit, cancel, amend and report rights.")+"
  2. \
  3. "+wn._("Permissions translate to Users based on what Role they are assigned")+".
  4. \
  5. "+wn._("To set user roles, just go to Setup > Users and click on the user to assign roles.")+"
  6. \
  7. "+wn._("The system provides pre-defined roles, but you can add new roles to set finer permissions")+".
  8. \
  9. "+wn._("Permissions are automatically translated to Standard Reports and Searches")+".
  10. \
  11. "+wn._("As a best practice, do not assign the same set of permission rule to different Roles instead set multiple Roles to the User")+".
  12. \
\
\

"+wn._("Meaning of Submit, Cancel, Amend")+":

\
    \
  1. "+wn._("Certain documents should not be changed once final, like an Invoice for example. The final state for such documents is called Submitted. You can restrict which roles can Submit.")+"
  2. \
  3. "+wn._("Cancel allows you change Submitted documents by cancelling them and amending them.")+ wn._("Cancel permission also allows the user to delete a document (if it is not linked to any other document).")+"
  4. \
  5. "+wn._("When you Amend a document after cancel and save it, it will get a new number that is a version of the old number.")+ wn._("For example if you cancel and amend 'INV004' it will become a new document 'INV004-1'. This helps you to keep track of each amendment.")+ "
  6. \
\
\

"+wn._("Permission Levels")+":

\
    \
  1. "+wn._("Permissions at level 0 are 'Document Level' permissions, i.e. they are primary for access to the document.")+ wn._("If a User does not have access at Level 0, then higher levels are meaningless")+".
  2. \
  3. "+wn._("Permissions at higher levels are 'Field Level' permissions. All Fields have a 'Permission Level' set against them and the rules defined at that permissions apply to the field. This is useful incase you want to hide or make certain field read-only.")+ wn._("You can use Customize Form to set levels on fields.")+"
  4. \
\
\

"+wn._("Restricting By User")+":

\
    \
  1. "+wn._("To restrict a User of a particular Role to documents that are only self-created.")+ wn._("Click on button in the 'Condition' column and select the option 'User is the creator of the document'")+".
  2. \
  3. "+wn._("To restrict a User of a particular Role to documents that are explicitly assigned to them")+ ":"+ + wn._("create a Custom Field of type Link (Profile) and then use the 'Condition' settings to map that field to the Permission rule.")+ "
\
\

"+wn._("Advanced Settings")+":

\

"+wn._("To further restrict permissions based on certain values in a document, use the 'Condition' settings.")+"

"+ wn._("For example: You want to restrict users to transactions marked with a certain property called 'Territory'")+":

\
    \
  1. "+wn._("Make sure that the transactions you want to restrict have a Link field 'territory' that maps to a 'Territory' master.")+" " +wn._("If not, create a")+ ""+wn._("Custom Field")+""+ wn._("of type Link")+".
  2. \
  3. "+wn._("In the Permission Manager, click on the button in the 'Condition' column for the Role you want to restrict.")+"
  4. \
  5. "+wn._("A new popup will open that will ask you to select further conditions.")+ wn._("If the 'territory' Link Field exists, it will give you an option to select it")+".
  6. \
  7. "+wn._("Go to Setup > User Properties to set \ 'territory' for diffent Users.")+"
  8. \
\

"+wn._("Once you have set this, the users will only be able access documents with that property.")+"

\
\

"+wn._("If these instructions where not helpful, please add in your suggestions at GitHub Issues")+"

\
"); wrapper.permission_engine = new wn.PermissionEngine(wrapper); } wn.pages['permission-manager'].refresh = function(wrapper) { wrapper.permission_engine.set_from_route(); } wn.PermissionEngine = Class.extend({ init: function(wrapper) { this.wrapper = wrapper; this.body = $(this.wrapper).find(".perm-engine"); this.make(); this.refresh(); this.add_check_events(); }, make: function() { var me = this; me.make_reset_button(); return wn.call({ module:"core", page:"permission_manager", method: "get_roles_and_doctypes", callback: function(r) { me.options = r.message; me.doctype_select = me.wrapper.appframe.add_select("doctypes", [wn._("Select Document Type")+"..."].concat(r.message.doctypes)) .change(function() { wn.set_route("permission-manager", $(this).val()) }); me.role_select = me.wrapper.appframe.add_select("roles", [wn._("Select Role")+"..."].concat(r.message.roles)) .change(function() { me.refresh(); }); me.set_from_route(); } }); }, set_from_route: function() { if(wn.get_route()[1] && this.doctype_select) { this.doctype_select.val(wn.get_route()[1]); this.refresh(); } else { this.refresh(); } }, make_reset_button: function() { var me = this; me.reset_button = me.wrapper.appframe.set_title_right("Reset Permissions", function() { if(wn.confirm("Reset Permissions for " + me.get_doctype() + "?", function() { return wn.call({ module:"core", page:"permission_manager", method:"reset", args: { doctype:me.get_doctype(), }, callback: function() { me.refresh(); } }); })); }).toggle(false); }, get_doctype: function() { var doctype = this.doctype_select.val(); return this.doctype_select.get(0).selectedIndex==0 ? null : doctype; }, get_role: function() { var role = this.role_select.val(); return this.role_select.get(0).selectedIndex==0 ? null : role; }, refresh: function() { var me = this; if(!me.doctype_select) { this.body.html("
Loading...
"); return; } if(!me.get_doctype() && !me.get_role()) { this.body.html("
"+wn._("Select Document Type or Role to start.")+"
"); return; } // get permissions wn.call({ module: "core", page: "permission_manager", method: "get_permissions", args: { doctype: me.get_doctype(), role: me.get_role() }, callback: function(r) { me.render(r.message); } }); me.reset_button.toggle(me.get_doctype() ? true : false); }, render: function(perm_list) { this.body.empty(); this.perm_list = perm_list || []; if(!this.perm_list.length) { this.body.html("
" +wn._("No Permissions set for this criteria.")+"
"); } else { this.show_permission_table(this.perm_list); } this.show_add_rule(); }, show_permission_table: function(perm_list) { var me = this; this.table = $("\ \ \
").appendTo(this.body); $.each([["Document Type", 150], ["Role", 100], ["Level",50], ["Read", 50], ["Edit", 50], ["Make New", 50], ["Submit", 50], ["Cancel", 50], ["Amend", 50], ["Report", 50], ["Condition", 150], ["", 50]], function(i, col) { $("").html(col[0]).css("width", col[1]+"px") .appendTo(me.table.find("thead tr")); }); var add_cell = function(row, d, fieldname, is_check) { var cell = $("").appendTo(row).attr("data-fieldname", fieldname); if(is_check) { if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) { cell.html("-"); } else { var input = $("") .prop("checked", d[fieldname] ? true: false) .attr("data-ptype", fieldname) .attr("data-name", d.name) .attr("data-doctype", d.parent) .appendTo(cell); } } else { cell.html(d[fieldname]); } return cell; } $.each(perm_list, function(i, d) { if(!d.permlevel) d.permlevel = 0; var row = $("").appendTo(me.table.find("tbody")); add_cell(row, d, "parent"); me.set_show_users(add_cell(row, d, "role"), d.role); var cell = add_cell(row, d, "permlevel"); if(d.permlevel==0) { cell.css("font-weight", "bold"); row.addClass("warning"); } add_cell(row, d, "read", true); add_cell(row, d, "write", true); add_cell(row, d, "create", true); add_cell(row, d, "submit", true); add_cell(row, d, "cancel", true); add_cell(row, d, "amend", true); add_cell(row, d, "report", true); // buttons me.add_match_button(row, d); me.add_delete_button(row, d); }); }, set_show_users: function(cell, role) { cell.html(""+role+"") .find("a") .attr("data-role", role) .click(function() { var role = $(this).attr("data-role"); wn.call({ module: "core", page: "permission_manager", method: "get_users_with_role", args: { role: role }, callback: function(r) { r.message = $.map(r.message, function(p) { return ''+p+''; }) msgprint("

Users with role "+role+":

" + r.message.join("
")); } }) return false; }) }, add_match_button: function(row, d) { var me = this; if(d.permlevel > 0) { $("-").appendTo(row); return; } var btn = $("") .html(d.match ? d.match : wn._("For All Users")) .appendTo($("").appendTo(row)) .attr("data-name", d.name) .click(function() { me.show_match_manager($(this).attr("data-name")); }); if(d.match) btn.addClass("btn-info"); }, add_delete_button: function(row, d) { var me = this; $("") .appendTo($("").appendTo(row)) .attr("data-name", d.name) .attr("data-doctype", d.parent) .click(function() { return wn.call({ module: "core", page: "permission_manager", method: "remove", args: { name: $(this).attr("data-name"), doctype: $(this).attr("data-doctype") }, callback: function(r) { if(r.exc) { msgprint("Did not remove."); } else { me.refresh(); } } }) }); }, add_check_events: function() { var me = this; this.body.on("click", "input[type='checkbox']", function() { var chk = $(this); var args = { name: chk.attr("data-name"), doctype: chk.attr("data-doctype"), ptype: chk.attr("data-ptype"), value: chk.prop("checked") ? 1 : 0 } return wn.call({ module: "core", page: "permission_manager", method: "update", args: args, callback: function(r) { if(r.exc) { // exception: reverse chk.prop("checked", !chk.prop("checked")); } else { me.get_perm(args.name)[args.ptype]=args.value; } } }) }) }, show_add_rule: function() { var me = this; $("") .appendTo($("

").appendTo(this.body)) .click(function() { var d = new wn.ui.Dialog({ title: wn._("Add New Permission Rule"), fields: [ {fieldtype:"Select", label:"Document Type", options:me.options.doctypes, reqd:1, fieldname:"parent"}, {fieldtype:"Select", label:"Role", options:me.options.roles, reqd:1}, {fieldtype:"Select", label:"Permission Level", options:[0,1,2,3,4,5,6,7,8,9], reqd:1, fieldname: "permlevel", description: wn._("Level 0 is for document level permissions, higher levels for field level permissions.")}, {fieldtype:"Button", label:"Add"}, ] }); if(me.get_doctype()) { d.set_value("parent", me.get_doctype()); d.get_input("parent").prop("disabled", true); } if(me.get_role()) { d.set_value("role", me.get_role()); d.get_input("role").prop("disabled", true); } d.set_value("permlevel", "0"); d.get_input("add").click(function() { var args = d.get_values(); if(!args) { return; } wn.call({ module: "core", page: "permission_manager", method: "add", args: args, callback: function(r) { if(r.exc) { msgprint(wn._("Did not add.")); } else { me.refresh(); } } }) d.hide(); }); d.show(); }); }, get_perm: function(name) { return $.map(this.perm_list, function(d) { if(d.name==name) return d; })[0]; }, show_match_manager:function(name) { var perm = this.get_perm(name); var me = this; wn.model.with_doctype(perm.parent, function() { var dialog = new wn.ui.Dialog({ title: "Applies for Users", }); $(dialog.body).html("

Rule Applies to Following Users:

\ \ ").css("padding", "15px"); // profile fields $.each(me.get_profile_fields(perm.parent), function(i, d) { $("").appendTo(dialog.body); }); // add options for all link fields $.each(me.get_link_fields(perm.parent), function(i, d) { $("").appendTo(dialog.body); }); // button $("") .appendTo($("

").appendTo(dialog.body)) .attr("data-name", perm.name) .click(function() { var match_value = $(dialog.wrapper).find(":radio:checked").val(); var perm = me.get_perm($(this).attr('data-name')) return wn.call({ module: "core", page: "permission_manager", method: "update_match", args: { name: perm.name, doctype: perm.parent, match: match_value }, callback: function() { dialog.hide(); me.refresh(); } }) }); dialog.show(); // select if(perm.match) { $(dialog.wrapper).find("[value='"+perm.match+"']").prop("checked", true).focus(); } else { $(dialog.wrapper).find('[value=""]').prop("checked", true).focus(); } }); }, get_profile_fields: function(doctype) { var profile_fields = wn.model.get("DocField", {parent:doctype, fieldtype:"Link", options:"Profile"}); profile_fields = profile_fields.concat(wn.model.get("DocField", {parent:doctype, fieldtype:"Select", link_doctype:"Profile"})) return profile_fields }, get_link_fields: function(doctype) { return link_fields = wn.model.get("DocField", {parent:doctype, fieldtype:"Link", options:["not in", ["Profile", '[Select]']]}); } })