diff --git a/frappe/__init__.py b/frappe/__init__.py index d64683c620..b26452cc07 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -446,7 +446,8 @@ def get_meta(doctype, cached=True): import frappe.model.meta return frappe.model.meta.get_meta(doctype, cached=cached) -def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): +def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, + ignore_permissions=False, flags=None): """Delete a document. Calls `frappe.model.delete_doc.delete_doc`. :param doctype: DocType of document to be delete. @@ -456,7 +457,8 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa :param for_reload: Call `before_reload` trigger before deleting. :param ignore_permissions: Ignore user permissions.""" import frappe.model.delete_doc - frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload, ignore_permissions) + frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload, + ignore_permissions, flags) def delete_doc_if_exists(doctype, name): """Delete document if exists.""" @@ -682,6 +684,10 @@ def call(fn, *args, **kwargs): for a in fnargs: if a in kwargs: newargs[a] = kwargs.get(a) + + if "flags" in newargs: + del newargs["flags"] + return fn(*args, **newargs) def make_property_setter(args, ignore_validate=False, validate_fields_for_doctype=True): diff --git a/frappe/api.py b/frappe/api.py index ae17316631..094a83036e 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -70,10 +70,15 @@ def handle(): if frappe.local.request.method=="PUT": data = json.loads(frappe.local.form_dict.data) doc = frappe.get_doc(doctype, name) + + if "flags" in data: + del data["flags"] + # Not checking permissions here because it's checked in doc.save doc.update(data) + frappe.local.response.update({ - "data": doc.save().as_dict() + "data": doc.save().as_dict() }) frappe.db.commit() diff --git a/frappe/boot.py b/frappe/boot.py index 0c63c48284..71f301b003 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -45,7 +45,7 @@ def get_bootinfo(): tabDocType where ifnull(icon,'')!=''""")) bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where ifnull(issingle,0)=1""") add_home_page(bootinfo, doclist) - add_allowed_pages(bootinfo) + bootinfo.page_info = get_allowed_pages() load_translations(bootinfo) add_timezone_info(bootinfo) load_conf_settings(bootinfo) @@ -65,6 +65,7 @@ def get_bootinfo(): bootinfo.lang = unicode(bootinfo.lang) bootinfo.error_report_email = frappe.get_hooks("error_report_email") + bootinfo.default_background_image = "/assets/frappe/images/ui/into-the-dawn.jpg" return bootinfo @@ -73,9 +74,10 @@ def load_conf_settings(bootinfo): for key in ['developer_mode']: if key in conf: bootinfo[key] = conf.get(key) -def add_allowed_pages(bootinfo): +def get_allowed_pages(): roles = frappe.get_roles() - bootinfo.page_info = {} + page_info = {} + for p in frappe.db.sql("""select distinct tabPage.name, tabPage.modified, tabPage.title from `tabPage Role`, `tabPage` @@ -83,7 +85,7 @@ def add_allowed_pages(bootinfo): and `tabPage Role`.parent = `tabPage`.name""" % ', '.join(['%s']*len(roles)), roles, as_dict=True): - bootinfo.page_info[p.name] = {"modified":p.modified, "title":p.title} + page_info[p.name] = {"modified":p.modified, "title":p.title} # pages where role is not set are also allowed for p in frappe.db.sql("""select name, modified, title @@ -91,7 +93,9 @@ def add_allowed_pages(bootinfo): (select count(*) from `tabPage Role` where `tabPage Role`.parent=tabPage.name) = 0""", as_dict=1): - bootinfo.page_info[p.name] = {"modified":p.modified, "title":p.title} + page_info[p.name] = {"modified":p.modified, "title":p.title} + + return page_info def load_translations(bootinfo): if frappe.local.lang != 'en': diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index f03336c69f..74e70de112 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -105,6 +105,9 @@ class Comment(Document): def on_trash(self): """Removes from `_comments` in parent Document""" + if self.comment_doctype == "Message": + return + if (self.comment_type or "Comment") != "Comment": frappe.only_for("System Manager") diff --git a/frappe/core/doctype/docshare/docshare.py b/frappe/core/doctype/docshare/docshare.py index a41fd4c4ad..f035f6baeb 100644 --- a/frappe/core/doctype/docshare/docshare.py +++ b/frappe/core/doctype/docshare/docshare.py @@ -11,8 +11,9 @@ class DocShare(Document): no_feed_on_delete = True def validate(self): - self.check_share_permssion() + self.check_share_permission() self.cascade_permissions_downwards() + self.get_doc().run_method("validate_share", self) def cascade_permissions_downwards(self): if self.share: @@ -25,18 +26,22 @@ class DocShare(Document): self._doc = frappe.get_doc(self.share_doctype, self.share_name) return self._doc - def check_share_permssion(self): - if not frappe.has_permission(self.share_doctype, "share", self.get_doc()): - raise frappe.PermissionError + def check_share_permission(self): + if (not self.flags.ignore_share_permission and + not frappe.has_permission(self.share_doctype, "share", self.get_doc())): + + frappe.throw(_('You need to have "Share" permission'), frappe.PermissionError) def after_insert(self): - self.get_doc().add_comment(_("{0} shared this document with {0}").format(get_fullname(self.owner), - get_fullname(self.user))) + self.get_doc().add_comment("Shared", + _("{0} shared this document with {1}").format(get_fullname(self.owner), get_fullname(self.user))) def on_trash(self): - self.check_share_permssion() - self.get_doc().add_comment(_("{0} un-shared this document with {0}").format(get_fullname(self.owner), - get_fullname(self.user))) + if not self.flags.ignore_share_permission: + self.check_share_permission() + + self.get_doc().add_comment("Unshared", + _("{0} un-shared this document with {1}").format(get_fullname(self.owner), get_fullname(self.user))) def on_doctype_update(): """Add index in `tabDocShare` for `(user, share_doctype)`""" diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 8440afc09c..ef00e38bed 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -82,9 +82,19 @@ class User(Document): def share_with_self(self): if self.user_type=="System User": - frappe.share.add(self.doctype, self.name, self.name, share=1) + frappe.share.add(self.doctype, self.name, self.name, share=1, + flags={"ignore_share_permission": True}) else: - frappe.share.remove(self.doctype, self.name, self.name) + frappe.share.remove(self.doctype, self.name, self.name, + flags={"ignore_share_permission": True}) + + def validate_share(self, docshare): + if docshare.user == self.name: + if self.user_type=="System User": + if docshare.share != 1: + frappe.throw(_("Sorry! User should have complete access to their own record.")) + else: + frappe.throw(_("Sorry! Sharing with Website User is prohibited.")) def clear_new_password(self): new_password = self.new_password diff --git a/frappe/core/page/desktop/desktop.js b/frappe/core/page/desktop/desktop.js index bf799afb83..f2491b7da0 100644 --- a/frappe/core/page/desktop/desktop.js +++ b/frappe/core/page/desktop/desktop.js @@ -4,6 +4,7 @@ frappe.pages['desktop'].on_page_load = function(wrapper) { frappe.assets.views["Module"](); // load desktop + frappe.desktop.set_background(); frappe.desktop.refresh(wrapper); }; @@ -130,6 +131,11 @@ $.extend(frappe.desktop, { }); }, + set_background: function() { + frappe.ui.set_user_background(frappe.boot.user.background_image, null, + frappe.boot.user.background_style); + }, + all_applications: { show: function() { if(!this.dialog) { diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index 0b5ac737fe..a306bb95e1 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.boot import get_allowed_pages @frappe.whitelist() def get(module): @@ -31,6 +32,7 @@ def get_data(module): get_report_list(module)) data = combine_common_sections(data) + data = apply_permissions(data) set_last_modified(data) @@ -115,6 +117,37 @@ def combine_common_sections(data): return sections +def apply_permissions(data): + default_country = frappe.db.get_default("country") + + user = frappe.get_user(frappe.session.user) + user.build_permissions() + + allowed_pages = get_allowed_pages() + + new_data = [] + for section in data: + new_items = [] + + for item in (section.get("items") or []): + item = frappe._dict(item) + + if item.country and item.country!=default_country: + continue + + if ((item.type=="doctype" and item.name in user.can_read) + or (item.type=="page" and item.name in allowed_pages) + or (item.type=="report" and item.doctype in user.can_get_report)): + + new_items.append(item) + + if new_items: + new_section = section.copy() + new_section["items"] = new_items + new_data.append(new_section) + + return new_data + def get_config(app, module): """Load module info from `[app].config.[module]`.""" config = frappe.get_module("{app}.config.{module}".format(app=app, module=module)) diff --git a/frappe/desk/page/messages/messages.js b/frappe/desk/page/messages/messages.js index cd222e7336..c9c8f8578f 100644 --- a/frappe/desk/page/messages/messages.js +++ b/frappe/desk/page/messages/messages.js @@ -7,10 +7,12 @@ frappe.provide('frappe.desk.pages.messages'); frappe.pages.messages.on_page_load = function(parent) { + frappe.assets.views["Form"](); + var page = frappe.ui.make_app_page({ parent: parent, }); - page.set_title(__("Messages"), frappe.get_module("Messages").icon); + page.set_title(__("Messages")); frappe.desk.pages.messages = new frappe.desk.pages.messages(parent); } diff --git a/frappe/desk/page/messages/messages.py b/frappe/desk/page/messages/messages.py index 8008114c97..e9d0d7e099 100644 --- a/frappe/desk/page/messages/messages.py +++ b/frappe/desk/page/messages/messages.py @@ -90,8 +90,7 @@ def post(txt, contact, parenttype=None, notify=False, subject=None): @frappe.whitelist() def delete(arg=None): - frappe.db.sql("""delete from `tabComment` where name=%s""", - frappe.form_dict['name']); + frappe.get_doc("Comment", frappe.form_dict['name']).delete() def _notify(contact, txt, subject=None): from frappe.utils import get_fullname, get_url diff --git a/frappe/desk/page/messages/messages_row.html b/frappe/desk/page/messages/messages_row.html index 5ceac972b8..d544b89a59 100644 --- a/frappe/desk/page/messages/messages_row.html +++ b/frappe/desk/page/messages/messages_row.html @@ -18,7 +18,7 @@