Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

permission_manager.js 16 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. wn.pages['permission-manager'].onload = function(wrapper) {
  2. wn.ui.make_app_page({
  3. parent: wrapper,
  4. title: wn._('Permission Manager'),
  5. single_column: true
  6. });
  7. $(wrapper).find(".layout-main").html("<div class='perm-engine'></div>\
  8. <table class='table table-bordered' style='background-color: #f9f9f9;'>\
  9. <tr><td>\
  10. <h4><i class='icon-question-sign'></i> "+wn._("Quick Help for Setting Permissions")+":</h4>\
  11. <ol>\
  12. <li>"+wn._("Permissions are set on Roles and Document Types (called DocTypes) by restricting read, edit, make new, submit, cancel, amend and report rights.")+"</li>\
  13. <li>"+wn._("Permissions translate to Users based on what Role they are assigned")+".</li>\
  14. <li>"+wn._("To set user roles, just go to <a href='#List/Profile'>Setup > Users</a> and click on the user to assign roles.")+"</li>\
  15. <li>"+wn._("The system provides pre-defined roles, but you can <a href='#List/Role'>add new roles</a> to set finer permissions")+".</li>\
  16. <li>"+wn._("Permissions are automatically translated to Standard Reports and Searches")+".</li>\
  17. <li>"+wn._("As a best practice, do not assign the same set of permission rule to different Roles instead set multiple Roles to the User")+".</li>\
  18. </ol>\
  19. </tr></td>\
  20. <tr><td>\
  21. <h4><i class='icon-hand-right'></i> "+wn._("Meaning of Submit, Cancel, Amend")+":</h4>\
  22. <ol>\
  23. <li>"+wn._("Certain documents should not be changed once final, like an Invoice for example. The final state for such documents is called <b>Submitted</b>. You can restrict which roles can Submit.")+"</li>\
  24. <li>"+wn._("<b>Cancel</b> allows you change Submitted documents by cancelling them and amending them.")+
  25. wn._("Cancel permission also allows the user to delete a document (if it is not linked to any other document).")+"</li>\
  26. <li>"+wn._("When you <b>Amend</b> a document after cancel and save it, it will get a new number that is a version of the old number.")+
  27. 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.")+
  28. "</li>\
  29. </ol>\
  30. </tr></td>\
  31. <tr><td>\
  32. <h4><i class='icon-signal'></i> "+wn._("Permission Levels")+":</h4>\
  33. <ol>\
  34. <li>"+wn._("Permissions at level 0 are 'Document Level' permissions, i.e. they are primary for access to the document.")+
  35. wn._("If a User does not have access at Level 0, then higher levels are meaningless")+".</li>\
  36. <li>"+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.")+
  37. wn._("You can use <a href='#Form/Customize Form'>Customize Form</a> to set levels on fields.")+"</li>\
  38. </ol>\
  39. </tr></td>\
  40. <tr><td>\
  41. <h4><i class='icon-user'></i> "+wn._("Restricting By User")+":</h4>\
  42. <ol>\
  43. <li>"+wn._("To restrict a User of a particular Role to documents that are only self-created.")+
  44. wn._("Click on button in the 'Condition' column and select the option 'User is the creator of the document'")+".</li>\
  45. <li>"+wn._("To restrict a User of a particular Role to documents that are explicitly assigned to them")+ ":"+
  46. + wn._("create a Custom Field of type Link (Profile) and then use the 'Condition' settings to map that field to the Permission rule.")+
  47. "</ol>\
  48. </tr></td>\
  49. <tr><td>\
  50. <h4><i class='icon-cog'></i> "+wn._("Advanced Settings")+":</h4>\
  51. <p>"+wn._("To further restrict permissions based on certain values in a document, use the 'Condition' settings.")+" <br><br>"+
  52. wn._("For example: You want to restrict users to transactions marked with a certain property called 'Territory'")+":</p>\
  53. <ol>\
  54. <li>"+wn._("Make sure that the transactions you want to restrict have a Link field 'territory' that maps to a 'Territory' master.")+" "
  55. +wn._("If not, create a")+
  56. "<a href='#List/Custom Field'>"+wn._("Custom Field")+"</a>"+ wn._("of type Link")+".</li>\
  57. <li>"+wn._("In the Permission Manager, click on the button in the 'Condition' column for the Role you want to restrict.")+"</li>\
  58. <li>"+wn._("A new popup will open that will ask you to select further conditions.")+
  59. wn._("If the 'territory' Link Field exists, it will give you an option to select it")+".</li>\
  60. <li>"+wn._("Go to Setup > <a href='#user-properties'>User Properties</a> to set \
  61. 'territory' for diffent Users.")+"</li>\
  62. </ol>\
  63. <p>"+wn._("Once you have set this, the users will only be able access documents with that property.")+"</p>\
  64. <hr>\
  65. <p>If these instructions where not helpful, please add in your suggestions at\
  66. <a href='https://github.com/webnotes/wnframework/issues'>GitHub Issues</a></p>\
  67. </tr></td>\
  68. </table>");
  69. wrapper.permission_engine = new wn.PermissionEngine(wrapper);
  70. }
  71. wn.pages['permission-manager'].refresh = function(wrapper) {
  72. wrapper.permission_engine.set_from_route();
  73. }
  74. wn.PermissionEngine = Class.extend({
  75. init: function(wrapper) {
  76. this.wrapper = wrapper;
  77. this.body = $(this.wrapper).find(".perm-engine");
  78. this.make();
  79. this.refresh();
  80. this.add_check_events();
  81. },
  82. make: function() {
  83. var me = this;
  84. me.make_reset_button();
  85. return wn.call({
  86. module:"core",
  87. page:"permission_manager",
  88. method: "get_roles_and_doctypes",
  89. callback: function(r) {
  90. me.options = r.message;
  91. me.doctype_select
  92. = me.wrapper.appframe.add_select("doctypes",
  93. [wn._("Select Document Type")+"..."].concat(r.message.doctypes))
  94. .change(function() {
  95. wn.set_route("permission-manager", $(this).val())
  96. });
  97. me.role_select
  98. = me.wrapper.appframe.add_select("roles",
  99. [wn._("Select Role")+"..."].concat(r.message.roles))
  100. .change(function() {
  101. me.refresh();
  102. });
  103. me.set_from_route();
  104. }
  105. });
  106. },
  107. set_from_route: function() {
  108. if(wn.get_route()[1] && this.doctype_select) {
  109. this.doctype_select.val(wn.get_route()[1]);
  110. this.refresh();
  111. } else {
  112. this.refresh();
  113. }
  114. },
  115. make_reset_button: function() {
  116. var me = this;
  117. me.reset_button = me.wrapper.appframe.add_button("Reset Permissions", function() {
  118. if(wn.confirm("Reset Permissions for " + me.get_doctype() + "?", function() {
  119. return wn.call({
  120. module:"core",
  121. page:"permission_manager",
  122. method:"reset",
  123. args: {
  124. doctype:me.get_doctype(),
  125. },
  126. callback: function() { me.refresh(); }
  127. });
  128. }));
  129. }, 'icon-retweet').toggle(false);
  130. },
  131. get_doctype: function() {
  132. var doctype = this.doctype_select.val();
  133. return this.doctype_select.get(0).selectedIndex==0 ? null : doctype;
  134. },
  135. get_role: function() {
  136. var role = this.role_select.val();
  137. return this.role_select.get(0).selectedIndex==0 ? null : role;
  138. },
  139. refresh: function() {
  140. var me = this;
  141. if(!me.doctype_select) {
  142. this.body.html("<div class='alert alert-info'>Loading...</div>");
  143. return;
  144. }
  145. if(!me.get_doctype() && !me.get_role()) {
  146. this.body.html("<div class='alert alert-info'>"+wn._("Select Document Type or Role to start.")+"</div>");
  147. return;
  148. }
  149. // get permissions
  150. wn.call({
  151. module: "core",
  152. page: "permission_manager",
  153. method: "get_permissions",
  154. args: {
  155. doctype: me.get_doctype(),
  156. role: me.get_role()
  157. },
  158. callback: function(r) {
  159. me.render(r.message);
  160. }
  161. });
  162. me.reset_button.toggle(me.get_doctype() ? true : false);
  163. },
  164. render: function(perm_list) {
  165. this.body.empty();
  166. this.perm_list = perm_list;
  167. if(!perm_list.length) {
  168. this.body.html("<div class='alert alert-warning'>"+wn._("No Permissions set for this criteria.")+"</div>");
  169. } else {
  170. this.show_permission_table(perm_list);
  171. }
  172. this.show_add_rule();
  173. },
  174. show_permission_table: function(perm_list) {
  175. var me = this;
  176. this.table = $("<table class='table table-bordered'>\
  177. <thead><tr></tr></thead>\
  178. <tbody></tbody>\
  179. </table>").appendTo(this.body);
  180. $.each([["Document Type", 150], ["Role", 100], ["Level",50],
  181. ["Read", 50], ["Edit", 50], ["Make New", 50],
  182. ["Submit", 50], ["Cancel", 50], ["Amend", 50], ["Report", 50],
  183. ["Condition", 150], ["", 50]], function(i, col) {
  184. $("<th>").html(col[0]).css("width", col[1]+"px")
  185. .appendTo(me.table.find("thead tr"));
  186. });
  187. var add_cell = function(row, d, fieldname, is_check) {
  188. var cell = $("<td>").appendTo(row).attr("data-fieldname", fieldname);
  189. if(is_check) {
  190. if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) {
  191. cell.html("-");
  192. } else {
  193. var input = $("<input type='checkbox'>")
  194. .prop("checked", d[fieldname] ? true: false)
  195. .attr("data-ptype", fieldname)
  196. .attr("data-name", d.name)
  197. .attr("data-doctype", d.parent)
  198. .appendTo(cell);
  199. }
  200. } else {
  201. cell.html(d[fieldname]);
  202. }
  203. return cell;
  204. }
  205. $.each(perm_list, function(i, d) {
  206. if(!d.permlevel) d.permlevel = 0;
  207. var row = $("<tr>").appendTo(me.table.find("tbody"));
  208. add_cell(row, d, "parent");
  209. me.set_show_users(add_cell(row, d, "role"), d.role);
  210. var cell = add_cell(row, d, "permlevel");
  211. if(d.permlevel==0) {
  212. cell.css("font-weight", "bold");
  213. row.addClass("warning");
  214. }
  215. add_cell(row, d, "read", true);
  216. add_cell(row, d, "write", true);
  217. add_cell(row, d, "create", true);
  218. add_cell(row, d, "submit", true);
  219. add_cell(row, d, "cancel", true);
  220. add_cell(row, d, "amend", true);
  221. add_cell(row, d, "report", true);
  222. // buttons
  223. me.add_match_button(row, d);
  224. me.add_delete_button(row, d);
  225. });
  226. },
  227. set_show_users: function(cell, role) {
  228. cell.html("<a href='#'>"+role+"</a>")
  229. .find("a")
  230. .attr("data-role", role)
  231. .click(function() {
  232. var role = $(this).attr("data-role");
  233. wn.call({
  234. module: "core",
  235. page: "permission_manager",
  236. method: "get_users_with_role",
  237. args: {
  238. role: role
  239. },
  240. callback: function(r) {
  241. r.message = $.map(r.message, function(p) {
  242. return '<a href="#Form/Profile/'+p+'">'+p+'</a>';
  243. })
  244. msgprint("<h4>Users with role "+role+":</h4>"
  245. + r.message.join("<br>"));
  246. }
  247. })
  248. return false;
  249. })
  250. },
  251. add_match_button: function(row, d) {
  252. var me = this;
  253. if(d.permlevel > 0) {
  254. $("<td>-</td>").appendTo(row);
  255. return;
  256. }
  257. var btn = $("<button class='btn btn-default btn-small'></button>")
  258. .html(d.match ? d.match : wn._("For All Users"))
  259. .appendTo($("<td>").appendTo(row))
  260. .attr("data-name", d.name)
  261. .click(function() {
  262. me.show_match_manager($(this).attr("data-name"));
  263. });
  264. if(d.match) btn.addClass("btn-info");
  265. },
  266. add_delete_button: function(row, d) {
  267. var me = this;
  268. $("<button class='btn btn-default btn-small'><i class='icon-remove'></i></button>")
  269. .appendTo($("<td>").appendTo(row))
  270. .attr("data-name", d.name)
  271. .attr("data-doctype", d.parent)
  272. .click(function() {
  273. return wn.call({
  274. module: "core",
  275. page: "permission_manager",
  276. method: "remove",
  277. args: {
  278. name: $(this).attr("data-name"),
  279. doctype: $(this).attr("data-doctype")
  280. },
  281. callback: function(r) {
  282. if(r.exc) {
  283. msgprint("Did not remove.");
  284. } else {
  285. me.refresh();
  286. }
  287. }
  288. })
  289. });
  290. },
  291. add_check_events: function() {
  292. var me = this;
  293. this.body.on("click", "input[type='checkbox']", function() {
  294. var chk = $(this);
  295. var args = {
  296. name: chk.attr("data-name"),
  297. doctype: chk.attr("data-doctype"),
  298. ptype: chk.attr("data-ptype"),
  299. value: chk.prop("checked") ? 1 : 0
  300. }
  301. return wn.call({
  302. module: "core",
  303. page: "permission_manager",
  304. method: "update",
  305. args: args,
  306. callback: function(r) {
  307. if(r.exc) {
  308. // exception: reverse
  309. chk.prop("checked", !chk.prop("checked"));
  310. } else {
  311. me.get_perm(args.name)[args.ptype]=args.value;
  312. }
  313. }
  314. })
  315. })
  316. },
  317. show_add_rule: function() {
  318. var me = this;
  319. $("<button class='btn btn-default btn-info'>"+wn._("Add A New Rule")+"</button>")
  320. .appendTo($("<p>").appendTo(this.body))
  321. .click(function() {
  322. var d = new wn.ui.Dialog({
  323. title: wn._("Add New Permission Rule"),
  324. fields: [
  325. {fieldtype:"Select", label:"Document Type",
  326. options:me.options.doctypes, reqd:1, fieldname:"parent"},
  327. {fieldtype:"Select", label:"Role",
  328. options:me.options.roles, reqd:1},
  329. {fieldtype:"Select", label:"Permission Level",
  330. options:[0,1,2,3,4,5,6,7,8,9], reqd:1, fieldname: "permlevel",
  331. description: wn._("Level 0 is for document level permissions, higher levels for field level permissions.")},
  332. {fieldtype:"Button", label:"Add"},
  333. ]
  334. });
  335. if(me.get_doctype()) {
  336. d.set_value("parent", me.get_doctype());
  337. d.get_input("parent").prop("disabled", true);
  338. }
  339. if(me.get_role()) {
  340. d.set_value("role", me.get_role());
  341. d.get_input("role").prop("disabled", true);
  342. }
  343. d.set_value("permlevel", "0");
  344. d.get_input("add").click(function() {
  345. var args = d.get_values();
  346. if(!args) {
  347. return;
  348. }
  349. wn.call({
  350. module: "core",
  351. page: "permission_manager",
  352. method: "add",
  353. args: args,
  354. callback: function(r) {
  355. if(r.exc) {
  356. msgprint(wn._("Did not add."));
  357. } else {
  358. me.refresh();
  359. }
  360. }
  361. })
  362. d.hide();
  363. });
  364. d.show();
  365. });
  366. },
  367. get_perm: function(name) {
  368. return $.map(this.perm_list, function(d) { if(d.name==name) return d; })[0];
  369. },
  370. show_match_manager:function(name) {
  371. var perm = this.get_perm(name);
  372. var me = this;
  373. wn.model.with_doctype(perm.parent, function() {
  374. var dialog = new wn.ui.Dialog({
  375. title: "Applies for Users",
  376. });
  377. $(dialog.body).html("<h4>Rule Applies to Following Users:</h4>\
  378. <label class='radio'>\
  379. <input name='perm-rule' type='radio' value=''> All users with role <b>"+perm.role+"</b>.\
  380. </label>\
  381. <label class='radio'>\
  382. <input name='perm-rule' type='radio' value='owner'> The user is the creator of the document.\
  383. </label>").css("padding", "15px");
  384. // profile fields
  385. $.each(me.get_profile_fields(perm.parent), function(i, d) {
  386. $("<label class='radio'>\
  387. <input name='perm-rule' type='radio' value='"+d.fieldname
  388. +":user'>Value of field <b>"+d.label+"</b> is the User.\
  389. </label>").appendTo(dialog.body);
  390. });
  391. // add options for all link fields
  392. $.each(me.get_link_fields(perm.parent), function(i, d) {
  393. $("<label class='radio'>\
  394. <input name='perm-rule' type='radio' value='"+d.fieldname
  395. +"'><b>"+d.label+"</b> in <b>"+d.parent+"</b> matches <a href='#user-properties//"+d.fieldname+"'>User Property</a> <b>"
  396. +d.fieldname+"</b>.\
  397. </label>").appendTo(dialog.body);
  398. });
  399. // button
  400. $("<button class='btn btn-default btn-info'>Update</button>")
  401. .appendTo($("<p>").appendTo(dialog.body))
  402. .attr("data-name", perm.name)
  403. .click(function() {
  404. var match_value = $(dialog.wrapper).find(":radio:checked").val();
  405. var perm = me.get_perm($(this).attr('data-name'))
  406. return wn.call({
  407. module: "core",
  408. page: "permission_manager",
  409. method: "update_match",
  410. args: {
  411. name: perm.name,
  412. doctype: perm.parent,
  413. match: match_value
  414. },
  415. callback: function() {
  416. dialog.hide();
  417. me.refresh();
  418. }
  419. })
  420. });
  421. dialog.show();
  422. // select
  423. if(perm.match) {
  424. $(dialog.wrapper).find("[value='"+perm.match+"']").prop("checked", true).focus();
  425. } else {
  426. $(dialog.wrapper).find('[value=""]').prop("checked", true).focus();
  427. }
  428. });
  429. },
  430. get_profile_fields: function(doctype) {
  431. var profile_fields = wn.model.get("DocField", {parent:doctype,
  432. fieldtype:"Link", options:"Profile"});
  433. profile_fields = profile_fields.concat(wn.model.get("DocField", {parent:doctype,
  434. fieldtype:"Select", link_doctype:"Profile"}))
  435. return profile_fields
  436. },
  437. get_link_fields: function(doctype) {
  438. return link_fields = wn.model.get("DocField", {parent:doctype,
  439. fieldtype:"Link", options:["not in", ["Profile", '[Select]']]});
  440. }
  441. })