Remove flags in frappe.call, api, run_method, filter moduleview items by permission

This commit is contained in:
Anand Doshi 2015-02-10 18:56:05 +05:30
parent 3f605787f5
commit ce9e3ed931
29 changed files with 176 additions and 67 deletions

View file

@ -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):

View file

@ -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()

View file

@ -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':

View file

@ -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")

View file

@ -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)`"""

View file

@ -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

View file

@ -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) {

View file

@ -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))

View file

@ -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);
}

View file

@ -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

View file

@ -18,7 +18,7 @@
<div class="text-muted">
{%= comment_when(data.modified) %}
</div>
{% if (data.owner==user || data.comment.indexOf("assigned to")!=-1) { %}
{% if (data.owner==user /* && !data.comment_type && data.parenttype!="Assignment" */ ) { %}
<div>
<a class="delete text-extra-muted" data-name="{%= data.name %}"
onclick="frappe.desk.pages.messages.delete(this)">Delete</a>

View file

@ -1,4 +1,4 @@
<ul class="nav nav-pills nav-stacked" style="margin-right: -15px;">
<ul class="nav nav-pills nav-stacked">
{% for (var i=0, l= data.length; i < l; i++) { var contact = data[i]; %}
<li data-user="{%= contact.name %}" class="h6 module-sidebar-item">
<a class="messages-sidebar-link text-ellipsis">

View file

@ -94,3 +94,5 @@ scheduler_events = {
"frappe.email.doctype.email_alert.email_alert.trigger_daily_alerts",
]
}
default_background = "/assets/frappe/images/ui/into-the-dawn.jpg"

View file

@ -11,7 +11,8 @@ from frappe import _
from rename_doc import dynamic_link_queries
from frappe.model.naming import revert_series_if_last
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):
"""
Deletes a doc(dt, dn) and validates if it is not submitted and not linked in a live record
"""
@ -58,7 +59,14 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa
doc = frappe.get_doc(doctype, name)
if not for_reload:
check_permission_and_not_submitted(doc, ignore_permissions)
if ignore_permissions:
if not flags: flags = {}
flags["ignore_permissions"] = ignore_permissions
if flags:
doc.flags.update(flags)
check_permission_and_not_submitted(doc)
doc.run_method("on_trash")
delete_linked_todos(doc)
@ -113,9 +121,9 @@ def delete_from_table(doctype, name, ignore_doctypes, doc):
if t not in ignore_doctypes:
frappe.db.sql("delete from `tab%s` where parenttype=%s and parent = %s" % (t, '%s', '%s'), (doctype, name))
def check_permission_and_not_submitted(doc, ignore_permissions=False):
def check_permission_and_not_submitted(doc):
# permission
if not ignore_permissions and frappe.session.user!="Administrator" and not doc.has_permission("delete"):
if frappe.session.user!="Administrator" and not doc.has_permission("delete"):
frappe.msgprint(_("User not allowed to delete {0}: {1}").format(doc.doctype, doc.name), raise_exception=True)
# check if submitted

View file

@ -448,6 +448,9 @@ class Document(BaseDocument):
def run_method(self, method, *args, **kwargs):
"""run standard triggers, plus those in hooks"""
if "flags" in kwargs:
del kwargs["flags"]
if hasattr(self, method) and hasattr(getattr(self, method), "__call__"):
fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
else:
@ -476,7 +479,7 @@ class Document(BaseDocument):
def delete(self):
"""Delete document."""
frappe.delete_doc(self.doctype, self.name)
frappe.delete_doc(self.doctype, self.name, flags=self.flags)
def run_before_save_methods(self):
"""Run standard methods before `INSERT` or `UPDATE`. Standard Methods are:

View file

@ -117,7 +117,6 @@ def import_doc(docdict, force=False, data_import=False):
doc.flags.ignore_validate = True
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.flags.ignore_user_permissions = True
doc.insert()
frappe.flags.in_import = False

View file

@ -306,13 +306,16 @@ kbd {
padding: 10px 15px;
}
.message-row .indicator {
margin-left: -10px;
margin-left: -5px;
margin-right: -20px;
}
.message-box .indicator {
margin-right: 15px;
margin-top: 7px;
}
.message-box .timeline-head {
padding-top: 30px;
}
.page-only-label {
margin-top: 5px;
text-align: center;

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

View file

@ -145,6 +145,16 @@ frappe.assets = {
"Module": function() {
frappe.require("assets/css/module.min.css");
frappe.require("assets/js/module.min.js");
}
},
"Calendar": function() {
frappe.assets.views["Report"]();
frappe.require('assets/frappe/js/lib/fullcalendar/fullcalendar.css');
frappe.require('assets/frappe/js/lib/fullcalendar/fullcalendar.js');
},
"Gantt": function() {
frappe.assets.views["Report"]();
frappe.require('assets/frappe/js/lib/jQuery.Gantt/css/style.css');
frappe.require('assets/frappe/js/lib/jQuery.Gantt/js/jquery.fn.gantt.js');
},
}
};

View file

@ -17,7 +17,6 @@ frappe.Application = Class.extend({
},
startup: function() {
this.load_bootinfo();
this.set_user_display_settings();
this.make_nav_bar();
this.set_favicon();
this.setup_keyboard_shortcuts();
@ -44,10 +43,6 @@ frappe.Application = Class.extend({
$(document).trigger('app_ready');
},
set_user_display_settings: function() {
frappe.ui.set_user_background(frappe.boot.user.background_image, null,
frappe.boot.user.background_style);
},
load_bootinfo: function() {
if(frappe.boot) {
frappe.modules = frappe.boot.modules;

View file

@ -145,7 +145,9 @@ frappe.ui.form.Comments = Class.extend({
"Workflow": "octicon octicon-git-branch",
"Label": "octicon octicon-tag",
"Attachment": "octicon octicon-cloud-upload",
"Attachment Removed": "octicon octicon-trashcan"
"Attachment Removed": "octicon octicon-trashcan",
"Shared": "octicon octicon-eye",
"Unshared": "octicon octicon-circle-slash"
}[c.comment_type]
c.color = {

View file

@ -25,7 +25,7 @@
{%= data.comment_html %}
</div>
</div>
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared"], data.comment_type)) { %}
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared", "Unshared"], data.comment_type)) { %}
<h6>
<i class="{%= data.icon %} icon-fixed-width"></i>
{%= data.comment %}

View file

@ -36,10 +36,7 @@ frappe.get_gravatar = function(email_id) {
frappe.ui.set_user_background = function(src, selector, style) {
if(!selector) selector = "#page-desktop";
if(!style) style = "Fill Screen";
if(!src) src = "assets/frappe/images/ui/wallpaper-5.jpg";
// hack! load background image asap, before page is rendered
$('<img src="'+src+'" style="height: 1px; width: 1px; margin-bottom: -1px;">').appendTo("body");
if(!src) src = frappe.boot.default_background_image;
frappe.dom.set_style(repl('%(selector)s { \
background: url("%(src)s") center center;\

View file

@ -22,10 +22,6 @@ frappe.views.CalendarFactory = frappe.views.Factory.extend({
frappe.views.Calendar = Class.extend({
init: function(options) {
$.extend(this, options);
frappe.require('assets/frappe/js/lib/fullcalendar/fullcalendar.css');
frappe.require('assets/frappe/js/lib/fullcalendar/fullcalendar.js');
this.make_page();
this.setup_options();
this.make();

View file

@ -26,10 +26,6 @@ frappe.views.GanttFactory = frappe.views.Factory.extend({
frappe.views.Gantt = Class.extend({
init: function(opts) {
$.extend(this, opts);
frappe.require('assets/frappe/js/lib/jQuery.Gantt/css/style.css');
frappe.require('assets/frappe/js/lib/jQuery.Gantt/js/jquery.fn.gantt.js');
this.make_page();
frappe.route_options ?
this.set_filters_from_route_options() :

View file

@ -353,13 +353,19 @@ kbd {
}
.message-row .indicator {
margin-left: -10px;
margin-left: -5px;
margin-right: -20px;
}
.message-box .indicator {
margin-right: 15px;
margin-top: 7px;
.message-box {
.indicator {
margin-right: 15px;
margin-top: 7px;
}
.timeline-head {
padding-top: 30px;
}
}
.page-only-label {

View file

@ -3,6 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint
@frappe.whitelist()
def add(doctype, name, user=None, read=1, write=0, share=0, flags=None):
@ -14,19 +15,30 @@ def add(doctype, name, user=None, read=1, write=0, share=0, flags=None):
"share_doctype": doctype})
if share_name:
frappe.delete_doc("DocShare", share_name)
doc = frappe.get_doc("DocShare", share_name)
else:
doc = frappe.new_doc("DocShare")
doc.update({
"user": user,
"share_doctype": doctype,
"share_name": name
})
return frappe.get_doc({
"doctype": "DocShare",
"user": user,
"share_doctype": doctype,
"share_name": name,
"read": read,
"write": write,
"share": share
}).insert(ignore_permissions=True)
if flags:
doc.flags.update(flags)
def remove(doctype, name, user):
doc.update({
# always add read, since you are adding!
"read": 1,
"write": cint(write),
"share": cint(share)
})
doc.save(ignore_permissions=True)
return doc
def remove(doctype, name, user, flags=None):
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
"share_doctype": doctype})
@ -48,6 +60,7 @@ def set_permission(doctype, name, user, permission_to, value=1):
pass
else:
share = frappe.get_doc("DocShare", share_name)
share.flags.ignore_permissions = True
share.set(permission_to, value)
if not value:

View file

@ -36,6 +36,9 @@
</div>
</div>
<!-- hack! load background image asap, before desktop is rendered -->
<img src="{{ background_image }}" style="height: 1px; width: 1px; margin-bottom: -1px;">
<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
<script type="text/javascript">

View file

@ -20,10 +20,13 @@ def get_context(context):
frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError)
hooks = frappe.get_hooks()
boot = frappe.sessions.get()
return {
"build_version": str(os.path.getmtime(os.path.join(frappe.local.sites_path, "assets", "js",
"frappe.min.js"))),
"include_js": hooks["app_include_js"],
"include_css": hooks["app_include_css"],
"boot": json.dumps(frappe.sessions.get(), default=json_handler, indent=1, sort_keys=True)
"boot": json.dumps(boot, default=json_handler, indent=1, sort_keys=True),
"background_image": boot.user.background_image or boot.default_background_image
}