From fc085a2b396aa5b0496a12d7cd71f2b88e04cbdc Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 15 Nov 2018 19:48:25 +0530 Subject: [PATCH 1/7] fix(redis-wrapper): Do not assume existence of superclass of redis.Redis RedisWrapper inherits from Redis which inherits from StrictRedis In redis-py 3.0.0 StrictRedis was renamed to Redis. This seems like a harmless change, but, instead of using `self(RedisWrapper ...`, all methods use `self(Redis ...` which assumes previous hierarchy (i.e. RedisWrapper <- Redis <- StrictRedis) --- frappe/utils/redis_wrapper.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frappe/utils/redis_wrapper.py b/frappe/utils/redis_wrapper.py index 05af4c99fb..41d8ba20bc 100644 --- a/frappe/utils/redis_wrapper.py +++ b/frappe/utils/redis_wrapper.py @@ -122,16 +122,16 @@ class RedisWrapper(redis.Redis): pass def lpush(self, key, value): - super(redis.Redis, self).lpush(self.make_key(key), value) + super(RedisWrapper, self).lpush(self.make_key(key), value) def rpush(self, key, value): - super(redis.Redis, self).rpush(self.make_key(key), value) + super(RedisWrapper, self).rpush(self.make_key(key), value) def lpop(self, key): - return super(redis.Redis, self).lpop(self.make_key(key)) + return super(RedisWrapper, self).lpop(self.make_key(key)) def llen(self, key): - return super(redis.Redis, self).llen(self.make_key(key)) + return super(RedisWrapper, self).llen(self.make_key(key)) def hset(self, name, key, value, shared=False): _name = self.make_key(name, shared=shared) @@ -143,14 +143,14 @@ class RedisWrapper(redis.Redis): # set in redis try: - super(redis.Redis, self).hset(_name, + super(RedisWrapper, self).hset(_name, key, pickle.dumps(value)) except redis.exceptions.ConnectionError: pass def hgetall(self, name): return {key: pickle.loads(value) for key, value in - iteritems(super(redis.Redis, self).hgetall(self.make_key(name)))} + iteritems(super(RedisWrapper, self).hgetall(self.make_key(name)))} def hget(self, name, key, generator=None, shared=False): _name = self.make_key(name, shared=shared) @@ -162,7 +162,7 @@ class RedisWrapper(redis.Redis): value = None try: - value = super(redis.Redis, self).hget(_name, key) + value = super(RedisWrapper, self).hget(_name, key) except redis.exceptions.ConnectionError: pass @@ -184,7 +184,7 @@ class RedisWrapper(redis.Redis): if key in frappe.local.cache[_name]: del frappe.local.cache[_name][key] try: - super(redis.Redis, self).hdel(_name, key) + super(RedisWrapper, self).hdel(_name, key) except redis.exceptions.ConnectionError: pass @@ -196,31 +196,31 @@ class RedisWrapper(redis.Redis): def hkeys(self, name): try: - return super(redis.Redis, self).hkeys(self.make_key(name)) + return super(RedisWrapper, self).hkeys(self.make_key(name)) except redis.exceptions.ConnectionError: return [] def sadd(self, name, *values): """Add a member/members to a given set""" - super(redis.Redis, self).sadd(self.make_key(name), *values) + super(RedisWrapper, self).sadd(self.make_key(name), *values) def srem(self, name, *values): """Remove a specific member/list of members from the set""" - super(redis.Redis, self).srem(self.make_key(name), *values) + super(RedisWrapper, self).srem(self.make_key(name), *values) def sismember(self, name, value): """Returns True or False based on if a given value is present in the set""" - return super(redis.Redis, self).sismember(self.make_key(name), value) + return super(RedisWrapper, self).sismember(self.make_key(name), value) def spop(self, name): """Removes and returns a random member from the set""" - return super(redis.Redis, self).spop(self.make_key(name)) + return super(RedisWrapper, self).spop(self.make_key(name)) def srandmember(self, name, count=None): """Returns a random member from the set""" - return super(redis.Redis, self).srandmember(self.make_key(name)) + return super(RedisWrapper, self).srandmember(self.make_key(name)) def smembers(self, name): """Return all members of the set""" - return super(redis.Redis, self).smembers(self.make_key(name)) + return super(RedisWrapper, self).smembers(self.make_key(name)) From 9d28467c52ec4f89686346297d46bd6c6fd45526 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 20 Nov 2018 15:16:31 +0530 Subject: [PATCH 2/7] [fix] support additional keys in limits --- frappe/commands/site.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 0dbc83848f..27d0b1c742 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -455,8 +455,10 @@ def _set_limits(context, site, limits): frappe.connect() new_limits = {} for limit, value in limits: - if limit not in ('daily_emails', 'emails', 'space', 'users', 'email_group', - 'expiry', 'support_email', 'support_chat', 'upgrade_url'): + if limit not in ('daily_emails', 'emails', 'space', 'users', 'email_group', 'currency', + 'expiry', 'support_email', 'support_chat', 'upgrade_url', 'subscription_id', + 'subscription_type', 'current_plan', 'subscription_base_price', 'upgrade_plan', + 'upgrade_base_price'): frappe.throw(_('Invalid limit {0}').format(limit)) if limit=='expiry' and value: @@ -465,7 +467,7 @@ def _set_limits(context, site, limits): except ValueError: raise ValueError("Incorrect data format, should be YYYY-MM-DD") - elif limit=='space': + elif limit in ('space', 'subscription_base_price', 'upgrade_base_price'): value = float(value) elif limit in ('users', 'emails', 'email_group', 'daily_emails'): From a3f63ca1bd652c9c2739487bf4f270ea394a7a19 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 10:24:06 +0530 Subject: [PATCH 3/7] fix: Child table data can be get easily by passing fake parent id (#6509) --- frappe/client.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/client.py b/frappe/client.py index 1695b39d3a..c8ec3a7156 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -28,7 +28,7 @@ def get_list(doctype, fields=None, filters=None, order_by=None, :param limit_start: Start at this index :param limit_page_length: Number of records to be returned (default 20)''' if frappe.is_table(doctype): - check_parent_permission(parent) + check_parent_permission(parent, doctype) return frappe.get_list(doctype, fields=fields, filters=filters, order_by=order_by, limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=False) @@ -41,7 +41,7 @@ def get(doctype, name=None, filters=None, parent=None): :param name: return document of this `name` :param filters: If name is not set, filter by these values and return the first match''' if frappe.is_table(doctype): - check_parent_permission(parent) + check_parent_permission(parent, doctype) if filters and not name: name = frappe.db.get_value(doctype, json.loads(filters)) @@ -62,7 +62,7 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren :param fieldname: Field to be returned (default `name`) :param filters: dict or string for identifying the record''' if frappe.is_table(doctype): - check_parent_permission(parent) + check_parent_permission(parent, doctype) if not frappe.has_permission(doctype): frappe.throw(_("No permission for {0}".format(doctype)), frappe.PermissionError) @@ -314,9 +314,14 @@ def get_time_zone(): '''Returns default time zone''' return {"time_zone": frappe.defaults.get_defaults().get("time_zone")} -def check_parent_permission(parent): +def check_parent_permission(parent, child_doctype): if parent: + # User may pass fake parent and get the information from the child table + if child_doctype and not frappe.db.exists('DocField', + {'parent': parent, 'options': child_doctype}): + raise frappe.PermissionError + if frappe.permissions.has_permission(parent): return # Either parent not passed or the user doesn't have permission on parent doctype of child table! - raise frappe.PermissionError + raise frappe.PermissionError \ No newline at end of file From df6687f9c032efdce8ff897e7ef52dc5bd3ada39 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 11:33:29 +0530 Subject: [PATCH 4/7] fix: After rename, user still able to search document using old name (#6515) --- frappe/model/rename_doc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 732b85456b..961a147ec6 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -78,6 +78,7 @@ def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=F frappe.delete_doc(doctype, old) frappe.clear_cache() + frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype', doctype=doctype) return new From c247ec17ea6f2f16b984251944f15fb8421ed23a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 12:44:32 +0530 Subject: [PATCH 5/7] fix: child table access using get_list function (#6511) User was able to fetch child table data without having access from client side using frappe.db.get_list function --- frappe/model/db_query.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index d561517558..ed3b901084 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -14,6 +14,7 @@ import frappe.permissions from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter_tuple, get_filter, add_to_date from frappe import _ from frappe.model import optional_fields +from frappe.client import check_parent_permission from frappe.model.utils.user_settings import get_user_settings, update_user_settings from datetime import datetime @@ -619,6 +620,19 @@ def get_list(doctype, *args, **kwargs): '''wrapper for DatabaseQuery''' kwargs.pop('cmd', None) kwargs.pop('ignore_permissions', None) + + # If doctype is child table + if frappe.is_table(doctype): + # Example frappe.db.get_list('Purchase Receipt Item', {'parent': 'Purchase Receipt'}) + # Here purchase receipt is the parent doctype of the child doctype Purchase Receipt Item + + if not kwargs.get('parent'): + frappe.flags.error_message = _('Parent is required to get child table data') + raise frappe.PermissionError(doctype) + + check_parent_permission(kwargs.get('parent'), doctype) + del kwargs['parent'] + return DatabaseQuery(doctype).execute(None, *args, **kwargs) def is_parent_only_filter(doctype, filters): From 66bfa66e7cfa5dd5861f3e5a2c438e6d075f8b87 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 14:11:07 +0530 Subject: [PATCH 6/7] fix: From global search data is not displaying (#6521) --- frappe/public/js/frappe/ui/toolbar/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/toolbar/search.js b/frappe/public/js/frappe/ui/toolbar/search.js index dec4f38795..293989ca61 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.js +++ b/frappe/public/js/frappe/ui/toolbar/search.js @@ -357,7 +357,7 @@ frappe.search.SearchDialog = Class.extend({ no_results_status: (keyword) => __("

No results found for '" + keyword + "' in Global Search

"), get_results: function(keywords, callback) { - var start = 0, limit = 100; + var start = 0, limit = 1000; var results = frappe.search.utils.get_nav_results(keywords); frappe.search.utils.get_global_results(keywords, start, limit) .then(function(global_results) { From 889d56ae5444a42c3335933108a3bd1f383f04d5 Mon Sep 17 00:00:00 2001 From: Ameya Shenoy Date: Fri, 23 Nov 2018 08:02:14 +0000 Subject: [PATCH 7/7] bumped to version 10.1.64 --- frappe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index df81841011..1942111218 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json from .exceptions import * from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template -__version__ = '10.1.63' +__version__ = '10.1.64' __title__ = "Frappe Framework" local = Local()