Merge pull request #795 from anandpdoshi/user-permission-doctypes
Select Document Types for applying User Permissions
This commit is contained in:
commit
d4891aa7c1
17 changed files with 338 additions and 96 deletions
|
|
@ -20,7 +20,7 @@ def get_data():
|
|||
{
|
||||
"type": "page",
|
||||
"name": "permission-manager",
|
||||
"label": "Role Permissions Manager",
|
||||
"label": _("Role Permissions Manager"),
|
||||
"icon": "icon-lock",
|
||||
"description": _("Set Permissions on Document Types and Roles")
|
||||
},
|
||||
|
|
|
|||
|
|
@ -53,6 +53,15 @@
|
|||
"search_index": 0,
|
||||
"width": "40px"
|
||||
},
|
||||
{
|
||||
"depends_on": "",
|
||||
"description": "JSON list of DocTypes used to apply User Permissions. If empty, all linked DocTypes will be used to apply User Permissions.",
|
||||
"fieldname": "user_permission_doctypes",
|
||||
"fieldtype": "Text",
|
||||
"label": "User Permission DocTypes",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
|
|
@ -207,7 +216,7 @@
|
|||
"idx": 1,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2014-05-26 03:46:53.737397",
|
||||
"modified": "2014-08-26 01:43:31.499363",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocPerm",
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ frappe.PermissionEngine = Class.extend({
|
|||
setup_appframe: function() {
|
||||
var me = this;
|
||||
this.doctype_select
|
||||
= this.wrapper.appframe.add_select("doctypes",
|
||||
= this.wrapper.appframe.add_select(__("Document Types"),
|
||||
[{value: "", label: __("Select Document Type")+"..."}].concat(this.options.doctypes))
|
||||
.change(function() {
|
||||
frappe.set_route("permission-manager", $(this).val());
|
||||
});
|
||||
this.role_select
|
||||
= this.wrapper.appframe.add_select("roles",
|
||||
= this.wrapper.appframe.add_select(__("Roles"),
|
||||
[__("Select Role")+"..."].concat(this.options.roles))
|
||||
.change(function() {
|
||||
me.refresh();
|
||||
|
|
@ -95,7 +95,7 @@ frappe.PermissionEngine = Class.extend({
|
|||
page:"permission_manager",
|
||||
method:"reset",
|
||||
args: {
|
||||
doctype:me.get_doctype(),
|
||||
doctype: me.get_doctype(),
|
||||
},
|
||||
callback: function() { me.refresh(); }
|
||||
});
|
||||
|
|
@ -130,7 +130,7 @@ frappe.PermissionEngine = Class.extend({
|
|||
refresh: function() {
|
||||
var me = this;
|
||||
if(!me.doctype_select) {
|
||||
this.body.html("<div class='alert alert-info'>Loading...</div>");
|
||||
this.body.html("<div class='alert alert-info'>" + __("Loading") + "...</div>");
|
||||
return;
|
||||
}
|
||||
if(!me.get_doctype() && !me.get_role()) {
|
||||
|
|
@ -164,6 +164,7 @@ frappe.PermissionEngine = Class.extend({
|
|||
this.make_reset_button();
|
||||
},
|
||||
show_permission_table: function(perm_list) {
|
||||
|
||||
var me = this;
|
||||
this.table = $("<div class='table-responsive'>\
|
||||
<table class='table table-bordered'>\
|
||||
|
|
@ -172,77 +173,104 @@ frappe.PermissionEngine = Class.extend({
|
|||
</table>\
|
||||
</div>").appendTo(this.body);
|
||||
|
||||
$.each([["Document Type", 150], ["Role", 170], ["Level", 40],
|
||||
["Permissions", 350], ["", 40]], function(i, col) {
|
||||
$.each([[__("Document Type"), 150], [__("Role"), 170], [__("Level"), 40],
|
||||
[__("Permissions"), 350], ["", 40]], function(i, col) {
|
||||
$("<th>").html(col[0]).css("width", col[1]+"px")
|
||||
.appendTo(me.table.find("thead tr"));
|
||||
});
|
||||
|
||||
var add_cell = function(row, d, fieldname) {
|
||||
return $("<td>").appendTo(row)
|
||||
.attr("data-fieldname", fieldname)
|
||||
.html(d[fieldname]);
|
||||
};
|
||||
|
||||
var add_check = function(cell, d, fieldname, label, without_grid) {
|
||||
if(!label) label = fieldname.replace(/_/g, " ");
|
||||
if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var checkbox = $("<div class='col-md-4'><div class='checkbox'>\
|
||||
<label><input type='checkbox'>"+__(label)+"</input></label>"
|
||||
+ (d.help || "") + "</div></div>").appendTo(cell)
|
||||
.attr("data-fieldname", fieldname)
|
||||
.css("text-transform", "capitalize");
|
||||
|
||||
checkbox.find("input")
|
||||
.prop("checked", d[fieldname] ? true: false)
|
||||
.attr("data-ptype", fieldname)
|
||||
.attr("data-name", d.name)
|
||||
.attr("data-doctype", d.parent)
|
||||
|
||||
return checkbox;
|
||||
};
|
||||
|
||||
$.each(perm_list, function(i, d) {
|
||||
if(!d.permlevel) d.permlevel = 0;
|
||||
var row = $("<tr>").appendTo(me.table.find("tbody"));
|
||||
add_cell(row, d, "parent");
|
||||
var role_cell = add_cell(row, d, "role");
|
||||
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) {
|
||||
d.help = '<div style="margin-left: 20px;">\
|
||||
<a class="show-user-permissions small">Show User Pemissions</a></div>';
|
||||
add_check(role_cell, d, "apply_user_permissions")
|
||||
.removeClass("col-md-4")
|
||||
.css({"margin-top": "15px"});
|
||||
d.help = "";
|
||||
me.setup_user_permissions(d, role_cell);
|
||||
}
|
||||
|
||||
var cell = add_cell(row, d, "permlevel");
|
||||
var cell = me.add_cell(row, d, "permlevel");
|
||||
if(d.permlevel==0) {
|
||||
cell.css("font-weight", "bold");
|
||||
row.addClass("warning");
|
||||
}
|
||||
|
||||
var perm_cell = add_cell(row, d, "permissions").css("padding-top", 0);
|
||||
var perm_cell = me.add_cell(row, d, "permissions").css("padding-top", 0);
|
||||
var perm_container = $("<div class='row'></div>").appendTo(perm_cell);
|
||||
|
||||
$.each(me.rights, function(i, r) {
|
||||
add_check(perm_container, d, r);
|
||||
me.add_check(perm_container, d, r);
|
||||
});
|
||||
|
||||
// buttons
|
||||
me.add_delete_button(row, d);
|
||||
});
|
||||
},
|
||||
|
||||
add_cell: function(row, d, fieldname) {
|
||||
return $("<td>").appendTo(row)
|
||||
.attr("data-fieldname", fieldname)
|
||||
.html(__(d[fieldname]));
|
||||
},
|
||||
|
||||
add_check: function(cell, d, fieldname, label) {
|
||||
var me = this;
|
||||
|
||||
if(!label) label = toTitle(fieldname.replace(/_/g, " "));
|
||||
if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var checkbox = $("<div class='col-md-4'><div class='checkbox'>\
|
||||
<label><input type='checkbox'>"+__(label)+"</input></label>"
|
||||
+ (d.help || "") + "</div></div>").appendTo(cell)
|
||||
.attr("data-fieldname", fieldname);
|
||||
|
||||
checkbox.find("input")
|
||||
.prop("checked", d[fieldname] ? true: false)
|
||||
.attr("data-ptype", fieldname)
|
||||
.attr("data-name", d.name)
|
||||
.attr("data-doctype", d.parent)
|
||||
|
||||
checkbox.find("label")
|
||||
.css("text-transform", "capitalize");
|
||||
|
||||
return checkbox;
|
||||
},
|
||||
|
||||
setup_user_permissions: function(d, role_cell) {
|
||||
var me = this;
|
||||
d.help = frappe.render('<ul class="user-permission-help small hidden" style="margin-left: -10px;">\
|
||||
<li style="margin-top: 7px;"><a class="show-user-permission-doctypes">{%= __("Select Document Types") %}</a></li>\
|
||||
<li style="margin-top: 3px;"><a class="show-user-permissions">{%= __("Show User Permissions") %}</a></li>\
|
||||
</ul>', {});
|
||||
|
||||
var checkbox = this.add_check(role_cell, d, "apply_user_permissions")
|
||||
.removeClass("col-md-4")
|
||||
.css({"margin-top": "15px"});
|
||||
|
||||
checkbox.find(".show-user-permission-doctypes").on("click", function() {
|
||||
me.show_user_permission_doctypes(d);
|
||||
});
|
||||
|
||||
var toggle_user_permissions = function() {
|
||||
checkbox.find(".user-permission-help").toggleClass("hidden", !checkbox.find("input").prop("checked"));
|
||||
};
|
||||
|
||||
toggle_user_permissions();
|
||||
checkbox.find("input").on('click', function() {
|
||||
toggle_user_permissions();
|
||||
});
|
||||
|
||||
d.help = "";
|
||||
},
|
||||
|
||||
rights: ["read", "write", "create", "delete", "submit", "cancel", "amend",
|
||||
"print", "email", "report", "import", "export", "set_user_permissions"],
|
||||
|
||||
set_show_users: function(cell, role) {
|
||||
cell.html("<a href='#'>"+role+"</a>")
|
||||
cell.html("<a href='#'>"+__(role)+"</a>")
|
||||
.find("a")
|
||||
.attr("data-role", role)
|
||||
.click(function() {
|
||||
|
|
@ -331,14 +359,14 @@ frappe.PermissionEngine = Class.extend({
|
|||
var d = new frappe.ui.Dialog({
|
||||
title: __("Add New Permission Rule"),
|
||||
fields: [
|
||||
{fieldtype:"Select", label:"Document Type",
|
||||
{fieldtype:"Select", label:__("Document Type"),
|
||||
options:me.options.doctypes, reqd:1, fieldname:"parent"},
|
||||
{fieldtype:"Select", label:"Role",
|
||||
{fieldtype:"Select", label:__("Role"),
|
||||
options:me.options.roles, reqd:1},
|
||||
{fieldtype:"Select", label:"Permission Level",
|
||||
{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:"Button", label:"Add"},
|
||||
{fieldtype:"Button", label:__("Add")},
|
||||
]
|
||||
});
|
||||
if(me.get_doctype()) {
|
||||
|
|
@ -374,10 +402,86 @@ frappe.PermissionEngine = Class.extend({
|
|||
});
|
||||
},
|
||||
|
||||
show_user_permission_doctypes: function(d) {
|
||||
if (!d.dialog) {
|
||||
var fields = [];
|
||||
for (var i=0, l=d.linked_doctypes.length; i<l; i++) {
|
||||
fields.push({
|
||||
fieldtype: "Check",
|
||||
label: __(d.linked_doctypes[i]),
|
||||
fieldname: d.linked_doctypes[i]
|
||||
});
|
||||
}
|
||||
|
||||
fields.push({
|
||||
fieldtype: "Button",
|
||||
label: __("Set"),
|
||||
fieldname: "set_user_permission_doctypes"
|
||||
})
|
||||
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __('Apply User Permissions of these Document Types'),
|
||||
fields: fields
|
||||
});
|
||||
|
||||
var fields_to_check = d.user_permission_doctypes
|
||||
? JSON.parse(d.user_permission_doctypes) : d.linked_doctypes;
|
||||
|
||||
for (var i=0, l=fields_to_check.length; i<l; i++) {
|
||||
dialog.set_value(fields_to_check[i], 1);
|
||||
}
|
||||
|
||||
var btn = dialog.get_input("set_user_permission_doctypes");
|
||||
btn.on("click", function() {
|
||||
var values = dialog.get_values();
|
||||
var user_permission_doctypes = [];
|
||||
$.each(values, function(key, val) {
|
||||
if (val) {
|
||||
user_permission_doctypes.push(key);
|
||||
}
|
||||
});
|
||||
if (!user_permission_doctypes || !user_permission_doctypes.length ||
|
||||
user_permission_doctypes.length === d.linked_doctypes.length) {
|
||||
// if all checked
|
||||
user_permission_doctypes = undefined;
|
||||
} else {
|
||||
user_permission_doctypes.sort();
|
||||
user_permission_doctypes = JSON.stringify(user_permission_doctypes);
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
module: "frappe.core",
|
||||
page: "permission_manager",
|
||||
method: "update",
|
||||
args: {
|
||||
doctype: d.parent,
|
||||
name: d.name,
|
||||
ptype: "user_permission_doctypes",
|
||||
value: user_permission_doctypes
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.exc) {
|
||||
msgprint(__("Did not set"));
|
||||
} else {
|
||||
var msg = msgprint(__("Saved!"));
|
||||
setTimeout(function() { msg.hide(); }, 3000);
|
||||
d.user_permission_doctypes = user_permission_doctypes;
|
||||
dialog.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
d.dialog = dialog;
|
||||
}
|
||||
|
||||
d.dialog.show();
|
||||
},
|
||||
|
||||
make_reset_button: function() {
|
||||
var me = this;
|
||||
$('<button class="btn btn-default" style="margin-left: 10px;">\
|
||||
<i class="icon-refresh"></i> Restore Original Permissions</button>')
|
||||
<i class="icon-refresh"></i> ' + __("Restore Original Permissions") + '</button>')
|
||||
.appendTo(this.body.find(".permission-toolbar"))
|
||||
.on("click", function() {
|
||||
me.get_standard_permissions(function(data) {
|
||||
|
|
@ -478,6 +582,9 @@ var permissions_help = ['<table class="table table-bordered" style="background-c
|
|||
__("To give acess to a role for only specific records, check the 'Apply User Permissions'. User Permissions are used to limit users with such role to specific records.")
|
||||
+ ' (<a href="#user-permissions">' + __('Setup > User Permissions Manager') + '</a>)',
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Select Document Types to set which User Permissions are used to limit access."),
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger)."),
|
||||
'</li>',
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
import frappe.defaults
|
||||
from frappe.modules.import_file import get_file_path, read_doc_from_file
|
||||
from frappe.translate import send_translations
|
||||
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_roles_and_doctypes():
|
||||
frappe.only_for("System Manager")
|
||||
send_translations(frappe.get_lang_dict("doctype", "DocPerm"))
|
||||
return {
|
||||
"doctypes": [d[0] for d in frappe.db.sql("""select name from `tabDocType` dt where
|
||||
ifnull(istable,0)=0 and
|
||||
|
|
@ -22,12 +24,27 @@ def get_roles_and_doctypes():
|
|||
@frappe.whitelist()
|
||||
def get_permissions(doctype=None, role=None):
|
||||
frappe.only_for("System Manager")
|
||||
return frappe.db.sql("""select * from tabDocPerm
|
||||
out = frappe.db.sql("""select * from tabDocPerm
|
||||
where %s%s order by parent, permlevel, role""" %
|
||||
(doctype and (" parent='%s'" % doctype.replace("'", "\'")) or "",
|
||||
role and ((doctype and " and " or "") + " role='%s'" % role.replace("'", "\'")) or ""),
|
||||
as_dict=True)
|
||||
|
||||
def get_linked_doctypes(dt):
|
||||
return list(set([dt] + [d.options for d in
|
||||
frappe.get_meta(dt).get("fields", {
|
||||
"fieldtype":"Link",
|
||||
"ignore_user_permissions":("!=", 1),
|
||||
"options": ("!=", "[Select]")
|
||||
})
|
||||
]))
|
||||
|
||||
linked_doctypes = {}
|
||||
for d in out:
|
||||
d.linked_doctypes = linked_doctypes.setdefault(d.parent, get_linked_doctypes(d.parent))
|
||||
|
||||
return out
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove(doctype, name):
|
||||
frappe.only_for("System Manager")
|
||||
|
|
@ -51,7 +68,7 @@ def add(parent, role, permlevel):
|
|||
validate_and_reset(parent)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update(name, doctype, ptype, value=0):
|
||||
def update(name, doctype, ptype, value=None):
|
||||
frappe.only_for("System Manager")
|
||||
frappe.db.sql("""update tabDocPerm set `%s`=%s where name=%s"""\
|
||||
% (ptype, '%s', '%s'), (value, name))
|
||||
|
|
@ -88,6 +105,7 @@ def get_users_with_role(role):
|
|||
|
||||
@frappe.whitelist()
|
||||
def get_standard_permissions(doctype):
|
||||
frappe.only_for("System Manager")
|
||||
module = frappe.db.get_value("DocType", doctype, "module")
|
||||
path = get_file_path(module, "DocType", doctype)
|
||||
return read_doc_from_file(path).get("permissions")
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class DatabaseQuery(object):
|
|||
self.doctype = doctype
|
||||
self.tables = []
|
||||
self.conditions = []
|
||||
self.fields = ["name"]
|
||||
self.fields = ["`tab{0}`.`name`".format(doctype)]
|
||||
self.user = None
|
||||
self.ignore_permissions = False
|
||||
|
||||
|
|
@ -223,15 +223,18 @@ class DatabaseQuery(object):
|
|||
if role_permissions.get("apply_user_permissions", {}).get("read"):
|
||||
# get user permissions
|
||||
user_permissions = frappe.defaults.get_user_permissions(self.user)
|
||||
self.add_user_permissions(user_permissions)
|
||||
self.add_user_permissions(user_permissions,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes"))
|
||||
|
||||
if as_condition:
|
||||
return self.build_match_condition_string()
|
||||
else:
|
||||
return self.match_filters
|
||||
|
||||
def add_user_permissions(self, user_permissions):
|
||||
fields_to_check = frappe.get_meta(self.doctype).get_fields_to_check_permissions(user_permissions.keys())
|
||||
def add_user_permissions(self, user_permissions, user_permission_doctypes=None):
|
||||
user_permission_doctypes = frappe.permissions.get_user_permission_doctypes(user_permission_doctypes,
|
||||
user_permissions)
|
||||
fields_to_check = frappe.get_meta(self.doctype).get_fields_to_check_permissions(user_permission_doctypes)
|
||||
|
||||
# check in links
|
||||
for df in fields_to_check:
|
||||
|
|
|
|||
|
|
@ -194,15 +194,15 @@ class Meta(Document):
|
|||
|
||||
self.set("fields", newlist)
|
||||
|
||||
def get_fields_to_check_permissions(self, user_permissions_doctypes):
|
||||
def get_fields_to_check_permissions(self, user_permission_doctypes):
|
||||
fields = self.get("fields", {
|
||||
"fieldtype":"Link",
|
||||
"parent": self.name,
|
||||
"ignore_user_permissions":("!=", 1),
|
||||
"options":("in", user_permissions_doctypes)
|
||||
"options":("in", user_permission_doctypes)
|
||||
})
|
||||
|
||||
if self.name in user_permissions_doctypes:
|
||||
if self.name in user_permission_doctypes:
|
||||
fields.append(frappe._dict({
|
||||
"label":"Name",
|
||||
"fieldname":"name",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, copy
|
||||
import frappe, copy, json
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import cint
|
||||
|
||||
|
|
@ -42,7 +42,8 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None):
|
|||
doc = frappe.get_doc(meta.name, doc)
|
||||
|
||||
if role_permissions["apply_user_permissions"].get(ptype):
|
||||
if not user_has_permission(doc, verbose=verbose, user=user):
|
||||
if not user_has_permission(doc, verbose=verbose, user=user,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes")):
|
||||
return False
|
||||
|
||||
if not has_controller_permissions(doc, ptype, user=user):
|
||||
|
|
@ -66,7 +67,8 @@ def get_doc_permissions(doc, verbose=False, user=None):
|
|||
if not cint(meta.allow_import):
|
||||
role_permissions["import"] = 0
|
||||
|
||||
if not user_has_permission(doc, verbose=verbose, user=user):
|
||||
if not user_has_permission(doc, verbose=verbose, user=user,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes")):
|
||||
# no user permissions, switch off all user-level permissions
|
||||
for ptype in role_permissions:
|
||||
if role_permissions["apply_user_permissions"].get(ptype):
|
||||
|
|
@ -88,7 +90,20 @@ def get_role_permissions(meta, user=None):
|
|||
perms[ptype] = perms.get(ptype, 0) or cint(p.get(ptype))
|
||||
|
||||
if ptype != "set_user_permissions" and p.get(ptype):
|
||||
perms["apply_user_permissions"][ptype] = perms["apply_user_permissions"].get(ptype, 1) and p.get("apply_user_permissions")
|
||||
perms["apply_user_permissions"][ptype] = (perms["apply_user_permissions"].get(ptype, 1)
|
||||
and p.get("apply_user_permissions"))
|
||||
|
||||
if p.apply_user_permissions and p.user_permission_doctypes:
|
||||
# set user_permission_doctypes in perms
|
||||
user_permission_doctypes = (json.loads(p.user_permission_doctypes)
|
||||
if p.user_permission_doctypes else None)
|
||||
|
||||
if user_permission_doctypes and (not perms.get("user_permission_doctypes") or
|
||||
len(user_permission_doctypes) <= len(perms["user_permission_doctypes"])):
|
||||
# selecting the least no. of "user_permission_doctypes" for lesser filtering
|
||||
# why? if there is a conflict of two user_permission_doctypes, the least restrictive should win
|
||||
# hence, using the simplistic approach of less no. of "user_permission_doctypes" implies least restrictive!
|
||||
perms["user_permission_doctypes"] = user_permission_doctypes
|
||||
|
||||
for key, value in perms.get("apply_user_permissions").items():
|
||||
if not value:
|
||||
|
|
@ -98,16 +113,17 @@ def get_role_permissions(meta, user=None):
|
|||
|
||||
return frappe.local.role_permissions[cache_key]
|
||||
|
||||
def user_has_permission(doc, verbose=True, user=None):
|
||||
def user_has_permission(doc, verbose=True, user=None, user_permission_doctypes=None):
|
||||
from frappe.defaults import get_user_permissions
|
||||
user_permissions = get_user_permissions(user)
|
||||
user_permissions_keys = user_permissions.keys()
|
||||
user_permission_doctypes = get_user_permission_doctypes(user_permission_doctypes, user_permissions)
|
||||
|
||||
def check_user_permission(d):
|
||||
result = True
|
||||
meta = frappe.get_meta(d.get("doctype"))
|
||||
for df in meta.get_fields_to_check_permissions(user_permissions_keys):
|
||||
if d.get(df.fieldname) and d.get(df.fieldname) not in user_permissions[df.options]:
|
||||
for df in meta.get_fields_to_check_permissions(user_permission_doctypes):
|
||||
if (df.options in user_permissions and d.get(df.fieldname)
|
||||
and d.get(df.fieldname) not in user_permissions[df.options]):
|
||||
result = False
|
||||
|
||||
if verbose:
|
||||
|
|
@ -192,3 +208,12 @@ def apply_user_permissions(doctype, ptype, user=None):
|
|||
"""Check if apply_user_permissions is checked for a doctype, perm type, user combination"""
|
||||
role_permissions = get_role_permissions(frappe.get_meta(doctype), user=user)
|
||||
return role_permissions.get("apply_user_permissions", {}).get(ptype)
|
||||
|
||||
def get_user_permission_doctypes(user_permission_doctypes, user_permissions):
|
||||
if user_permission_doctypes:
|
||||
# select those user permission doctypes for which user permissions exist!
|
||||
user_permission_doctypes = list(set(user_permission_doctypes).intersection(set(user_permissions.keys())))
|
||||
else:
|
||||
user_permission_doctypes = user_permissions.keys()
|
||||
|
||||
return user_permission_doctypes
|
||||
|
|
|
|||
|
|
@ -62,14 +62,13 @@ $.extend(frappe.meta, {
|
|||
return docfields;
|
||||
},
|
||||
|
||||
get_fields_to_check_permissions: function(doctype, name, user_permissions) {
|
||||
var user_permissions_doctypes = Object.keys(user_permissions);
|
||||
get_fields_to_check_permissions: function(doctype, name, user_permission_doctypes) {
|
||||
var fields = $.map(frappe.meta.get_docfields(doctype, name), function(df) {
|
||||
return (df.fieldtype==="Link" && df.ignore_user_permissions!==1 &&
|
||||
user_permissions_doctypes.indexOf(df.options)!==-1) ? df : null;
|
||||
user_permission_doctypes.indexOf(df.options)!==-1) ? df : null;
|
||||
});
|
||||
|
||||
if (user_permissions_doctypes.indexOf(doctype)!==-1) {
|
||||
if (user_permission_doctypes.indexOf(doctype)!==-1) {
|
||||
fields = fields.concat({label: "Name", fieldname: name, options: doctype});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,20 @@ $.extend(frappe.perm, {
|
|||
apply_user_permissions[key] = current_value && p.apply_user_permissions;
|
||||
}
|
||||
});
|
||||
|
||||
if (permlevel===0 && p.apply_user_permissions && p.user_permission_doctypes) {
|
||||
// set user_permission_doctypes in perms
|
||||
var user_permission_doctypes = p.user_permission_doctypes
|
||||
? JSON.parse(p.user_permission_doctypes) : null;
|
||||
|
||||
if (user_permission_doctypes && (!perm[permlevel].user_permission_doctypes ||
|
||||
user_permission_doctypes.length <= perm[permlevel].user_permission_doctypes.length)) {
|
||||
// selecting the least no. of "user_permission_doctypes" for lesser filtering
|
||||
// why? if there is a conflict of two user_permission_doctypes, the least restrictive should win
|
||||
// hence, using the simplistic approach of less no. of "user_permission_doctypes" implies least restrictive!
|
||||
perm[permlevel]["user_permission_doctypes"] = user_permission_doctypes;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -94,6 +108,8 @@ $.extend(frappe.perm, {
|
|||
},
|
||||
|
||||
get_match_rules: function(doctype, ptype) {
|
||||
var me = this;
|
||||
|
||||
if (!ptype) ptype = "read";
|
||||
|
||||
var perm = frappe.perm.get_perm(doctype);
|
||||
|
|
@ -105,7 +121,10 @@ $.extend(frappe.perm, {
|
|||
var match_rules = {};
|
||||
var user_permissions = frappe.defaults.get_user_permissions();
|
||||
if(user_permissions && !$.isEmptyObject(user_permissions)) {
|
||||
var fields_to_check = frappe.meta.get_fields_to_check_permissions(doctype, null, user_permissions);
|
||||
var user_permission_doctypes = me.get_user_permission_doctypes(perm[0].user_permission_doctypes,
|
||||
user_permissions);
|
||||
|
||||
var fields_to_check = frappe.meta.get_fields_to_check_permissions(doctype, null, user_permission_doctypes);
|
||||
$.each(fields_to_check, function(i, df) {
|
||||
match_rules[df.label] = user_permissions[df.options];
|
||||
});
|
||||
|
|
@ -114,6 +133,20 @@ $.extend(frappe.perm, {
|
|||
return match_rules;
|
||||
},
|
||||
|
||||
get_user_permission_doctypes: function(user_permission_doctypes, user_permissions) {
|
||||
if (user_permission_doctypes) {
|
||||
var out = [];
|
||||
$.each(user_permission_doctypes, function(i, doctype) {
|
||||
if (user_permissions[doctype]) {
|
||||
out.push(doctype);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
} else {
|
||||
return Object.keys(user_permissions);
|
||||
}
|
||||
},
|
||||
|
||||
get_field_display_status: function(df, doc, perm, explain) {
|
||||
if(!doc) return "Write";
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ frappe.ui.AppFrame = Class.extend({
|
|||
views.push({
|
||||
icon: module_info.icon,
|
||||
route: "Module/" + meta.module,
|
||||
type: "module"
|
||||
type: "module",
|
||||
label: __("Module")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +125,7 @@ frappe.ui.AppFrame = Class.extend({
|
|||
icon: "icon-file-alt",
|
||||
route: "",
|
||||
type: "form",
|
||||
label: __("Form"),
|
||||
set_route: function() {
|
||||
console.log(me.doctype);
|
||||
if(frappe.views.formview[me.doctype]) {
|
||||
|
|
@ -140,7 +142,8 @@ frappe.ui.AppFrame = Class.extend({
|
|||
views.push({
|
||||
icon: "icon-list",
|
||||
route: "List/" + doctype,
|
||||
type: "list"
|
||||
type: "list",
|
||||
label: __("List")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +151,8 @@ frappe.ui.AppFrame = Class.extend({
|
|||
views.push({
|
||||
icon: "icon-calendar",
|
||||
route: "Calendar/" + doctype,
|
||||
type: "calendar"
|
||||
type: "calendar",
|
||||
label: __("Calendar")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +160,8 @@ frappe.ui.AppFrame = Class.extend({
|
|||
views.push({
|
||||
icon: "icon-tasks",
|
||||
route: "Gantt/" + doctype,
|
||||
type: "gantt"
|
||||
type: "gantt",
|
||||
label: __("Gantt Chart")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +169,8 @@ frappe.ui.AppFrame = Class.extend({
|
|||
views.push({
|
||||
icon: "icon-table",
|
||||
route: "Report/" + doctype,
|
||||
type: "report"
|
||||
type: "report",
|
||||
label: __("Report")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +180,7 @@ frappe.ui.AppFrame = Class.extend({
|
|||
set_views: function(views, active_view) {
|
||||
var me = this;
|
||||
$.each(views, function(i, e) {
|
||||
var btn = me.add_icon_btn("3", e.icon, __(toTitle(e.type)), e.set_route || function() {
|
||||
var btn = me.add_icon_btn("3", e.icon, __(e.label) || __(toTitle(e.type)), e.set_route || function() {
|
||||
window.location.hash = "#" + $(this).attr("data-route");
|
||||
}).attr("data-route", e.route);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
this.appframe = this.page.appframe;
|
||||
var module = locals.DocType[this.doctype].module;
|
||||
|
||||
this.appframe.set_title(__("{0} List", [this.doctype]));
|
||||
this.appframe.set_title(__("{0} List", [__(this.doctype)]));
|
||||
this.appframe.add_module_icon(module, this.doctype, null, true);
|
||||
this.appframe.set_title_left(function() {
|
||||
frappe.set_route(frappe.listview_parent_route[me.doctype]
|
||||
|
|
@ -253,14 +253,11 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
make_no_result: function() {
|
||||
var new_button = frappe.boot.user.can_create.indexOf(this.doctype)!=-1
|
||||
? ('<hr><p><button class="btn btn-primary" \
|
||||
list_view_doc="%(doctype)s">'+
|
||||
__('Make a new') + ' %(doctype_label)s</button></p>')
|
||||
list_view_doc="' + this.doctype + '">'+
|
||||
__('Make a new {0}', [__(this.doctype)]) + '</button></p>')
|
||||
: '';
|
||||
var no_result_message = repl('<div class="well" style="margin-top: 20px;">\
|
||||
<p>' + __("No") + ' %(doctype_label)s ' + __("found") + '</p>' + new_button + '</div>', {
|
||||
doctype_label: __(this.doctype),
|
||||
doctype: this.doctype,
|
||||
});
|
||||
var no_result_message = '<div class="well" style="margin-top: 20px;">\
|
||||
<p>' + __("No {0} found", [__(this.doctype)]) + '</p>' + new_button + '</div>';
|
||||
|
||||
return no_result_message;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ def get_dict(fortype, name=None):
|
|||
elif fortype=="boot":
|
||||
messages = get_messages_from_include_files()
|
||||
messages += frappe.db.sql_list("select name from tabDocType")
|
||||
messages += frappe.db.sql_list("select name from tabRole")
|
||||
messages += frappe.db.sql_list("select name from `tabModule Def`")
|
||||
|
||||
translation_assets[asset_key] = make_dict_from_messages(messages)
|
||||
|
|
@ -193,6 +194,7 @@ def get_messages_from_doctype(name):
|
|||
|
||||
messages = [meta.name, meta.module]
|
||||
|
||||
# translations of field labels, description and options
|
||||
for d in meta.get("fields"):
|
||||
messages.extend([d.label, d.description])
|
||||
|
||||
|
|
@ -202,6 +204,11 @@ def get_messages_from_doctype(name):
|
|||
if not "icon" in options[0]:
|
||||
messages.extend(options)
|
||||
|
||||
# translations of roles
|
||||
for d in meta.get("permissions"):
|
||||
if d.role:
|
||||
messages.append(d.role)
|
||||
|
||||
# extract from js, py files
|
||||
doctype_file_path = frappe.get_module_path(meta.module, "doctype", meta.name, meta.name)
|
||||
messages.extend(get_messages_from_file(doctype_file_path + ".js"))
|
||||
|
|
@ -301,6 +308,8 @@ def get_untranslated(lang, untranslated_file, get_all=False):
|
|||
for app in apps:
|
||||
messages.extend(get_messages_for_app(app))
|
||||
|
||||
messages = list(set(messages))
|
||||
|
||||
def escape_newlines(s):
|
||||
return (s.replace("\\\n", "|||||")
|
||||
.replace("\\n", "||||")
|
||||
|
|
@ -366,3 +375,10 @@ def write_translations_file(app, lang, full_dict=None):
|
|||
frappe.create_folder(tpath)
|
||||
write_csv_file(os.path.join(tpath, lang + ".csv"),
|
||||
app_messages, full_dict or get_full_dict(lang))
|
||||
|
||||
def send_translations(translation_dict):
|
||||
"""send these translations in response"""
|
||||
if "__messages" not in frappe.local.response:
|
||||
frappe.local.response["__messages"] = {}
|
||||
|
||||
frappe.local.response["__messages"].update(translation_dict)
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ def build_response(response_type=None):
|
|||
def as_csv():
|
||||
response = Response()
|
||||
response.headers[b"Content-Type"] = b"text/csv; charset: utf-8"
|
||||
response.headers[b"Content-Disposition"] = ("attachment; filename=%s.csv" % frappe.response['doctype'].replace(' ', '_')).encode("utf-8")
|
||||
response.headers[b"Content-Disposition"] = ("attachment; filename=\"%s.csv\"" % frappe.response['doctype'].replace(' ', '_')).encode("utf-8")
|
||||
response.data = frappe.response['result']
|
||||
return response
|
||||
|
||||
def as_raw():
|
||||
response = Response()
|
||||
response.headers[b"Content-Type"] = frappe.response.get("content_type") or mimetypes.guess_type(frappe.response['filename'])[0] or b"application/unknown"
|
||||
response.headers[b"Content-Disposition"] = ("filename=%s" % frappe.response['filename'].replace(' ', '_')).encode("utf-8")
|
||||
response.headers[b"Content-Disposition"] = ("filename=\"%s\"" % frappe.response['filename'].replace(' ', '_')).encode("utf-8")
|
||||
response.data = frappe.response['filecontent']
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
import frappe
|
||||
import frappe.defaults
|
||||
import unittest
|
||||
import json
|
||||
import frappe.model.meta
|
||||
from frappe.core.page.user_permissions.user_permissions import add, remove, get_permissions
|
||||
from frappe.permissions import clear_user_permissions_for_doctype, get_doc_permissions
|
||||
|
||||
|
|
@ -30,6 +32,9 @@ class TestBlogPost(unittest.TestCase):
|
|||
clear_user_permissions_for_doctype("Blog Category")
|
||||
clear_user_permissions_for_doctype("Blog Post")
|
||||
clear_user_permissions_for_doctype("Blogger")
|
||||
frappe.db.sql("""update `tabDocPerm` set user_permission_doctypes=null
|
||||
where parent='Blog Post' and permlevel=0 and apply_user_permissions=1
|
||||
and `read`=1""")
|
||||
|
||||
def test_basic_permission(self):
|
||||
post = frappe.get_doc("Blog Post", "_test-blog-post")
|
||||
|
|
@ -145,3 +150,25 @@ class TestBlogPost(unittest.TestCase):
|
|||
doc.title = "New"
|
||||
self.assertRaises(frappe.CannotChangeConstantError, doc.save)
|
||||
blog_post.get_field("title").set_only_once = 0
|
||||
|
||||
def test_user_permission_doctypes(self):
|
||||
frappe.permissions.add_user_permission("Blog Category", "_Test Blog Category 1",
|
||||
"test2@example.com")
|
||||
frappe.permissions.add_user_permission("Blogger", "_Test Blogger 1",
|
||||
"test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
frappe.db.sql("""update `tabDocPerm` set user_permission_doctypes=%s
|
||||
where parent='Blog Post' and permlevel=0 and apply_user_permissions=1
|
||||
and `read`=1""", json.dumps(["Blogger"]))
|
||||
|
||||
frappe.model.meta.clear_cache("Blog Post")
|
||||
|
||||
doc = frappe.get_doc("Blog Post", "_test-blog-post")
|
||||
self.assertFalse(doc.has_permission("read"))
|
||||
|
||||
doc = frappe.get_doc("Blog Post", "_test-blog-post-2")
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
|
||||
frappe.model.meta.clear_cache("Blog Post")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.translate import send_translations
|
||||
|
||||
@frappe.whitelist()
|
||||
def get(name):
|
||||
|
|
@ -28,14 +29,14 @@ def getpage():
|
|||
|
||||
# load translations
|
||||
if frappe.lang != "en":
|
||||
frappe.response["__messages"] = frappe.get_lang_dict("page", page)
|
||||
send_translations(frappe.get_lang_dict("page", page))
|
||||
|
||||
frappe.response.docs.append(doc)
|
||||
|
||||
def has_permission(page):
|
||||
if frappe.user.name == "Administrator" or "System Manager" in frappe.user.get_roles():
|
||||
return True
|
||||
|
||||
|
||||
page_roles = [d.role for d in page.get("roles")]
|
||||
if page_roles:
|
||||
if frappe.session.user == "Guest" and "Guest" not in page_roles:
|
||||
|
|
@ -43,7 +44,7 @@ def has_permission(page):
|
|||
elif not set(page_roles).intersection(set(frappe.get_roles())):
|
||||
# check if roles match
|
||||
return False
|
||||
|
||||
|
||||
if not frappe.has_permission("Page", ptype="read", doc=page):
|
||||
# check if there are any user_permissions
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import os, json
|
|||
from frappe import _
|
||||
from frappe.modules import scrub, get_module_path
|
||||
from frappe.utils import flt, cint, get_html_format
|
||||
from frappe.translate import send_translations
|
||||
import frappe.widgets.reportview
|
||||
|
||||
def get_report_doc(report_name):
|
||||
|
|
@ -50,7 +51,7 @@ def get_script(report_name):
|
|||
|
||||
# load translations
|
||||
if frappe.lang != "en":
|
||||
frappe.response["__messages"] = frappe.get_lang_dict("report", report_name)
|
||||
send_translations(frappe.get_lang_dict("report", report_name))
|
||||
|
||||
return {
|
||||
"script": script,
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
|
|||
fields = get_std_fields_list(meta, searchfield or "name")
|
||||
|
||||
# find relevance as location of search term from the beginning of string `name`. used for sorting results.
|
||||
fields.append("""locate("{_txt}", name) as `_relevance`""".format(
|
||||
fields.append("""locate("{_txt}", `tab{doctype}`.`name`) as `_relevance`""".format(
|
||||
_txt=frappe.db.escape((txt or "").replace("%", "")), doctype=doctype))
|
||||
|
||||
values = frappe.widgets.reportview.execute(doctype,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue