From fc085a2b396aa5b0496a12d7cd71f2b88e04cbdc Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 15 Nov 2018 19:48:25 +0530 Subject: [PATCH 01/14] 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 02/14] [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 d74da75a8d811cf9fa37455fddecad6dff5ecedf Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Tue, 20 Nov 2018 15:57:21 +0530 Subject: [PATCH 03/14] fix: Add download with data checkbox (#6494) --- frappe/core/doctype/data_import/data_import.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js index 36c2488aab..6177aaf86f 100644 --- a/frappe/core/doctype/data_import/data_import.js +++ b/frappe/core/doctype/data_import/data_import.js @@ -200,6 +200,13 @@ frappe.data_import.download_dialog = function(frm) { "options": "Excel\nCSV", "default": "Excel" }, + { + "label": __("Download with Data"), + "fieldname": "with_data", + "fieldtype": "Check", + "hidden": !frm.doc.overwrite, + "default": 1 + }, { "label": __("Select All"), "fieldname": "select_all", @@ -270,7 +277,7 @@ frappe.data_import.download_dialog = function(frm) { doctype: frm.doc.reference_doctype, parent_doctype: frm.doc.reference_doctype, select_columns: JSON.stringify(columns), - with_data: frm.doc.overwrite, + with_data: frm.doc.overwrite && data.with_data, all_doctypes: true, file_type: data.file_type, template: true From 7319853d589b9931959baa78e11320e66ce73f89 Mon Sep 17 00:00:00 2001 From: Juan Carlos Espinoza Zurita Date: Wed, 21 Nov 2018 07:47:34 -0500 Subject: [PATCH 04/14] [MINOR FEATURE] Export file to TXT #6737 (#6489) --- frappe/utils/response.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 78eb88cee1..4809fcb45b 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -39,6 +39,7 @@ def build_response(response_type=None): response_type_map = { 'csv': as_csv, + 'txt': as_txt, 'download': as_raw, 'json': as_json, 'page': as_page, @@ -56,6 +57,14 @@ def as_csv(): response.data = frappe.response['result'] return response +def as_txt(): + response = Response() + response.mimetype = 'text' + response.charset = 'utf-8' + response.headers["Content-Disposition"] = ("attachment; filename=\"%s.txt\"" % frappe.response['doctype'].replace(' ', '_')).encode("utf-8") + response.data = frappe.response['result'] + return response + def as_raw(): response = Response() response.mimetype = frappe.response.get("content_type") or mimetypes.guess_type(frappe.response['filename'])[0] or "application/unknown" From c97577577e3953802b407d477308084fa4a6c039 Mon Sep 17 00:00:00 2001 From: Ameya Shenoy Date: Thu, 22 Nov 2018 09:38:05 +0530 Subject: [PATCH 05/14] fix(communication): indentation (#6513) --- frappe/core/doctype/communication/communication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 2585af3479..d0a7433e72 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -94,8 +94,8 @@ class Communication(Document): if self.communication_type in ("Communication", "Comment"): # send new comment to listening clients frappe.publish_realtime('new_communication', self.as_dict(), - doctype=self.reference_doctype, docname=self.reference_name, - after_commit=True) + doctype=self.reference_doctype, docname=self.reference_name, + after_commit=True) if self.communication_type == "Comment": notify_mentions(self) @@ -108,7 +108,7 @@ class Communication(Document): else: # reference_name contains the user who is addressed in the messages' page comment frappe.publish_realtime('new_message', self.as_dict(), - user=self.reference_name, after_commit=True) + user=self.reference_name, after_commit=True) def on_update(self): """Update parent status as `Open` or `Replied`.""" From a3f63ca1bd652c9c2739487bf4f270ea394a7a19 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 10:24:06 +0530 Subject: [PATCH 06/14] 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 3b5d1efff371ffb55c61d6148def06d29688906c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 22 Nov 2018 11:06:41 +0530 Subject: [PATCH 07/14] Fix: Table header on each page on printing report (#6514) * Table header on each page after page break * Fix formatting --- frappe/templates/styles/standard.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/templates/styles/standard.css b/frappe/templates/styles/standard.css index fa60c8d45f..e8c14210f9 100644 --- a/frappe/templates/styles/standard.css +++ b/frappe/templates/styles/standard.css @@ -150,7 +150,9 @@ table td div { /* hack for webkit specific browser */ @media (-webkit-min-device-pixel-ratio:0) { - thead, tfoot { display: table-row-group; } + thead, tfoot { + display: table-header-group; + } } [document-status] { From df6687f9c032efdce8ff897e7ef52dc5bd3ada39 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 11:33:29 +0530 Subject: [PATCH 08/14] 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 2c6561d485220dd63c18e809ca53ecb0a30b57a8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 22 Nov 2018 12:10:57 +0530 Subject: [PATCH 09/14] feat: Newsletter link in email section (#6519) --- frappe/config/setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/config/setup.py b/frappe/config/setup.py index 42c29fe866..7ed9d837ce 100644 --- a/frappe/config/setup.py +++ b/frappe/config/setup.py @@ -177,6 +177,11 @@ def get_data(): "name": "Auto Email Report", "description": _("Setup Reports to be emailed at regular intervals"), }, + { + "type": "doctype", + "name": "Newsletter", + "description": _("Create and manage newsletter") + } ] }, { From c247ec17ea6f2f16b984251944f15fb8421ed23a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Nov 2018 12:44:32 +0530 Subject: [PATCH 10/14] 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 11/14] 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 d8277d0f98490e7b252da0e41cde82a05891a04a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 22 Nov 2018 20:10:08 +0530 Subject: [PATCH 12/14] [Fix] Fetch from works after submission of document --- frappe/model/base_document.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index b66aae9a2c..41b42fede4 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -474,7 +474,8 @@ class BaseDocument(object): setattr(self, df.fieldname, values.name) for _df in fields_to_fetch: - setattr(self, _df.fieldname, values[_df.fetch_from.split('.')[-1]]) + if self.docstatus != 1 or _df.allow_on_submit: + setattr(self, _df.fieldname, values[_df.fetch_from.split('.')[-1]]) notify_link_count(doctype, docname) From 889d56ae5444a42c3335933108a3bd1f383f04d5 Mon Sep 17 00:00:00 2001 From: Ameya Shenoy Date: Fri, 23 Nov 2018 08:02:14 +0000 Subject: [PATCH 13/14] 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() From 2698aede0c74b27267e50565cb9e066e69053cb4 Mon Sep 17 00:00:00 2001 From: Ameya Shenoy Date: Fri, 23 Nov 2018 08:14:29 +0000 Subject: [PATCH 14/14] bumped to version 11.0.3-beta.30 --- frappe/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index ab03106828..23d99255c1 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -12,7 +12,7 @@ source_link = "https://github.com/frappe/frappe" app_license = "MIT" develop_version = '12.x.x-develop' -staging_version = '11.0.3-beta.29' +staging_version = '11.0.3-beta.30' app_email = "info@frappe.io"