diff --git a/frappe/__init__.py b/frappe/__init__.py index 316e2ed916..39dc793dc8 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -17,7 +17,7 @@ from faker import Faker from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) -__version__ = '10.1.63' +__version__ = '10.1.64' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/client.py b/frappe/client.py index 454b0a79e1..2b5917c100 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -29,7 +29,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) @@ -46,7 +46,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)) @@ -67,7 +67,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) @@ -359,8 +359,13 @@ def attach_file(filename=None, filedata=None, doctype=None, docname=None, folder return f.as_dict() -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! diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 0479692ff3..7593071c36 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 @@ -659,6 +660,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): diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 1a724eea9e..dd69bc97d3 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -82,6 +82,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 diff --git a/frappe/public/js/frappe/ui/toolbar/search.js b/frappe/public/js/frappe/ui/toolbar/search.js index f5c7714179..0554d0af09 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.js +++ b/frappe/public/js/frappe/ui/toolbar/search.js @@ -361,7 +361,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) { diff --git a/frappe/utils/redis_wrapper.py b/frappe/utils/redis_wrapper.py index c103c37212..1ac3667138 100644 --- a/frappe/utils/redis_wrapper.py +++ b/frappe/utils/redis_wrapper.py @@ -121,17 +121,17 @@ class RedisWrapper(redis.Redis): except redis.exceptions.ConnectionError: pass - def lpush(self, key, *values): - super(redis.Redis, self).lpush(self.make_key(key), *values) + def lpush(self, key, value): + super(RedisWrapper, self).lpush(self.make_key(key), value) - def rpush(self, key, *values): - super(redis.Redis, self).rpush(self.make_key(key), *values) + def rpush(self, 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))