@@ -108,6 +108,7 @@ def init(site, sites_path=None): | |||||
local.user = None | local.user = None | ||||
local.role_permissions = {} | local.role_permissions = {} | ||||
local.valid_columns = {} | local.valid_columns = {} | ||||
local.new_doc_templates = {} | |||||
local.jenv = None | local.jenv = None | ||||
local.jloader =None | local.jloader =None | ||||
@@ -429,14 +430,14 @@ def reset_metadata_version(): | |||||
cache().set_value("metadata_version", v) | cache().set_value("metadata_version", v) | ||||
return v | return v | ||||
def new_doc(doctype, parent_doc=None, parentfield=None): | |||||
def new_doc(doctype, parent_doc=None, parentfield=None, as_dict=False): | |||||
"""Returns a new document of the given DocType with defaults set. | """Returns a new document of the given DocType with defaults set. | ||||
:param doctype: DocType of the new document. | :param doctype: DocType of the new document. | ||||
:param parent_doc: [optional] add to parent document. | :param parent_doc: [optional] add to parent document. | ||||
:param parentfield: [optional] add against this `parentfield`.""" | :param parentfield: [optional] add against this `parentfield`.""" | ||||
from frappe.model.create_new import get_new_doc | from frappe.model.create_new import get_new_doc | ||||
return get_new_doc(doctype, parent_doc, parentfield) | |||||
return get_new_doc(doctype, parent_doc, parentfield, as_dict=as_dict) | |||||
def set_value(doctype, docname, fieldname, value): | def set_value(doctype, docname, fieldname, value): | ||||
"""Set document value. Calls `frappe.client.set_value`""" | """Set document value. Calls `frappe.client.set_value`""" | ||||
@@ -1,52 +1,60 @@ | |||||
[ | [ | ||||
{ | { | ||||
"doctype": "User", | |||||
"email": "test@example.com", | |||||
"enabled": 1, | |||||
"first_name": "_Test", | |||||
"new_password": "testpassword", | |||||
"doctype": "User", | |||||
"email": "test@example.com", | |||||
"enabled": 1, | |||||
"first_name": "_Test", | |||||
"new_password": "testpassword", | |||||
"user_roles": [ | "user_roles": [ | ||||
{ | { | ||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"role": "_Test Role" | "role": "_Test Role" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"role": "System Manager" | "role": "System Manager" | ||||
} | } | ||||
] | ] | ||||
}, | |||||
}, | |||||
{ | { | ||||
"doctype": "User", | |||||
"email": "test1@example.com", | |||||
"first_name": "_Test1", | |||||
"doctype": "User", | |||||
"email": "test1@example.com", | |||||
"first_name": "_Test1", | |||||
"new_password": "testpassword" | "new_password": "testpassword" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"doctype": "User", | |||||
"email": "test2@example.com", | |||||
"first_name": "_Test2", | |||||
"new_password": "testpassword" | |||||
}, | |||||
"doctype": "User", | |||||
"email": "test2@example.com", | |||||
"first_name": "_Test2", | |||||
"new_password": "testpassword", | |||||
"enabled": 1 | |||||
}, | |||||
{ | |||||
"doctype": "User", | |||||
"email": "testperm@example.com", | |||||
"first_name": "_Test Perm", | |||||
"new_password": "testpassword", | |||||
"enabled": 1 | |||||
}, | |||||
{ | { | ||||
"doctype": "User", | |||||
"email": "testdelete@example.com", | |||||
"enabled": 1, | |||||
"first_name": "_Test", | |||||
"new_password": "testpassword", | |||||
"doctype": "User", | |||||
"email": "testdelete@example.com", | |||||
"enabled": 1, | |||||
"first_name": "_Test", | |||||
"new_password": "testpassword", | |||||
"user_roles": [ | "user_roles": [ | ||||
{ | { | ||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"role": "_Test Role 2" | "role": "_Test Role 2" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"doctype": "UserRole", | |||||
"parentfield": "user_roles", | |||||
"role": "System Manager" | "role": "System Manager" | ||||
} | } | ||||
] | ] | ||||
} | } | ||||
] | |||||
] |
@@ -15,6 +15,9 @@ class TestUser(unittest.TestCase): | |||||
frappe.db.sql("""delete from tabUserRole where role='_Test Role 2'""") | frappe.db.sql("""delete from tabUserRole where role='_Test Role 2'""") | ||||
delete_doc("Role","_Test Role 2") | delete_doc("Role","_Test Role 2") | ||||
if frappe.db.exists("User", "_test@example.com"): | |||||
delete_doc("User", "_test@example.com") | |||||
user = frappe.copy_doc(test_records[1]) | user = frappe.copy_doc(test_records[1]) | ||||
user.email = "_test@example.com" | user.email = "_test@example.com" | ||||
user.insert() | user.insert() | ||||
@@ -50,3 +53,21 @@ class TestUser(unittest.TestCase): | |||||
frappe.db.set_value("Website Settings", "Website Settings", "_test", "_test_val") | frappe.db.set_value("Website Settings", "Website Settings", "_test", "_test_val") | ||||
self.assertEquals(frappe.db.get_value("Website Settings", None, "_test"), "_test_val") | self.assertEquals(frappe.db.get_value("Website Settings", None, "_test"), "_test_val") | ||||
self.assertEquals(frappe.db.get_value("Website Settings", "Website Settings", "_test"), "_test_val") | self.assertEquals(frappe.db.get_value("Website Settings", "Website Settings", "_test"), "_test_val") | ||||
def test_high_permlevel_validations(self): | |||||
user = frappe.get_meta("User") | |||||
self.assertTrue("user_roles" in [d.fieldname for d in user.get_high_permlevel_fields()]) | |||||
frappe.set_user("testperm@example.com") | |||||
me = frappe.get_doc("User", "testperm@example.com") | |||||
me.add_roles("System Manager") | |||||
self.assertTrue("System Manager" not in [d.role for d in me.get("user_roles")]) | |||||
frappe.set_user("Administrator") | |||||
me = frappe.get_doc("User", "testperm@example.com") | |||||
me.add_roles("System Manager") | |||||
self.assertTrue("System Manager" in [d.role for d in me.get("user_roles")]) |
@@ -2,12 +2,13 @@ | |||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe, json, sys | |||||
import frappe, sys | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.utils import cint, flt, now, cstr, strip_html | from frappe.utils import cint, flt, now, cstr, strip_html | ||||
from frappe.model import default_fields | from frappe.model import default_fields | ||||
from frappe.model.naming import set_new_name | from frappe.model.naming import set_new_name | ||||
from frappe.modules import load_doctype_module | from frappe.modules import load_doctype_module | ||||
from frappe.model import display_fieldtypes | |||||
_classes = {} | _classes = {} | ||||
@@ -471,6 +472,29 @@ class BaseDocument(object): | |||||
else: | else: | ||||
return True | return True | ||||
def reset_values_if_no_permlevel_access(self, has_access_to, high_permlevel_fields): | |||||
"""If the user does not have permissions at permlevel > 0, then reset the values to original / default""" | |||||
to_reset = [] | |||||
for df in high_permlevel_fields: | |||||
if df.permlevel not in has_access_to and df.fieldtype not in display_fieldtypes: | |||||
to_reset.append(df) | |||||
if to_reset: | |||||
if self.is_new(): | |||||
# if new, set default value | |||||
ref_doc = frappe.new_doc(self.doctype) | |||||
else: | |||||
# get values from old doc | |||||
if self.parent: | |||||
self.parent_doc.get_latest() | |||||
ref_doc = [d for d in self.parent_doc.get(self.parentfield) if d.name == self.name][0] | |||||
else: | |||||
ref_doc = self.get_latest() | |||||
for df in to_reset: | |||||
self.set(df.fieldname, ref_doc.get(df.fieldname)) | |||||
def _filter(data, filters, limit=None): | def _filter(data, filters, limit=None): | ||||
"""pass filters as: | """pass filters as: | ||||
{"key": "val", "key": ["!=", "val"], | {"key": "val", "key": ["!=", "val"], | ||||
@@ -7,37 +7,51 @@ Create a new document with defaults set | |||||
""" | """ | ||||
import frappe | import frappe | ||||
from frappe.utils import nowdate, nowtime, cint, flt, now_datetime | |||||
from frappe.utils import nowdate, nowtime, now_datetime | |||||
import frappe.defaults | import frappe.defaults | ||||
from frappe.model.db_schema import type_map | from frappe.model.db_schema import type_map | ||||
import copy | |||||
def get_new_doc(doctype, parent_doc = None, parentfield = None): | |||||
doc = frappe.get_doc({ | |||||
"doctype": doctype, | |||||
"__islocal": 1, | |||||
"owner": frappe.session.user, | |||||
"docstatus": 0 | |||||
}) | |||||
def get_new_doc(doctype, parent_doc = None, parentfield = None, as_dict=False): | |||||
if not doctype in frappe.local.new_doc_templates: | |||||
# cache a copy of new doc as it is called | |||||
# frequently for inserts | |||||
doc = frappe.get_doc({ | |||||
"doctype": doctype, | |||||
"__islocal": 1, | |||||
"owner": frappe.session.user, | |||||
"docstatus": 0 | |||||
}) | |||||
user_permissions = frappe.defaults.get_user_permissions() | |||||
user_permissions = frappe.defaults.get_user_permissions() | |||||
if parent_doc: | |||||
doc.parent = parent_doc.name | |||||
doc.parenttype = parent_doc.doctype | |||||
defaults = frappe.defaults.get_defaults() | |||||
if parentfield: | |||||
doc.parentfield = parentfield | |||||
for df in doc.meta.get("fields"): | |||||
if df.fieldtype in type_map: | |||||
default_value = get_default_value(df, defaults, user_permissions, parent_doc) | |||||
doc.set(df.fieldname, default_value) | |||||
doc._fix_numeric_types() | |||||
doc = doc.get_valid_dict() | |||||
doc["doctype"] = doctype | |||||
doc["__islocal"] = 1 | |||||
defaults = frappe.defaults.get_defaults() | |||||
frappe.local.new_doc_templates[doctype] = doc | |||||
for df in doc.meta.get("fields"): | |||||
if df.fieldtype in type_map: | |||||
default_value = get_default_value(df, defaults, user_permissions, parent_doc) | |||||
doc.set(df.fieldname, default_value) | |||||
doc = copy.deepcopy(frappe.local.new_doc_templates[doctype]) | |||||
doc._fix_numeric_types() | |||||
if parent_doc: | |||||
doc["parent"] = parent_doc.name | |||||
doc["parenttype"] = parent_doc.doctype | |||||
if parentfield: | |||||
doc["parentfield"] = parentfield | |||||
return doc | |||||
if as_dict: | |||||
return doc | |||||
else: | |||||
return frappe.get_doc(doc) | |||||
def get_default_value(df, defaults, user_permissions, parent_doc): | def get_default_value(df, defaults, user_permissions, parent_doc): | ||||
user_permissions_exist = (df.fieldtype=="Link" | user_permissions_exist = (df.fieldtype=="Link" | ||||
@@ -8,7 +8,6 @@ from frappe.utils import flt, cint, cstr, now, get_datetime_str | |||||
from frappe.model.base_document import BaseDocument, get_controller | from frappe.model.base_document import BaseDocument, get_controller | ||||
from frappe.model.naming import set_new_name | from frappe.model.naming import set_new_name | ||||
from werkzeug.exceptions import NotFound, Forbidden | from werkzeug.exceptions import NotFound, Forbidden | ||||
from frappe.model import display_fieldtypes | |||||
import hashlib, json | import hashlib, json | ||||
# once_only validation | # once_only validation | ||||
@@ -84,7 +83,7 @@ class Document(BaseDocument): | |||||
# incorrect arguments. let's not proceed. | # incorrect arguments. let's not proceed. | ||||
raise frappe.DataError("Document({0}, {1})".format(arg1, arg2)) | raise frappe.DataError("Document({0}, {1})".format(arg1, arg2)) | ||||
self.dont_update_if_missing = [] | |||||
self._default_new_docs = {} | |||||
self.flags = frappe._dict() | self.flags = frappe._dict() | ||||
def load_from_db(self): | def load_from_db(self): | ||||
@@ -122,6 +121,11 @@ class Document(BaseDocument): | |||||
else: | else: | ||||
self.set(df.fieldname, []) | self.set(df.fieldname, []) | ||||
def get_latest(self): | |||||
if not getattr(self, "latest", None): | |||||
self.latest = frappe.get_doc(self.doctype, self.name) | |||||
return self.latest | |||||
def check_permission(self, permtype, permlabel=None): | def check_permission(self, permtype, permlabel=None): | ||||
"""Raise `frappe.PermissionError` if not permitted""" | """Raise `frappe.PermissionError` if not permitted""" | ||||
if not self.has_permission(permtype): | if not self.has_permission(permtype): | ||||
@@ -301,37 +305,18 @@ class Document(BaseDocument): | |||||
if self.flags.ignore_permissions or frappe.flags.in_install: | if self.flags.ignore_permissions or frappe.flags.in_install: | ||||
return | return | ||||
self.get_high_permlevel_fields() | |||||
if not self.high_permlevel_fields: | |||||
return | |||||
has_access_to = self.get_permlevel_access() | has_access_to = self.get_permlevel_access() | ||||
to_reset = [] | |||||
for df in self.high_permlevel_fields: | |||||
if df.permlevel not in has_access_to and df.fieldtype not in display_fieldtypes: | |||||
to_reset.append(df) | |||||
high_permlevel_fields = self.meta.get_high_permlevel_fields() | |||||
if to_reset: | |||||
if self.is_new(): | |||||
# if new, set default value | |||||
for df in to_reset: | |||||
self.set(df.fieldname, df.default or None) | |||||
if high_permlevel_fields: | |||||
self.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields) | |||||
else: | |||||
# get values from old doc | |||||
old = frappe.get_doc(self.doctype, self.name) | |||||
for df in to_reset: | |||||
self.set(df.fieldname, old.get(df.fieldname)) | |||||
def get_high_permlevel_fields(self): | |||||
"""Build list of fields with high perm level and all the higher perm levels defined.""" | |||||
self.high_permlevel_fields = [] | |||||
self.high_permlevels = [] | |||||
for df in self.meta.fields: | |||||
if df.permlevel > 0: | |||||
self.high_permlevel_fields.append(df) | |||||
if not df.permlevel in self.high_permlevels: | |||||
self.high_permlevels.append(df.permlevel) | |||||
# check for child tables | |||||
for df in self.meta.get_table_fields(): | |||||
high_permlevel_fields = frappe.get_meta(df.options).meta.get_high_permlevel_fields() | |||||
if high_permlevel_fields: | |||||
for d in self.get(df.fieldname): | |||||
d.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields) | |||||
def get_permlevel_access(self): | def get_permlevel_access(self): | ||||
user_roles = frappe.get_roles() | user_roles = frappe.get_roles() | ||||
@@ -347,12 +332,12 @@ class Document(BaseDocument): | |||||
if frappe.flags.in_import: | if frappe.flags.in_import: | ||||
return | return | ||||
new_doc = frappe.new_doc(self.doctype) | |||||
new_doc = frappe.new_doc(self.doctype, as_dict=True) | |||||
self.update_if_missing(new_doc) | self.update_if_missing(new_doc) | ||||
# children | # children | ||||
for df in self.meta.get_table_fields(): | for df in self.meta.get_table_fields(): | ||||
new_doc = frappe.new_doc(df.options) | |||||
new_doc = frappe.new_doc(df.options, as_dict=True) | |||||
value = self.get(df.fieldname) | value = self.get(df.fieldname) | ||||
if isinstance(value, list): | if isinstance(value, list): | ||||
for d in value: | for d in value: | ||||
@@ -578,7 +563,7 @@ class Document(BaseDocument): | |||||
self.run_method("on_update_after_submit") | self.run_method("on_update_after_submit") | ||||
frappe.cache().set_value("last_modified:" + self.doctype, self.modified) | frappe.cache().set_value("last_modified:" + self.doctype, self.modified) | ||||
self.latest = None | |||||
def check_no_back_links_exist(self): | def check_no_back_links_exist(self): | ||||
"""Check if document links to any active document before Cancel.""" | """Check if document links to any active document before Cancel.""" | ||||
@@ -213,6 +213,16 @@ class Meta(Document): | |||||
return fields | return fields | ||||
def get_high_permlevel_fields(self): | |||||
"""Build list of fields with high perm level and all the higher perm levels defined.""" | |||||
if not hasattr(self, "high_permlevel_fields"): | |||||
self.high_permlevel_fields = [] | |||||
for df in self.fields: | |||||
if df.permlevel > 0: | |||||
self.high_permlevel_fields.append(df) | |||||
return self.high_permlevel_fields | |||||
doctype_table_fields = [ | doctype_table_fields = [ | ||||
frappe._dict({"fieldname": "fields", "options": "DocField"}), | frappe._dict({"fieldname": "fields", "options": "DocField"}), | ||||
frappe._dict({"fieldname": "permissions", "options": "DocPerm"}) | frappe._dict({"fieldname": "permissions", "options": "DocPerm"}) | ||||
@@ -193,14 +193,14 @@ def make_test_records_for_doctype(doctype, verbose=0, force=False): | |||||
def make_test_objects(doctype, test_records, verbose=None): | def make_test_objects(doctype, test_records, verbose=None): | ||||
records = [] | records = [] | ||||
if not frappe.get_meta(doctype).issingle: | |||||
existing = frappe.get_all(doctype, filters={"name":("like", "_T-" + doctype + "-%")}) | |||||
if existing: | |||||
return [d.name for d in existing] | |||||
existing = frappe.get_all(doctype, filters={"name":("like", "_Test " + doctype + "%")}) | |||||
if existing: | |||||
return [d.name for d in existing] | |||||
# if not frappe.get_meta(doctype).issingle: | |||||
# existing = frappe.get_all(doctype, filters={"name":("like", "_T-" + doctype + "-%")}) | |||||
# if existing: | |||||
# return [d.name for d in existing] | |||||
# | |||||
# existing = frappe.get_all(doctype, filters={"name":("like", "_Test " + doctype + "%")}) | |||||
# if existing: | |||||
# return [d.name for d in existing] | |||||
for doc in test_records: | for doc in test_records: | ||||
if not doc.get("doctype"): | if not doc.get("doctype"): | ||||
@@ -146,4 +146,3 @@ class TestDocument(unittest.TestCase): | |||||
d.validate_update_after_submit() | d.validate_update_after_submit() | ||||
d.meta.get_field("starts_on").allow_on_submit = 0 | d.meta.get_field("starts_on").allow_on_submit = 0 | ||||