diff --git a/frappe/core/page/background_jobs/background_jobs.css b/frappe/core/page/background_jobs/background_jobs.css index 63c0ce7202..0c77522cb3 100644 --- a/frappe/core/page/background_jobs/background_jobs.css +++ b/frappe/core/page/background_jobs/background_jobs.css @@ -54,9 +54,7 @@ thead > tr > th:last-child { } .footer { - display: flex; - align-items: center; - justify-content: space-between; + align-items: flex-end; margin-top: var(--margin-md); font-size: var(--text-base); } diff --git a/frappe/core/page/background_jobs/background_jobs.html b/frappe/core/page/background_jobs/background_jobs.html index 7668661444..1b00ec3106 100644 --- a/frappe/core/page/background_jobs/background_jobs.html +++ b/frappe/core/page/background_jobs/background_jobs.html @@ -38,13 +38,14 @@

{{ __("No pending or current jobs for this site") }}

{% endif %} - \ No newline at end of file diff --git a/frappe/core/page/permission_manager/permission_manager.css b/frappe/core/page/permission_manager/permission_manager.css new file mode 100644 index 0000000000..3c74859c75 --- /dev/null +++ b/frappe/core/page/permission_manager/permission_manager.css @@ -0,0 +1,50 @@ +.table { + margin-bottom: 0px; + margin-top: 0px; + border-radius: var(--border-radius-md); +} + +thead { + border: none; + background-color: var(--control-bg); + border-radius: var(--border-radius-md); +} + +thead > tr { + border-radius: var(--border-radius-md); +} + +thead > tr > th:first-child { + border-radius: var(--border-radius-md) 0 0 var(--border-radius-md); +} +thead > tr > th:last-child { + border-radius: 0 var(--border-radius-md) var(--border-radius-md) 0; +} + +/* Space between thead and tbody */ +/* tbody:before { + content: "@"; + display: block; + line-height: var(--margin-md); + text-indent: -99999px; +} */ + +td[data-fieldname="permissions"] > .row > .col-md-4 { + margin-bottom: var(--margin-sm); +} + +tbody > tr { + border-top: 1px solid var(--border-color); +} + +tbody > tr:first-child { + border-top: none; +} + +button.btn-remove-perm { + box-shadow: none; +} + +button.btn-remove-perm > svg > use { + stroke: var(--white); +} diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 0d3267c7d5..c4957f233c 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -1,8 +1,8 @@ frappe.pages['permission-manager'].on_page_load = (wrapper) => { - var page = frappe.ui.make_app_page({ + let page = frappe.ui.make_app_page({ parent: wrapper, title: __('Role Permissions Manager'), - icon: "fa fa-lock", + card_layout: true, single_column: true }); @@ -14,233 +14,255 @@ frappe.pages['permission-manager'].on_page_load = (wrapper) => { }; -frappe.pages['permission-manager'].refresh = function(wrapper) { +frappe.pages['permission-manager'].refresh = function (wrapper) { wrapper.permission_engine.set_from_route(); }; -frappe.PermissionEngine = Class.extend({ - init: function(wrapper) { +frappe.PermissionEngine = class PermissionEngine { + constructor(wrapper) { this.wrapper = wrapper; this.page = wrapper.page; 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 frappe.call({ - module:"frappe.core", - page:"permission_manager", - method: "get_roles_and_doctypes", - callback: function(r) { - me.options = r.message; - me.setup_page(); - } + make() { + this.make_reset_button(); + frappe.call({ + module: "frappe.core", + page: "permission_manager", + method: "get_roles_and_doctypes" + }).then((res) => { + this.options = res.message; + this.setup_page(); }); + } - }, - setup_page: function() { - var me = this; + setup_page() { this.doctype_select = this.wrapper.page.add_select(__("Document Type"), - [{value: "", label: __("Select Document Type")+"..."}].concat(this.options.doctypes)) - .change(function() { + [{ value: "", label: __("Select Document Type") + "..." }].concat(this.options.doctypes)) + .change(function () { frappe.set_route("permission-manager", $(this).val()); }); + this.role_select = this.wrapper.page.add_select(__("Roles"), - [__("Select Role")+"..."].concat(this.options.roles)) - .change(function() { - me.refresh(); + [__("Select Role") + "..."].concat(this.options.roles)) + .change(() => { + this.refresh(); }); this.page.add_inner_button(__('Set User Permissions'), () => { return frappe.set_route('List', 'User Permission'); }); this.set_from_route(); - }, - set_from_route: function() { - var me = this; - if(!this.doctype_select) { + } + + set_from_route() { + if (!this.doctype_select) { // selects not yet loaded, call again after a bit setTimeout(() => { - me.set_from_route(); + this.set_from_route(); }, 500); return; } - if(frappe.get_route()[1]) { + if (frappe.get_route()[1]) { this.doctype_select.val(frappe.get_route()[1]); - } else if(frappe.route_options) { - if(frappe.route_options.doctype) { + } else if (frappe.route_options) { + if (frappe.route_options.doctype) { this.doctype_select.val(frappe.route_options.doctype); } - if(frappe.route_options.role) { + if (frappe.route_options.role) { this.role_select.val(frappe.route_options.role); } frappe.route_options = null; } this.refresh(); - }, - get_standard_permissions: function(callback) { - var doctype = this.get_doctype(); - if(doctype) { + } + + get_standard_permissions(callback) { + let doctype = this.get_doctype(); + if (doctype) { return frappe.call({ - module:"frappe.core", - page:"permission_manager", + module: "frappe.core", + page: "permission_manager", method: "get_standard_permissions", - args: {doctype: doctype}, + args: { doctype: doctype }, callback: callback }); } return false; - }, - reset_std_permissions: function(data) { - var me = this; - var d = frappe.confirm(__("Reset Permissions for {0}?", [me.get_doctype()]), function() { + } + + reset_std_permissions(data) { + let doctype = this.get_doctype() + let d = frappe.confirm(__("Reset Permissions for {0}?", [doctype]), () => { return frappe.call({ - module:"frappe.core", - page:"permission_manager", - method:"reset", - args: { - doctype: me.get_doctype(), - }, - callback: function() { - me.refresh(); - } + module: "frappe.core", + page: "permission_manager", + method: "reset", + args: { doctype } + }).then(() => { + this.refresh(); }); }); // show standard permissions - var $d = $(d.wrapper).find(".frappe-confirm-message").append("

Standard Permissions:


"); - var $wrapper = $("

").appendTo($d); - $.each(data.message, function(i, d) { - d.rights = []; - $.each(me.rights, function(i, r) { - if(d[r]===1) { - d.rights.push(__(toTitle(r.replace("_", " ")))); - } - }); - d.rights = d.rights.join(", "); - $wrapper.append(repl('
\ -
%(role)s, Level %(permlevel)s
\ -
%(rights)s
\ -

', d)); - }); + let $d = $(d.wrapper).find(".frappe-confirm-message").append("
Standard Permissions:

"); + let $wrapper = $("

").appendTo($d); + data.message.forEach((d) => { + let rights = this.rights + .filter((r) => d[r]) + .map((r) => { + return __(toTitle(frappe.unscrub(r))) + }); - }, - 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("

"+__("Select Document Type or Role to start.")+"

"); + d.rights = rights.join(", "); + + $wrapper.append(`
\ +
${d.role}, Level ${d.permlevel || 0}
\ +
${d.rights}
\ +

`); + }); + } + + get_doctype() { + let doctype = this.doctype_select.val(); + return this.doctype_select.get(0).selectedIndex == 0 ? null : doctype; + } + + get_role() { + let role = this.role_select.val(); + return this.role_select.get(0).selectedIndex == 0 ? null : role; + } + + set_empty_message(message) { + this.body.html(` +
+

+ ${message} +

+
`); + } + + refresh() { + this.page.clear_secondary_action(); + this.page.clear_primary_action(); + + if (!this.doctype_select) { + this.set_empty_message(__("Loading")) + return + } + + let doctype = this.get_doctype(); + let role = this.get_role(); + + if (!doctype && !role) { + this.set_empty_message(__("Select Document Type or Role to start.")) return; } + // get permissions frappe.call({ module: "frappe.core", page: "permission_manager", method: "get_permissions", - args: { - doctype: me.get_doctype(), - role: me.get_role() - }, - callback: function(r) { - me.render(r.message); - } + args: { doctype, role } + }).then((r) => { + this.render(r.message); }); - }, - render: function(perm_list) { + } + + render(perm_list) { this.body.empty(); this.perm_list = perm_list || []; - if(!this.perm_list.length) { - this.body.html("

" - +__("No Permissions set for this criteria.")+"

"); + if (!this.perm_list.length) { + this.set_empty_message(__("No Permissions set for this criteria.")); } else { this.show_permission_table(this.perm_list); } this.show_add_rule(); - this.make_reset_button(); - }, - show_permission_table: function(perm_list) { + this.get_doctype() && this.make_reset_button(); + } - var me = this; + show_permission_table(perm_list) { this.table = $("
\ - \ +
\ \ \
\
").appendTo(this.body); - $.each([[__("Document Type"), 150], [__("Role"), 170], [__("Level"), 40], - [__("Permissions"), 350], ["", 40]], function(i, col) { - $("").html(col[0]).css("width", col[1]+"px") - .appendTo(me.table.find("thead tr")); + const table_columns = [ + [__("Document Type"), 150], + [__("Role"), 170], + [__("Level"), 40], + [__("Permissions"), 350], + ["", 40] + ] + + table_columns.forEach((col) => { + $("") + .html(col[0]) + .css("width", col[1] + "px") + .appendTo(this.table.find("thead tr")); }); - $.each(perm_list, function(i, d) { - if(d.parent==="DocType") { + perm_list.forEach((d) => { + if (d.parent === "DocType") { return; } - if(!d.permlevel) d.permlevel = 0; - var row = $("").appendTo(me.table.find("tbody")); - me.add_cell(row, d, "parent"); - var role_cell = me.add_cell(row, d, "role"); - me.set_show_users(role_cell, d.role); - if (d.permlevel===0) { - // me.setup_user_permissions(d, role_cell); - me.setup_if_owner(d, role_cell); + if (!d.permlevel) d.permlevel = 0; + + let row = $("").appendTo(this.table.find("tbody")); + this.add_cell(row, d, "parent"); + let role_cell = this.add_cell(row, d, "role"); + + this.set_show_users(role_cell, d.role); + + if (d.permlevel === 0) { + // this.setup_user_permissions(d, role_cell); + this.setup_if_owner(d, role_cell); } - var cell = me.add_cell(row, d, "permlevel"); - if(d.permlevel==0) { + let cell = this.add_cell(row, d, "permlevel"); + + if (d.permlevel == 0) { cell.css("font-weight", "bold"); - row.addClass("warning"); } - var perm_cell = me.add_cell(row, d, "permissions").css("padding-top", 0); - var perm_container = $("
").appendTo(perm_cell); + let perm_cell = this.add_cell(row, d, "permissions"); + let perm_container = $("
").appendTo(perm_cell); - me.rights.forEach(r => { + this.rights.forEach(r => { if (!d.is_submittable && ['submit', 'cancel', 'amend'].includes(r)) return; if (d.in_create && ['create', 'write', 'delete'].includes(r)) return; - me.add_check(perm_container, d, r); + this.add_check(perm_container, d, r); }); // buttons - me.add_delete_button(row, d); + this.add_delete_button(row, d); }); - }, + } - add_cell: function(row, d, fieldname) { + add_cell(row, d, fieldname) { return $("").appendTo(row) .attr("data-fieldname", fieldname) + .addClass("pt-4") .html(__(d[fieldname])); - }, + } - add_check: (cell, d, fieldname, label, description="") => { - var me = this; - - if(!label) label = toTitle(fieldname.replace(/_/g, " ")); - if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) { + add_check(cell, d, fieldname, label, description = "") { + if (!label) label = toTitle(fieldname.replace(/_/g, " ")); + if (d.permlevel > 0 && ["read", "write"].indexOf(fieldname) == -1) { return; } - var checkbox = $( + let checkbox = $( `
@@ -251,7 +273,7 @@ frappe.PermissionEngine = Class.extend({ .attr("data-fieldname", fieldname); checkbox.find("input") - .prop("checked", d[fieldname] ? true: false) + .prop("checked", d[fieldname] ? true : false) .attr("data-ptype", fieldname) .attr("data-role", d.role) .attr("data-permlevel", d.permlevel) @@ -261,23 +283,25 @@ frappe.PermissionEngine = Class.extend({ .css("text-transform", "capitalize"); return checkbox; - }, + } - setup_if_owner: function(d, role_cell) { + setup_if_owner(d, role_cell) { this.add_check(role_cell, d, "if_owner", "Only If Creator") .removeClass("col-md-4") - .css({"margin-top": "15px"}); - }, + .css({ "margin-top": "15px" }); + } - rights: ["read", "write", "create", "delete", "submit", "cancel", "amend", - "print", "email", "report", "import", "export", "set_user_permissions", "share"], + get rights() { + return ["read", "write", "create", "delete", "submit", "cancel", "amend", + "print", "email", "report", "import", "export", "set_user_permissions", "share"] + } - set_show_users: function(cell, role) { - cell.html(""+__(role)+"") + set_show_users(cell, role) { + cell.html("" + __(role) + "") .find("a") .attr("data-role", role) - .click(function() { - var role = $(this).attr("data-role"); + .click(function () { + let role = $(this).attr("data-role"); frappe.call({ module: "frappe.core", page: "permission_manager", @@ -285,8 +309,8 @@ frappe.PermissionEngine = Class.extend({ args: { role: role }, - callback: function(r) { - r.message = $.map(r.message, function(p) { + callback: function (r) { + r.message = $.map(r.message, function (p) { return $.format('{1}', [p, p]); }); frappe.msgprint(__("Users with role {0}:", [__(role)]) @@ -295,16 +319,15 @@ frappe.PermissionEngine = Class.extend({ }); return false; }); - }, + } - add_delete_button: function(row, d) { - var me = this; - $("") - .appendTo($("").appendTo(row)) + add_delete_button(row, d) { + $(``) + .appendTo($(``).appendTo(row)) .attr("data-doctype", d.parent) .attr("data-role", d.role) .attr("data-permlevel", d.permlevel) - .click(function() { + .click(function () { return frappe.call({ module: "frappe.core", page: "permission_manager", @@ -314,29 +337,27 @@ frappe.PermissionEngine = Class.extend({ role: $(this).attr("data-role"), permlevel: $(this).attr("data-permlevel") }, - callback: function(r) { - if(r.exc) { + callback: (r) => { + if (r.exc) { frappe.msgprint(__("Did not remove")); } else { - me.refresh(); + this.refresh(); } } }); }); - }, + } - add_check_events: function() { - var me = this; - - this.body.on("click", ".show-user-permissions", function() { - frappe.route_options = { allow: me.get_doctype() || "" }; + add_check_events() { + this.body.on("click", ".show-user-permissions", () => { + frappe.route_options = { allow: this.get_doctype() || "" }; frappe.set_route('List', 'User Permission'); }); - this.body.on("click", "input[type='checkbox']", function() { + this.body.on("click", "input[type='checkbox']", function () { frappe.dom.freeze(); - var chk = $(this); - var args = { + let chk = $(this); + let args = { role: chk.attr("data-role"), permlevel: chk.attr("data-permlevel"), doctype: chk.attr("data-doctype"), @@ -348,49 +369,53 @@ frappe.PermissionEngine = Class.extend({ page: "permission_manager", method: "update", args: args, - callback: function(r) { + callback: (r) => { frappe.dom.unfreeze(); - if(r.exc) { + if (r.exc) { // exception: reverse chk.prop("checked", !chk.prop("checked")); } else { - me.get_perm(args.role)[args.ptype]=args.value; + this.get_perm(args.role)[args.ptype] = args.value; } } }); }); - }, + } - show_add_rule: function() { - var me = this; - $("") - .appendTo($("

").appendTo(this.body)) - .click(function() { - var d = new frappe.ui.Dialog({ + show_add_rule() { + this.page.set_primary_action( + __("Add A New Rule"), + () => { + let d = new frappe.ui.Dialog({ title: __("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,fieldname:"role"}, - {fieldtype:"Select", label:__("Permission Level"), - options:[0,1,2,3,4,5,6,7,8,9], reqd:1, fieldname: "permlevel", - description: __("Level 0 is for document level permissions, higher levels for field level permissions.")} + { + fieldtype: "Select", label: __("Document Type"), + options: this.options.doctypes, reqd: 1, fieldname: "parent" + }, + { + fieldtype: "Select", label: __("Role"), + options: this.options.roles, reqd: 1, fieldname: "role" + }, + { + fieldtype: "Select", label: __("Permission Level"), + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], reqd: 1, fieldname: "permlevel", + description: __("Level 0 is for document level permissions, higher levels for field level permissions.") + } ] }); - if(me.get_doctype()) { - d.set_value("parent", me.get_doctype()); + if (this.get_doctype()) { + d.set_value("parent", this.get_doctype()); d.get_input("parent").prop("disabled", true); } - if(me.get_role()) { - d.set_value("role", me.get_role()); + if (this.get_role()) { + d.set_value("role", this.get_role()); d.get_input("role").prop("disabled", true); } d.set_value("permlevel", "0"); - d.set_primary_action(__('Add'), function() { - var args = d.get_values(); - if(!args) { + d.set_primary_action(__('Add'), () => { + let args = d.get_values(); + if (!args) { return; } frappe.call({ @@ -398,40 +423,40 @@ frappe.PermissionEngine = Class.extend({ page: "permission_manager", method: "add", args: args, - callback: function(r) { - if(r.exc) { + callback: (r) => { + if (r.exc) { frappe.msgprint(__("Did not add")); } else { - me.refresh(); + this.refresh(); } } }); d.hide(); }); d.show(); - }); - }, + }, + "small-add" + ) + } - make_reset_button: function() { - var me = this; - $('') - .appendTo(this.body.find(".permission-toolbar")) - .on("click", function() { - me.get_standard_permissions(function(data) { - me.reset_std_permissions(data); + make_reset_button() { + this.page.set_secondary_action( + __("Restore Original Permissions"), + () => { + this.get_standard_permissions((data) => { + this.reset_std_permissions(data); }); }); - }, - - get_perm: function(role) { - return $.map(this.perm_list, function(d) { - if(d.role==role) return d; - })[0]; - }, - - get_link_fields: function(doctype) { - return frappe.get_children("DocType", doctype, "fields", - {fieldtype:"Link", options:["not in", ["User", '[Select]']]}); } -}); + + get_perm(role) { + return $.map(this.perm_list, function (d) { + if (d.role == role) return d; + })[0]; + } + + get_link_fields(doctype) { + return frappe.get_children("DocType", doctype, "fields", + { fieldtype: "Link", options: ["not in", ["User", '[Select]']] }); + } +} diff --git a/frappe/core/page/permission_manager/permission_manager_help.html b/frappe/core/page/permission_manager/permission_manager_help.html index f192244fc9..30ccbed92f 100644 --- a/frappe/core/page/permission_manager/permission_manager_help.html +++ b/frappe/core/page/permission_manager/permission_manager_help.html @@ -1,5 +1,5 @@


-
+

{%= __("Quick Help for Setting Permissions") %}:

  1. {%= __("Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.") %}
  2. diff --git a/frappe/patches.txt b/frappe/patches.txt index 3cd566a200..f65a896d23 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -15,6 +15,7 @@ execute:frappe.reload_doc('core', 'doctype', 'custom_docperm') execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29 execute:frappe.reload_doc('core', 'doctype', 'comment') frappe.patches.v8_0.drop_is_custom_from_docperm +execute:frappe.reload_doc('core', 'doctype', 'document_naming_rule', force=True) execute:frappe.reload_doc('core', 'doctype', 'module_def') #2020-08-28 execute:frappe.reload_doc('core', 'doctype', 'version') #2017-04-01 execute:frappe.reload_doc('email', 'doctype', 'document_follow') diff --git a/frappe/public/scss/desk/timeline.scss b/frappe/public/scss/desk/timeline.scss index 970451333f..9d173b5bed 100644 --- a/frappe/public/scss/desk/timeline.scss +++ b/frappe/public/scss/desk/timeline.scss @@ -9,8 +9,10 @@ $threshold: 34; border-radius: 50%; font-size: var(--text-xs); position: absolute; - // please do not touch this sacred code - top: unquote("clamp(0px, 50% - #{$badge-size}/2, max(0px, (#{$threshold}px - (50% - #{$badge-size}/2)) * #{$threshold}))"); + // doing this will prevent cssnano from converting 0px to 0 + // clamp requires value unit to be valid + --zero-px: 0px; + top: unquote("clamp(var(--zero-px), 50% - #{$badge-size}/2, max(var(--zero-px), (#{$threshold}px - (50% - #{$badge-size}/2)) * #{$threshold}))"); left: calc(-1 * (#{$badge-size}/2)); background-color: var(--bg-color); border: 1px solid var(--dark-border-color);