From 929f9b6d63882f470aa875ebbfbe428adde35f54 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 12 Apr 2013 20:18:25 +0530 Subject: [PATCH 1/5] [permissions] [fixes] used BeanPermissionError, show label in error message of has_permission --- webnotes/__init__.py | 4 +++- webnotes/model/bean.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/webnotes/__init__.py b/webnotes/__init__.py index 484c29e8b8..854a6cc2de 100644 --- a/webnotes/__init__.py +++ b/webnotes/__init__.py @@ -275,9 +275,11 @@ def has_permission(doctype, ptype="read", doc=None): # no valid permission found if match_failed: + doctypelist = get_doctype(doctype) msg = _("Not allowed for: ") for key in match_failed: - msg += "\n" + key + " = " + (match_failed[key] or "None") + msg += "\n" + (doctypelist.get_field(key) and doctypelist.get_label(key) or key) \ + + " = " + (match_failed[key] or "None") msgprint(msg) return False else: diff --git a/webnotes/model/bean.py b/webnotes/model/bean.py index 3c5a03907f..bafc5ff165 100644 --- a/webnotes/model/bean.py +++ b/webnotes/model/bean.py @@ -34,6 +34,7 @@ from webnotes.utils import cint, cstr from webnotes.model.doc import Document class DocstatusTransitionError(webnotes.ValidationError): pass +class BeanPermissionError(webnotes.ValidationError): pass class Bean: """ @@ -334,7 +335,7 @@ class Bean: def no_permission_to(self, ptype): webnotes.msgprint(("%s (%s): " % (self.doc.name, _(self.doc.doctype))) + \ - _("No Permission to ") + ptype, raise_exception=True) + _("No Permission to ") + ptype, raise_exception=BeanPermissionError) def check_no_back_links_exist(self): from webnotes.model.utils import check_if_doc_is_linked From 270839ec89c1d1c025019cf3ad06e59467bdc4f6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 12 Apr 2013 21:00:46 +0530 Subject: [PATCH 2/5] [db.py get_value] [enhancement] allow more operators like !=, >, >= etc in get_value --- webnotes/client.py | 4 ++-- webnotes/db.py | 21 ++++++++++++++----- webnotes/tests/test_db.py | 43 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 webnotes/tests/test_db.py diff --git a/webnotes/client.py b/webnotes/client.py index 25ac4440b0..ef58459768 100644 --- a/webnotes/client.py +++ b/webnotes/client.py @@ -33,13 +33,13 @@ def get(doctype, name=None, filters=None): return [d.fields for d in webnotes.bean(doctype, name).doclist] @webnotes.whitelist() -def get_value(doctype, fieldname, filters=None, as_dict=True): +def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False): if not webnotes.has_permission(doctype): webnotes.msgprint("No Permission", raise_exception=True) if fieldname and fieldname.startswith("["): fieldname = json.loads(fieldname) - return webnotes.conn.get_value(doctype, json.loads(filters), fieldname, as_dict=as_dict) + return webnotes.conn.get_value(doctype, json.loads(filters), fieldname, as_dict=as_dict, debug=debug) @webnotes.whitelist() def insert(doclist): diff --git a/webnotes/db.py b/webnotes/db.py index dc3bcab399..1f62ba844e 100644 --- a/webnotes/db.py +++ b/webnotes/db.py @@ -260,18 +260,28 @@ class Database: filter's key is passed by map function build conditions like: * ifnull(`fieldname`, default_value) = %(fieldname)s - * `fieldname` = %(fieldname)s + * `fieldname` [=, !=, >, >=, <, <=] %(fieldname)s """ + _operator = "=" + value = filters.get(key) + if isinstance(value, (list, tuple)): + _operator = value[0] + filters[key] = value[1] + + if _operator not in ["=", "!=", ">", ">=", "<", "<=", "like"]: + _operator = "=" + if "[" in key: split_key = key.split("[") - return "ifnull(`" + split_key[0] + "`, " + split_key[1][:-1] + ") = %(" + key + ")s" + return "ifnull(`" + split_key[0] + "`, " + split_key[1][:-1] + ") " \ + + _operator + " %(" + key + ")s" else: - return "`" + key + "` = %(" + key + ")s" + return "`" + key + "` " + _operator + " %(" + key + ")s" if isinstance(filters, basestring): filters = { "name": filters } conditions = map(_build_condition, filters) - + return " and ".join(conditions), filters def get(self, doctype, filters=None, as_dict=True): @@ -282,7 +292,8 @@ class Database: For Single DocType, let filters be = None""" ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug) - return ret and (len(ret[0]) > 1 and ret[0] or ret[0][0]) or None + + return ret and ((len(ret[0]) > 1 or as_dict) and ret[0] or ret[0][0]) or None def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False): fields = fieldname diff --git a/webnotes/tests/test_db.py b/webnotes/tests/test_db.py new file mode 100644 index 0000000000..67236cbcb4 --- /dev/null +++ b/webnotes/tests/test_db.py @@ -0,0 +1,43 @@ +# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) +# +# MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import unicode_literals +import unittest +import webnotes +from webnotes.test_runner import make_test_records + +class TestDB(unittest.TestCase): + def test_get_value(self): + webnotes.conn.sql("""delete from `tabProfile` where name not in ('Administrator', 'Guest')""") + + make_test_records("Profile") + + self.assertEquals(webnotes.conn.get_value("Profile", {"name": ["=", "Administrator"]}), "Administrator") + self.assertEquals(webnotes.conn.get_value("Profile", {"name": ["like", "Admin%"]}), "Administrator") + self.assertEquals(webnotes.conn.get_value("Profile", {"name": ["!=", "Guest"]}), "Administrator") + + from webnotes.utils import nowdate + self.assertEquals(webnotes.conn.get_value("Profile", {"modified": ["<", nowdate()]}), "Administrator") + self.assertEquals(webnotes.conn.get_value("Profile", {"modified": ["<=", nowdate()]}), "Administrator") + self.assertEquals(webnotes.conn.get_value("Profile", {"modified": [">", nowdate()]}), "test1@example.com") + self.assertEquals(webnotes.conn.get_value("Profile", {"modified": [">=", nowdate()]}), "test1@example.com") + + \ No newline at end of file From c17e4db25ef7e5fc9199db4efe4e81414dfd90bb Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 12 Apr 2013 21:01:20 +0530 Subject: [PATCH 3/5] [profile] [fix] profile's get roles to be used to get roles of any profile --- webnotes/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webnotes/profile.py b/webnotes/profile.py index 3ad5ce003d..089787b67b 100644 --- a/webnotes/profile.py +++ b/webnotes/profile.py @@ -47,7 +47,7 @@ class Profile: def get_roles(self): """get list of roles""" if not self.roles: - self.roles = webnotes.get_roles() + self.roles = webnotes.get_roles(self.name) return self.roles def build_doctype_map(self): From 9a77313017c4c44a15ad7b06b6bcb6081df198f9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Apr 2013 11:14:09 +0530 Subject: [PATCH 4/5] removed self-reference check on deletion --- webnotes/model/utils.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/webnotes/model/utils.py b/webnotes/model/utils.py index 52c91f2b8a..88be96a276 100644 --- a/webnotes/model/utils.py +++ b/webnotes/model/utils.py @@ -52,7 +52,6 @@ def compress(doclist): Compress a doclist before sending it to the client side. (Internally used by the request handler) """ - from webnotes.model.doc import Document docs = [isinstance(d, Document) and d.fields or d for d in doclist] kl, vl = {}, [] @@ -99,7 +98,6 @@ def copy_doclist(doclist, no_copy = []): Save & return a copy of the given doclist Pass fields that are not to be copied in `no_copy` """ - from webnotes.model.doc import Document cl = [] @@ -208,8 +206,6 @@ def check_if_doc_is_linked(dt, dn, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ - sql = webnotes.conn.sql - from webnotes.model.rename_doc import get_link_fields link_fields = get_link_fields(dt) link_fields = [[lf['parent'], lf['fieldname']] for lf in link_fields] @@ -218,12 +214,13 @@ def check_if_doc_is_linked(dt, dn, method="Delete"): link_dt, link_field = l item = webnotes.conn.get_value(link_dt, {link_field:dn}, ["name", "parent", "parenttype", - "docstatus"]) + "docstatus"], as_dict=True) - if (method=="Delete" and item) or (method=="Cancel" and item and item[3]==1): + if item and item.parent != dn and (method=="Delete" or + (method=="Cancel" and item.docstatus==1)): webnotes.msgprint(method + " " + _("Error") + ":"+\ - ("%s (%s) " % (dn, dt)) + _("is linked in") + (" %s (%s)") % (item[1] or item[0], - item[1] and item[2] or link_dt), + ("%s (%s) " % (dn, dt)) + _("is linked in") + (" %s (%s)") % + (item.parent or item.name, item.parent and item.parenttype or link_dt), raise_exception=LinkExistsError) def round_floats_in_doc(doc, precision_map): From a8ae8c4e668f5e09954d317083616055653318b5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Apr 2013 17:19:17 +0530 Subject: [PATCH 5/5] naming_series_options mandatory if autoname set by naming_series: --- webnotes/model/doc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webnotes/model/doc.py b/webnotes/model/doc.py index e2a654cdcd..6964fc9a53 100755 --- a/webnotes/model/doc.py +++ b/webnotes/model/doc.py @@ -224,7 +224,10 @@ class Document: if not self.naming_series: # pick default naming series from webnotes.model.doctype import get_property - self.naming_series = get_property(self.doctype, "options", "naming_series").split("\n") + self.naming_series = get_property(self.doctype, "options", "naming_series") + if not self.naming_series: + webnotes.msgprint(webnotes._("Naming Series mandatory"), raise_exception=True) + self.naming_series = self.naming_series.split("\n") self.naming_series = self.naming_series[0] or self.naming_series[1] self.name = make_autoname(self.naming_series+'.#####')