From 6611c126628a3289324c9a882839658a51131e82 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 26 Aug 2014 19:33:58 +0530 Subject: [PATCH 1/4] Select Document Types for applying User Permissions --- frappe/core/doctype/docperm/docperm.json | 11 +- .../permission_manager/permission_manager.js | 181 ++++++++++++++---- .../permission_manager/permission_manager.py | 20 +- frappe/model/db_query.py | 9 +- frappe/model/meta.py | 6 +- frappe/permissions.py | 33 +++- frappe/public/js/frappe/model/meta.js | 7 +- frappe/public/js/frappe/model/perm.js | 25 ++- frappe/public/js/frappe/views/doclistview.js | 1 + .../doctype/blog_post/test_blog_post.py | 27 +++ 10 files changed, 259 insertions(+), 61 deletions(-) diff --git a/frappe/core/doctype/docperm/docperm.json b/frappe/core/doctype/docperm/docperm.json index f575e9f08d..4a57765d3b 100644 --- a/frappe/core/doctype/docperm/docperm.json +++ b/frappe/core/doctype/docperm/docperm.json @@ -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", diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 44314b9ca8..8daf4e9caf 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -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(); } }); @@ -178,66 +178,93 @@ frappe.PermissionEngine = Class.extend({ .appendTo(me.table.find("thead tr")); }); - var add_cell = function(row, d, fieldname) { - return $("").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 = $("
\ - " - + (d.help || "") + "
").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 = $("").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 = '
\ - Show User Pemissions
'; - 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 = $("
").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 $("").appendTo(row) + .attr("data-fieldname", fieldname) + .html(d[fieldname]); + }, + + add_check: function(cell, d, fieldname, label) { + var me = this; + + if(!label) label = fieldname.replace(/_/g, " "); + if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) { + return; + } + + var checkbox = $("
\ + " + + (d.help || "") + "
").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('', {}); + + 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"], @@ -374,6 +401,82 @@ 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\ diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 6ae405f9ca..ce3fd37ebc 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -22,12 +22,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 +66,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 +103,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") diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 334710bed0..036d1465c9 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -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: diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 8387170179..bbc8154c26 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -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", diff --git a/frappe/permissions.py b/frappe/permissions.py index 1085c17181..a141db7c14 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -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,12 @@ 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: + perms["user_permission_doctypes"] = (json.loads(p.user_permission_doctypes) + if p.user_permission_doctypes else None) for key, value in perms.get("apply_user_permissions").items(): if not value: @@ -98,16 +105,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 +200,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 diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js index f886a7e5b9..4ab3bfbbf0 100644 --- a/frappe/public/js/frappe/model/meta.js +++ b/frappe/public/js/frappe/model/meta.js @@ -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}); } diff --git a/frappe/public/js/frappe/model/perm.js b/frappe/public/js/frappe/model/perm.js index 09dcd62b13..25cf658958 100644 --- a/frappe/public/js/frappe/model/perm.js +++ b/frappe/public/js/frappe/model/perm.js @@ -82,6 +82,10 @@ $.extend(frappe.perm, { apply_user_permissions[key] = current_value && p.apply_user_permissions; } }); + + if (permlevel===0 && p.apply_user_permissions && p.user_permission_doctypes) { + perm[permlevel]["user_permission_doctypes"] = JSON.parse(p.user_permission_doctypes); + } } }); @@ -94,6 +98,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 +111,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 +123,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"; diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index 8a55d84269..3991fc0127 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -155,6 +155,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ if(keys(match_rules).length) { var match_text = [] + console.log(match_rules); $.each(match_rules, function(key, values) { if(values.length==0) { match_text.push(__(key) + __(" is not set")); diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index ce48b53ab7..187daeeba2 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -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") From e01a0600f18c0e76b82769fe794d514bdc8e9b26 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 27 Aug 2014 14:52:06 +0530 Subject: [PATCH 2/4] Handle conflicting User Permission DocTypes and DocPerm translations --- .../permission_manager/permission_manager.py | 2 ++ frappe/permissions.py | 14 +++++++++++--- frappe/public/js/frappe/model/perm.js | 12 +++++++++++- frappe/translate.py | 16 ++++++++++++++++ frappe/utils/response.py | 4 ++-- frappe/widgets/page.py | 9 +++++---- frappe/widgets/query_report.py | 3 ++- 7 files changed, 49 insertions(+), 11 deletions(-) diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index ce3fd37ebc..43c8531791 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -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 diff --git a/frappe/permissions.py b/frappe/permissions.py index a141db7c14..f1f85d50d1 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -93,9 +93,17 @@ def get_role_permissions(meta, user=None): perms["apply_user_permissions"][ptype] = (perms["apply_user_permissions"].get(ptype, 1) and p.get("apply_user_permissions")) - if p.apply_user_permissions: - perms["user_permission_doctypes"] = (json.loads(p.user_permission_doctypes) - if p.user_permission_doctypes else None) + 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: diff --git a/frappe/public/js/frappe/model/perm.js b/frappe/public/js/frappe/model/perm.js index 25cf658958..eb3174e24d 100644 --- a/frappe/public/js/frappe/model/perm.js +++ b/frappe/public/js/frappe/model/perm.js @@ -84,7 +84,17 @@ $.extend(frappe.perm, { }); if (permlevel===0 && p.apply_user_permissions && p.user_permission_doctypes) { - perm[permlevel]["user_permission_doctypes"] = JSON.parse(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; + } } } }); diff --git a/frappe/translate.py b/frappe/translate.py index 62c26a35f1..741fe41658 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -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) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 9c8db420f8..1a7b8754a5 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -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 diff --git a/frappe/widgets/page.py b/frappe/widgets/page.py index 0908986913..8e1e26cdc2 100644 --- a/frappe/widgets/page.py +++ b/frappe/widgets/page.py @@ -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 diff --git a/frappe/widgets/query_report.py b/frappe/widgets/query_report.py index e32f574283..1b310079f6 100644 --- a/frappe/widgets/query_report.py +++ b/frappe/widgets/query_report.py @@ -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, From cf829036095240b71c2e494ce5504302a7f1e7a1 Mon Sep 17 00:00:00 2001 From: 81552433qqcom <81552433@qq.com> Date: Tue, 26 Aug 2014 21:05:52 +0800 Subject: [PATCH 3/4] permission_manager.js translation doclistview, no {0} found. setup.py, translated. Issue exists: read, write etc translation doesn't show.(note, not sure if it is case sensitive) Conflicts: frappe/core/page/permission_manager/permission_manager.js --- frappe/config/setup.py | 2 +- .../permission_manager/permission_manager.js | 27 ++++++++++--------- frappe/public/js/frappe/views/doclistview.js | 11 +++----- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/frappe/config/setup.py b/frappe/config/setup.py index 1a8079f1ed..389757e632 100644 --- a/frappe/config/setup.py +++ b/frappe/config/setup.py @@ -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") }, diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 8daf4e9caf..c363a70823 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -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(); @@ -130,7 +130,7 @@ frappe.PermissionEngine = Class.extend({ refresh: function() { var me = this; if(!me.doctype_select) { - this.body.html("
Loading...
"); + this.body.html("
" + __("Loading") + "...
"); 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 = $("
\ \ @@ -172,8 +173,8 @@ frappe.PermissionEngine = Class.extend({
\
").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) { $("").html(col[0]).css("width", col[1]+"px") .appendTo(me.table.find("thead tr")); }); @@ -210,13 +211,13 @@ frappe.PermissionEngine = Class.extend({ add_cell: function(row, d, fieldname) { return $("").appendTo(row) .attr("data-fieldname", fieldname) - .html(d[fieldname]); + .html(__(d[fieldname])); }, add_check: function(cell, d, fieldname, label) { var me = this; - if(!label) label = fieldname.replace(/_/g, " "); + if(!label) label = toTitle(fieldname.replace(/_/g, " ")); if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) { return; } @@ -269,7 +270,7 @@ frappe.PermissionEngine = Class.extend({ "print", "email", "report", "import", "export", "set_user_permissions"], set_show_users: function(cell, role) { - cell.html(""+role+"") + cell.html(""+__(role)+"") .find("a") .attr("data-role", role) .click(function() { @@ -358,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()) { @@ -480,7 +481,7 @@ frappe.PermissionEngine = Class.extend({ make_reset_button: function() { var me = this; $('') + ' + __("Restore Original Permissions") + '') .appendTo(this.body.find(".permission-toolbar")) .on("click", function() { me.get_standard_permissions(function(data) { diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index 3991fc0127..e6a163caf2 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -254,14 +254,11 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ make_no_result: function() { var new_button = frappe.boot.user.can_create.indexOf(this.doctype)!=-1 ? ('

') + list_view_doc="' + this.doctype + '">'+ + __('Make a new {0}', [__(this.doctype)]) + '

') : ''; - var no_result_message = repl('
\ -

' + __("No") + ' %(doctype_label)s ' + __("found") + '

' + new_button + '
', { - doctype_label: __(this.doctype), - doctype: this.doctype, - }); + var no_result_message = '
\ +

' + __("No {0} found", [__(this.doctype)]) + '

' + new_button + '
'; return no_result_message; }, From bd995efb56fa1608727216de3e4bef08194861bd Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 27 Aug 2014 15:19:06 +0530 Subject: [PATCH 4/4] [minor] translations in appframe and listing --- .../permission_manager/permission_manager.js | 3 +++ frappe/model/db_query.py | 2 +- frappe/public/js/frappe/ui/appframe.js | 18 ++++++++++++------ frappe/public/js/frappe/views/doclistview.js | 3 +-- frappe/widgets/search.py | 2 +- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index c363a70823..36ff5b7e8d 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -582,6 +582,9 @@ var permissions_help = ['' + __('Setup > User Permissions Manager') + ')', '', + '
  • ', + __("Select Document Types to set which User Permissions are used to limit access."), + '
  • ', '
  • ', __("Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger)."), '
  • ', diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 036d1465c9..b5c091dade 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -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 diff --git a/frappe/public/js/frappe/ui/appframe.js b/frappe/public/js/frappe/ui/appframe.js index 186fa57073..1f53dae77b 100644 --- a/frappe/public/js/frappe/ui/appframe.js +++ b/frappe/public/js/frappe/ui/appframe.js @@ -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); diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index e6a163caf2..56a1328035 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -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] @@ -155,7 +155,6 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ if(keys(match_rules).length) { var match_text = [] - console.log(match_rules); $.each(match_rules, function(key, values) { if(values.length==0) { match_text.push(__(key) + __(" is not set")); diff --git a/frappe/widgets/search.py b/frappe/widgets/search.py index 1d8b92c564..5be24c05f2 100644 --- a/frappe/widgets/search.py +++ b/frappe/widgets/search.py @@ -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,