[print] tables & [feature] share #992
This commit is contained in:
parent
c22af0798a
commit
8cde7bfc23
23 changed files with 497 additions and 97 deletions
|
|
@ -198,6 +198,13 @@
|
|||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "share",
|
||||
"fieldtype": "Check",
|
||||
"label": "Share",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"fieldname": "print",
|
||||
"fieldtype": "Check",
|
||||
|
|
@ -216,7 +223,7 @@
|
|||
"idx": 1,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2014-08-26 01:43:31.499363",
|
||||
"modified": "2015-02-04 04:34:26.227213",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocPerm",
|
||||
|
|
|
|||
0
frappe/core/doctype/docshare/__init__.py
Normal file
0
frappe/core/doctype/docshare/__init__.py
Normal file
171
frappe/core/doctype/docshare/docshare.json
Normal file
171
frappe/core/doctype/docshare/docshare.json
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2015-02-04 04:33:36.330477",
|
||||
"custom": 0,
|
||||
"description": "Internal record of document shares",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "User",
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "share_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Document Type",
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "share_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Document Name",
|
||||
"no_copy": 0,
|
||||
"options": "share_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"default": "0",
|
||||
"fieldname": "read",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Read",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"default": "0",
|
||||
"fieldname": "write",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Write",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"default": "0",
|
||||
"fieldname": "share",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Share",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-04 04:44:55.131126",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocShare",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 0,
|
||||
"export": 1,
|
||||
"import": 1,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
33
frappe/core/doctype/docshare/docshare.py
Normal file
33
frappe/core/doctype/docshare/docshare.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
class DocShare(Document):
|
||||
no_feed_on_delete = True
|
||||
|
||||
def validate(self):
|
||||
if not frappe.has_permission(self.share_doctype, "share",
|
||||
frappe.get_doc(self.share_doctype, self.share_name)):
|
||||
frappe.throw(_("Not Allowed"), frappe.PermissionError)
|
||||
self.cascade_permissions_downwards()
|
||||
|
||||
def cascade_permissions_downwards(self):
|
||||
if self.share:
|
||||
self.write = 1
|
||||
if self.write:
|
||||
self.read = 1
|
||||
|
||||
def after_insert(self):
|
||||
self.add_comment(_("{0} shared this document with {0}").format(get_fullname(self.owner), get_fullname(self.user)))
|
||||
|
||||
def on_trash(self):
|
||||
self.add_comment(_("{0} un-shared this document with {0}").format(get_fullname(self.owner), get_fullname(self.user)))
|
||||
|
||||
def on_doctype_update():
|
||||
"""Add index in `tabDocShare` for `(user, share_doctype)`"""
|
||||
frappe.db.add_index("DocShare", ["user", "share_doctype"])
|
||||
69
frappe/core/doctype/docshare/test_docshare.py
Normal file
69
frappe/core/doctype/docshare/test_docshare.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
import frappe.share
|
||||
import unittest
|
||||
|
||||
class TestDocShare(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.user = "test@example.com"
|
||||
self.event = frappe.get_doc({"doctype": "Event",
|
||||
"subject": "test share event",
|
||||
"starts_on": "2015-01-01 10:00:00",
|
||||
"event_type": "Private"}).insert()
|
||||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
self.event.delete()
|
||||
|
||||
def test_add(self):
|
||||
# user not shared
|
||||
self.assertTrue(self.event.name not in frappe.db.get_shared("Event", self.user))
|
||||
frappe.share.add("Event", self.event.name, self.user)
|
||||
self.assertTrue(self.event.name in frappe.db.get_shared("Event", self.user))
|
||||
|
||||
def test_doc_permission(self):
|
||||
frappe.set_user(self.user)
|
||||
self.assertFalse(self.event.has_permission())
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
frappe.share.add("Event", self.event.name, self.user)
|
||||
|
||||
frappe.set_user(self.user)
|
||||
self.assertTrue(self.event.has_permission())
|
||||
|
||||
def test_share_permission(self):
|
||||
frappe.share.add("Event", self.event.name, self.user, share=1)
|
||||
|
||||
frappe.set_user(self.user)
|
||||
self.assertTrue(self.event.has_permission("share"))
|
||||
|
||||
# test cascade
|
||||
self.assertTrue(self.event.has_permission("read"))
|
||||
self.assertTrue(self.event.has_permission("write"))
|
||||
|
||||
def test_set_permission(self):
|
||||
frappe.share.add("Event", self.event.name, self.user)
|
||||
|
||||
frappe.set_user(self.user)
|
||||
self.assertFalse(self.event.has_permission("share"))
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
frappe.share.set_permission("Event", self.event.name, self.user, "share")
|
||||
|
||||
frappe.set_user(self.user)
|
||||
self.assertTrue(self.event.has_permission("share"))
|
||||
|
||||
def test_permission_to_share(self):
|
||||
frappe.set_user(self.user)
|
||||
self.assertRaises(frappe.PermissionError, frappe.share.add, "Event", self.event.name, self.user)
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
frappe.share.add("Event", self.event.name, self.user, share=1)
|
||||
|
||||
# test not raises
|
||||
frappe.set_user(self.user)
|
||||
frappe.share.add("Event", self.event.name, "test1@example.com", share=1)
|
||||
|
||||
|
||||
1
frappe/core/doctype/docshare/test_records.json
Normal file
1
frappe/core/doctype/docshare/test_records.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -14,7 +14,7 @@ from frappe.model.document import Document
|
|||
from frappe.utils.file_manager import delete_file_data_content
|
||||
|
||||
class FileData(Document):
|
||||
ignore_feed = True
|
||||
no_feed_on_delete = True
|
||||
|
||||
def before_insert(self):
|
||||
frappe.local.rollback_observers.append(self)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ CREATE TABLE `tabDocPerm` (
|
|||
`report` int(1) DEFAULT NULL,
|
||||
`export` int(1) DEFAULT NULL,
|
||||
`import` int(1) DEFAULT NULL,
|
||||
`share` int(1) DEFAULT NULL,
|
||||
`print` int(1) DEFAULT NULL,
|
||||
`email` int(1) DEFAULT NULL,
|
||||
`restrict` int(1) DEFAULT NULL,
|
||||
|
|
|
|||
|
|
@ -598,6 +598,24 @@ class Database:
|
|||
else:
|
||||
return frappe.defaults.get_defaults(parent)
|
||||
|
||||
def get_shared(self, doctype, user=None, rights=None):
|
||||
"""Get list of shared document names for given user and DocType.
|
||||
|
||||
:param doctype: DocType of which shared names are queried.
|
||||
:param user: User for which shared names are queried.
|
||||
:param rights: List of rights for which the document is shared. List of `read`, `write`, `share`"""
|
||||
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
if not rights:
|
||||
rights = ["read"]
|
||||
|
||||
condition = " and ".join(["`{0}`=1".format(right) for right in rights])
|
||||
|
||||
return self.sql_list("select share_name from tabDocShare where user=%s and share_doctype=%s and {0}".format(condition),
|
||||
(user, doctype))
|
||||
|
||||
def begin(self):
|
||||
return # not required
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,35 @@ from frappe import _
|
|||
from frappe.utils import cint, flt, now, cstr, strip_html
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.naming import set_new_name
|
||||
from frappe.modules import load_doctype_module
|
||||
|
||||
_classes = {}
|
||||
|
||||
def get_controller(doctype):
|
||||
"""Returns the **class** object of the given DocType.
|
||||
For `custom` type, returns `frappe.model.document.Document`.
|
||||
|
||||
:param doctype: DocType name as string."""
|
||||
if not doctype in _classes:
|
||||
module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \
|
||||
or ["Core", False]
|
||||
|
||||
if custom:
|
||||
_class = BaseDocument
|
||||
else:
|
||||
module = load_doctype_module(doctype, module_name)
|
||||
classname = doctype.replace(" ", "").replace("-", "")
|
||||
if hasattr(module, classname):
|
||||
_class = getattr(module, classname)
|
||||
if issubclass(_class, BaseDocument):
|
||||
_class = getattr(module, classname)
|
||||
else:
|
||||
raise ImportError, doctype
|
||||
else:
|
||||
raise ImportError, doctype
|
||||
_classes[doctype] = _class
|
||||
|
||||
return _classes[doctype]
|
||||
|
||||
class BaseDocument(object):
|
||||
ignore_in_getter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns")
|
||||
|
|
@ -15,6 +44,9 @@ class BaseDocument(object):
|
|||
self.update(d)
|
||||
self.dont_update_if_missing = []
|
||||
|
||||
if hasattr(self, "__setup__"):
|
||||
self.__setup__()
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
if not hasattr(self, "_meta"):
|
||||
|
|
@ -95,6 +127,9 @@ class BaseDocument(object):
|
|||
self.__dict__[key] = []
|
||||
value = self._init_child(value, key)
|
||||
self.__dict__[key].append(value)
|
||||
|
||||
# reference parent document
|
||||
value.parent_doc = self
|
||||
return value
|
||||
else:
|
||||
raise ValueError, "Document attached to child table must be a dict or BaseDocument, not " + str(type(value))[1:-1]
|
||||
|
|
@ -117,7 +152,7 @@ class BaseDocument(object):
|
|||
value["doctype"] = self.get_table_field_doctype(key)
|
||||
if not value["doctype"]:
|
||||
raise AttributeError, key
|
||||
value = BaseDocument(value)
|
||||
value = get_controller(value["doctype"])(value)
|
||||
value.init_valid_columns()
|
||||
|
||||
value.parent = self.name
|
||||
|
|
@ -364,28 +399,30 @@ class BaseDocument(object):
|
|||
return format_value(self.get(fieldname), self.meta.get_field(fieldname),
|
||||
doc=doc or self, currency=currency)
|
||||
|
||||
def get_print_template(self, fieldname, parent_doc=None):
|
||||
"""Returns print template for given fieldname if specified in controller
|
||||
or parent controller.
|
||||
def is_print_hide(self, fieldname, for_print=True):
|
||||
"""Returns true if fieldname is to be hidden for print.
|
||||
|
||||
Templates must be specified as:
|
||||
Print Hide can be set via the Print Format Builder or in the controller as a list
|
||||
of hidden fields. Example
|
||||
|
||||
class MyDocType(Document):
|
||||
class MyDoc(Document):
|
||||
def __setup__(self):
|
||||
self.print_templates = {
|
||||
"[fieldname]": "templates/includes/template_name.html",
|
||||
"[table fieldname].[fieldname]": "templates/includes/template_name.html"
|
||||
}
|
||||
self.print_hide = ["field1", "field2"]
|
||||
|
||||
:param fieldname: Field for which template is queried.
|
||||
:param parent_doc: Parent Document, if child doc."""
|
||||
src = self
|
||||
if parent_doc:
|
||||
src = parent_doc
|
||||
fieldname = self.parentfield + "." + fieldname
|
||||
if hasattr(src, "print_templates"):
|
||||
return src.print_templates.get(fieldname)
|
||||
:param fieldname: Fieldname to be checked if hidden.
|
||||
"""
|
||||
df = self.meta.get_field(fieldname)
|
||||
return df and (df.get("__print_hide") or df.print_hide)
|
||||
|
||||
def in_format_data(self, fieldname):
|
||||
"""Returns True if shown via Print Format::`format_data` property.
|
||||
Called from within standard print format."""
|
||||
doc = getattr(self, "parent_doc", self)
|
||||
|
||||
if hasattr(doc, "format_data_map"):
|
||||
return fieldname in doc.format_data_map
|
||||
else:
|
||||
return True
|
||||
|
||||
def _filter(data, filters, limit=None):
|
||||
"""pass filters as:
|
||||
|
|
|
|||
|
|
@ -240,19 +240,36 @@ class DatabaseQuery(object):
|
|||
self.match_filters = []
|
||||
self.match_conditions = []
|
||||
|
||||
only_if_shared = False
|
||||
|
||||
if not self.tables: self.extract_tables()
|
||||
|
||||
meta = frappe.get_meta(self.doctype)
|
||||
role_permissions = frappe.permissions.get_role_permissions(meta, user=self.user)
|
||||
if not meta.istable and not role_permissions.get("read") and not getattr(self, "ignore_permissions", False):
|
||||
frappe.throw(_("No permission to read {0}").format(self.doctype))
|
||||
|
||||
# apply user permissions?
|
||||
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,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes"))
|
||||
self.shared = frappe.db.get_shared(self.doctype, self.user)
|
||||
|
||||
if not meta.istable and not role_permissions.get("read") and not getattr(self,
|
||||
"ignore_permissions", False):
|
||||
only_if_shared = True
|
||||
if not self.shared:
|
||||
frappe.throw(_("No permission to read {0}").format(self.doctype))
|
||||
else:
|
||||
self.conditions.append(self.get_share_condition())
|
||||
|
||||
else:
|
||||
# share is an OR condition, if there is a role permission
|
||||
if not only_if_shared and self.shared:
|
||||
self.or_conditions.append(self.get_share_condition())
|
||||
|
||||
# apply user permissions?
|
||||
if not 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,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes"))
|
||||
|
||||
|
||||
|
||||
if as_condition:
|
||||
conditions = ""
|
||||
|
|
@ -269,6 +286,10 @@ class DatabaseQuery(object):
|
|||
else:
|
||||
return self.match_filters
|
||||
|
||||
def get_share_condition(self):
|
||||
return """`tab{0}`.name in ({1})""".format(self.doctype, ", ".join(["%s"] * len(self.shared))) % \
|
||||
[frappe.db.escape(s) for s in self.shared]
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa
|
|||
doc.run_method("on_trash")
|
||||
|
||||
delete_linked_todos(doc)
|
||||
delete_shared(doc)
|
||||
# check if links exist
|
||||
if not force:
|
||||
check_if_doc_is_linked(doc)
|
||||
|
|
@ -166,7 +167,7 @@ def delete_linked_todos(doc):
|
|||
def insert_feed(doc):
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
if frappe.flags.in_install or frappe.flags.in_import or doc.get("ignore_feed"):
|
||||
if frappe.flags.in_install or frappe.flags.in_import or getattr(doc, "no_feed_on_delete", False):
|
||||
return
|
||||
|
||||
frappe.get_doc({
|
||||
|
|
@ -177,3 +178,7 @@ def insert_feed(doc):
|
|||
"subject": _("Deleted"),
|
||||
"full_name": get_fullname(doc.owner)
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def delete_shared(doc):
|
||||
delete_doc("DocShare", frappe.db.sql_list("""select name from `tabDocShare`
|
||||
where share_doctype=%s and share_name=%s""", (doc.doctype, doc.name)))
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt, cint, cstr, now
|
||||
from frappe.modules import load_doctype_module
|
||||
from frappe.model.base_document import BaseDocument
|
||||
from frappe.model.base_document import BaseDocument, get_controller
|
||||
from frappe.model.naming import set_new_name
|
||||
from werkzeug.exceptions import NotFound, Forbidden
|
||||
import hashlib
|
||||
|
|
@ -47,34 +46,6 @@ def get_doc(arg1, arg2=None):
|
|||
|
||||
raise ImportError, arg1
|
||||
|
||||
_classes = {}
|
||||
|
||||
def get_controller(doctype):
|
||||
"""Returns the **class** object of the given DocType.
|
||||
For `custom` type, returns `frappe.model.document.Document`.
|
||||
|
||||
:param doctype: DocType name as string."""
|
||||
if not doctype in _classes:
|
||||
module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \
|
||||
or ["Core", False]
|
||||
|
||||
if custom:
|
||||
_class = Document
|
||||
else:
|
||||
module = load_doctype_module(doctype, module_name)
|
||||
classname = doctype.replace(" ", "").replace("-", "")
|
||||
if hasattr(module, classname):
|
||||
_class = getattr(module, classname)
|
||||
if issubclass(_class, Document):
|
||||
_class = getattr(module, classname)
|
||||
else:
|
||||
raise ImportError, doctype
|
||||
else:
|
||||
raise ImportError, doctype
|
||||
_classes[doctype] = _class
|
||||
|
||||
return _classes[doctype]
|
||||
|
||||
class Document(BaseDocument):
|
||||
"""All controllers inherit from `Document`."""
|
||||
def __init__(self, arg1, arg2=None):
|
||||
|
|
@ -112,9 +83,6 @@ class Document(BaseDocument):
|
|||
# incorrect arguments. let's not proceed.
|
||||
raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))
|
||||
|
||||
if hasattr(self, "__setup__"):
|
||||
self.__setup__()
|
||||
|
||||
self.dont_update_if_missing = []
|
||||
|
||||
def load_from_db(self):
|
||||
|
|
@ -157,7 +125,7 @@ class Document(BaseDocument):
|
|||
if not self.has_permission(permtype):
|
||||
self.raise_no_permission_to(permlabel or permtype)
|
||||
|
||||
def has_permission(self, permtype):
|
||||
def has_permission(self, permtype="read"):
|
||||
"""Call `frappe.has_permission` if `self.ignore_permissions`
|
||||
is not set.
|
||||
|
||||
|
|
|
|||
|
|
@ -211,10 +211,6 @@ class Meta(Document):
|
|||
|
||||
return fields
|
||||
|
||||
def is_print_hide(self, fieldname):
|
||||
df = self.get_field(fieldname)
|
||||
return df and (df.get("__print_hide") or df.print_hide)
|
||||
|
||||
doctype_table_fields = [
|
||||
frappe._dict({"fieldname": "fields", "options": "DocField"}),
|
||||
frappe._dict({"fieldname": "permissions", "options": "DocPerm"})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from frappe import _, msgprint
|
|||
from frappe.utils import cint
|
||||
|
||||
rights = ("read", "write", "create", "delete", "submit", "cancel", "amend",
|
||||
"print", "email", "report", "import", "export", "set_user_permissions")
|
||||
"print", "email", "report", "import", "export", "set_user_permissions", "share")
|
||||
|
||||
def check_admin_or_system_manager(user=None):
|
||||
if not user: user = frappe.session.user
|
||||
|
|
@ -33,9 +33,17 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None):
|
|||
if user=="Administrator":
|
||||
return True
|
||||
|
||||
def false_if_not_shared():
|
||||
if doc and ptype in ("read", "write", "share"):
|
||||
shared = frappe.db.get_shared(meta.name, user, [ptype])
|
||||
if doc.name in shared:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
role_permissions = get_role_permissions(meta, user=user)
|
||||
if not role_permissions.get(ptype):
|
||||
return False
|
||||
return false_if_not_shared()
|
||||
|
||||
if doc:
|
||||
if isinstance(doc, basestring):
|
||||
|
|
@ -44,10 +52,10 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None):
|
|||
if role_permissions["apply_user_permissions"].get(ptype):
|
||||
if not user_has_permission(doc, verbose=verbose, user=user,
|
||||
user_permission_doctypes=role_permissions.get("user_permission_doctypes")):
|
||||
return False
|
||||
return false_if_not_shared()
|
||||
|
||||
if not has_controller_permissions(doc, ptype, user=user):
|
||||
return False
|
||||
return false_if_not_shared()
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -74,6 +82,11 @@ def get_doc_permissions(doc, verbose=False, user=None):
|
|||
if role_permissions["apply_user_permissions"].get(ptype):
|
||||
role_permissions[ptype] = 0
|
||||
|
||||
# update share permissions
|
||||
role_permissions.update(frappe.db.get_value("DocShare",
|
||||
{"share_type": doc.doctype, "share_name": doc.name, "user": user},
|
||||
["read", "write", "share"], as_dict=True))
|
||||
|
||||
return role_permissions
|
||||
|
||||
def get_role_permissions(meta, user=None):
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ a,
|
|||
.badge,
|
||||
.btn,
|
||||
.ui-menu .ui-menu-item {
|
||||
transition: 0.2s;
|
||||
-webkit-transition: 0.2s;
|
||||
transition: background-color 0.2s;
|
||||
-webkit-transition: background-color 0.2s;
|
||||
}
|
||||
a.disabled,
|
||||
a.disabled:hover {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ frappe.ui.form.PrintPreview = Class.extend({
|
|||
},
|
||||
preview: function() {
|
||||
var me = this;
|
||||
this.wrapper.find(".btn-print-edit").toggle(this.get_print_format().name);
|
||||
this.get_print_html(function(html) {
|
||||
me.wrapper.find(".print-format").html(html);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -217,7 +217,9 @@ _f.Frm.prototype.set_value = function(field, value, if_missing) {
|
|||
_set(field, value)
|
||||
} else if($.isPlainObject(field)) {
|
||||
$.each(field, function(f, v) {
|
||||
_set(f, v);
|
||||
if(me.get_field(f)) {
|
||||
_set(f, v);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ a,
|
|||
.badge,
|
||||
.btn,
|
||||
.ui-menu .ui-menu-item {
|
||||
transition: 0.2s;
|
||||
-webkit-transition: 0.2s;
|
||||
transition: background-color 0.2s;
|
||||
-webkit-transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
a.disabled, a.disabled:hover {
|
||||
|
|
|
|||
42
frappe/share.py
Normal file
42
frappe/share.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def add(doctype, name, user=None, read=1, write=0, share=0):
|
||||
"""Share the given document with a user."""
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
return frappe.get_doc({
|
||||
"doctype": "DocShare",
|
||||
"user": user,
|
||||
"share_doctype": doctype,
|
||||
"share_name": name,
|
||||
"read": read,
|
||||
"write": write,
|
||||
"share": share
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def set_permission(doctype, name, user, permission_to, remove=False):
|
||||
"""Set share right."""
|
||||
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
|
||||
"share_doctype": doctype})
|
||||
if not share_name:
|
||||
if not remove:
|
||||
share = add(doctype, name, user, **{permission_to: 1})
|
||||
else:
|
||||
# no share found, nothing to remove
|
||||
share = {}
|
||||
pass
|
||||
else:
|
||||
share = frappe.get_doc("DocShare", share_name)
|
||||
share.set(permission_to, 0 if remove else 1)
|
||||
share.save()
|
||||
|
||||
if not (share.read or share.write or share.share):
|
||||
share.delete()
|
||||
share = {}
|
||||
|
||||
return share
|
||||
|
|
@ -32,22 +32,13 @@
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
padding: 6px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
input#login_email, input#signup_fullname {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
input#login_password, input#signup_email {
|
||||
margin-bottom: 10px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.btn-social {
|
||||
margin: 10px;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ def get_html(doc, name=None, print_format=None, meta=None,
|
|||
meta = frappe.get_meta(doc.doctype)
|
||||
|
||||
jenv = frappe.get_jenv()
|
||||
format_data = {}
|
||||
format_data, format_data_map = [], {}
|
||||
|
||||
# determine template
|
||||
if print_format in ("Standard", standard_format):
|
||||
|
|
@ -76,7 +76,16 @@ def get_html(doc, name=None, print_format=None, meta=None,
|
|||
else:
|
||||
print_format = frappe.get_doc("Print Format", print_format)
|
||||
if print_format.format_data:
|
||||
# set format data
|
||||
format_data = json.loads(print_format.format_data)
|
||||
for df in format_data:
|
||||
format_data_map[df.get("fieldname")] = df
|
||||
if "visible_columns" in df:
|
||||
for _df in df.get("visible_columns"):
|
||||
format_data_map[_df.get("fieldname")] = _df
|
||||
|
||||
doc.format_data_map = format_data_map
|
||||
|
||||
template = "standard"
|
||||
else:
|
||||
template = jenv.from_string(get_print_format(doc.doctype,
|
||||
|
|
@ -180,7 +189,7 @@ def make_layout(doc, meta, format_data=None):
|
|||
if df.fieldtype=="HTML" and df.options:
|
||||
doc.set(df.fieldname, True) # show this field
|
||||
|
||||
if is_visible(df) and has_value(df, doc):
|
||||
if is_visible(df, doc) and has_value(df, doc):
|
||||
page[-1][-1].append(df)
|
||||
|
||||
# if table, add the row info in the field
|
||||
|
|
@ -208,9 +217,16 @@ def make_layout(doc, meta, format_data=None):
|
|||
layout = [filter(lambda s: any(filter(lambda c: any(c), s)), page) for page in layout]
|
||||
return layout
|
||||
|
||||
def is_visible(df):
|
||||
no_display = ("Section Break", "Column Break", "Button")
|
||||
return (df.fieldtype not in no_display) and not df.get("__print_hide") and not df.print_hide
|
||||
def is_visible(df, doc):
|
||||
"""Returns True if docfield is visible in print layout and does not have print_hide set."""
|
||||
if df.fieldtype in ("Section Break", "Column Break", "Button"):
|
||||
return False
|
||||
|
||||
if hasattr(doc, "hide_in_print_layout"):
|
||||
if df.fieldname in doc.hide_in_print_layout:
|
||||
return False
|
||||
|
||||
return not doc.is_print_hide(df.fieldname)
|
||||
|
||||
def has_value(df, doc):
|
||||
value = doc.get(df.fieldname)
|
||||
|
|
@ -251,15 +267,22 @@ def get_print_style(style=None):
|
|||
def get_visible_columns(data, table_meta, df):
|
||||
"""Returns list of visible columns based on print_hide and if all columns have value."""
|
||||
columns = []
|
||||
doc = data[0] or frappe.new_doc(df.options)
|
||||
def add_column(col_df):
|
||||
return is_visible(col_df, doc) \
|
||||
and column_has_value(data, col_df.get("fieldname"))
|
||||
|
||||
if df.get("visible_columns"):
|
||||
# columns specified by column builder
|
||||
for col_df in df.get("visible_columns"):
|
||||
# load default docfield properties
|
||||
newdf = table_meta.get_field(col_df.get("fieldname")).as_dict().copy()
|
||||
newdf.update(col_df)
|
||||
columns.append(newdf)
|
||||
if add_column(newdf):
|
||||
columns.append(newdf)
|
||||
else:
|
||||
for col_df in table_meta.fields:
|
||||
if is_visible(col_df) and column_has_value(data, col_df.get("fieldname")):
|
||||
if add_column(col_df):
|
||||
columns.append(col_df)
|
||||
|
||||
return columns
|
||||
|
|
|
|||
|
|
@ -76,9 +76,10 @@
|
|||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro print_value(df, doc, parent_doc=None) -%}
|
||||
{% if doc.get_print_template(df.fieldname, parent_doc) %}
|
||||
{% include doc.get_print_template(df.fieldname, parent_doc) %}
|
||||
{%- macro print_value(df, doc, meta, parent_doc=None) -%}
|
||||
{% if doc.print_templates and
|
||||
doc.print_templates.get(df.fieldname) %}
|
||||
{% include doc.print_templates[df.fieldname] %}
|
||||
{% elif df.fieldtype=="Check" %}
|
||||
<i class="{{ 'icon-check' if doc[df.fieldname] else 'icon-check-empty' }}"></i>
|
||||
{% elif df.fieldtype=="Image" %}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue