25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

457 lines
14 KiB

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