From 072f0026c26b9821f615c8c470daf72f90a79b78 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 27 Jul 2020 20:38:29 +0530 Subject: [PATCH 01/31] fix: Handle directories in unzip --- frappe/core/doctype/file/file.py | 39 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 1748c60020..6adda307a4 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -278,25 +278,26 @@ class File(Document): base_url = os.path.dirname(self.file_url) files = [] - with zipfile.ZipFile(zip_path) as zf: - zf.extractall(os.path.dirname(zip_path)) - for info in zf.infolist(): - if not info.filename.startswith('__MACOSX'): - file_url = file_url = base_url + '/' + info.filename - file_name = frappe.db.get_value('File', dict(file_url=file_url)) - if file_name: - file_doc = frappe.get_doc('File', file_name) - else: - file_doc = frappe.new_doc("File") - file_doc.file_name = info.filename - file_doc.file_size = info.file_size - file_doc.folder = self.folder - file_doc.is_private = self.is_private - file_doc.file_url = file_url - file_doc.attached_to_doctype = self.attached_to_doctype - file_doc.attached_to_name = self.attached_to_name - file_doc.save() - files.append(file_doc) + with zipfile.ZipFile(zip_path) as z: + for file in z.filelist: + if file.is_dir() or file.filename.startswith('__MACOSX/'): + # skip directories and macos hidden directory + continue + + filename = os.path.basename(file.filename) + if filename.startswith('.'): + # skip hidden files + continue + + file_doc = frappe.new_doc('File') + file_doc.content = z.read(file.filename) + file_doc.file_name = filename + file_doc.folder = self.folder + file_doc.is_private = self.is_private + file_doc.attached_to_doctype = self.attached_to_doctype + file_doc.attached_to_name = self.attached_to_name + file_doc.save() + files.append(file_doc) frappe.delete_doc('File', self.name) return files From aae0309cedf567ef4a2f3c4dd7c9ca5eb79096e9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 19 Aug 2020 14:19:52 +0530 Subject: [PATCH 02/31] feat: Allow me to execute anything console would allow --- frappe/commands/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 721376016c..17e6e53c51 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -133,6 +133,7 @@ def reset_perms(context): def execute(context, method, args=None, kwargs=None, profile=False): "Execute a function" for site in context.sites: + ret = "" try: frappe.init(site=site) frappe.connect() @@ -154,7 +155,10 @@ def execute(context, method, args=None, kwargs=None, profile=False): pr = cProfile.Profile() pr.enable() - ret = frappe.get_attr(method)(*args, **kwargs) + try: + ret = frappe.get_attr(method)(*args, **kwargs) + except Exception: + ret = frappe.safe_eval(method + "()", eval_globals=globals(), eval_locals=locals()) if profile: pr.disable() From 557bceed027a032c1ec2c3ba6941a4b381b64948 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 20 Aug 2020 12:03:03 +0530 Subject: [PATCH 03/31] fix: Allow args and kwargs too Co-authored-by: Aditya Hase --- frappe/commands/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 17e6e53c51..acd25eb166 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -158,7 +158,7 @@ def execute(context, method, args=None, kwargs=None, profile=False): try: ret = frappe.get_attr(method)(*args, **kwargs) except Exception: - ret = frappe.safe_eval(method + "()", eval_globals=globals(), eval_locals=locals()) + ret = frappe.safe_eval(method + "(*args, **kwargs)", eval_globals=globals(), eval_locals=locals()) if profile: pr.disable() From 7c2510eb6ad71b88733938e58695afc1e9fb24ca Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 20 Aug 2020 18:19:23 +0530 Subject: [PATCH 04/31] feat: Mini Test Suite for commands --- frappe/tests/test_commands.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frappe/tests/test_commands.py diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py new file mode 100644 index 0000000000..b89079889f --- /dev/null +++ b/frappe/tests/test_commands.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors + +from __future__ import unicode_literals + +import shlex +import subprocess +import unittest + +import frappe + + +def clean(value): + if isinstance(value, (bytes, str)): + value = value.decode().strip() + return value + + +class BaseTestCommands: + def execute(self, command): + command = command.format(**{"site": frappe.local.site}) + command = shlex.split(command) + self._proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.stdout = clean(self._proc.stdout) + self.stderr = clean(self._proc.stderr) + self.returncode = clean(self._proc.returncode) From 99496d97f3e00284840d2127556bba0e21d1a99e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 20 Aug 2020 18:20:02 +0530 Subject: [PATCH 05/31] test: Add tests for bench execute --- frappe/tests/test_commands.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index b89079889f..fe0129566b 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -23,3 +23,21 @@ class BaseTestCommands: self.stdout = clean(self._proc.stdout) self.stderr = clean(self._proc.stderr) self.returncode = clean(self._proc.returncode) + + +class TestCommands(BaseTestCommands, unittest.TestCase): + def test_execute(self): + # execute a command expecting a numeric output + self.execute("bench --site {site} execute frappe.db.get_database_size") + self.assertEquals(self.returncode, 0) + self.assertIsInstance(float(self.stdout), float) + + # execute a command expecting an errored output as local won't exist + self.execute("bench --site {site} execute frappe.local.site") + self.assertEquals(self.returncode, 1) + self.assertIsNotNone(self.stderr) + + # execute a command with kwargs + self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""") + self.assertEquals(self.returncode, 0) + self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType')) From 5f299b191d4c9e5773142f2298aef2df1328048c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 20 Aug 2020 18:26:18 +0530 Subject: [PATCH 06/31] chore: Added comments and style --- frappe/tests/test_commands.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index fe0129566b..82c0cdce5c 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -1,11 +1,11 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -from __future__ import unicode_literals - +# imports - standard imports import shlex import subprocess import unittest +# imports - module imports import frappe @@ -27,17 +27,20 @@ class BaseTestCommands: class TestCommands(BaseTestCommands, unittest.TestCase): def test_execute(self): - # execute a command expecting a numeric output + # test 1: execute a command expecting a numeric output self.execute("bench --site {site} execute frappe.db.get_database_size") self.assertEquals(self.returncode, 0) self.assertIsInstance(float(self.stdout), float) - # execute a command expecting an errored output as local won't exist + # test 2: execute a command expecting an errored output as local won't exist self.execute("bench --site {site} execute frappe.local.site") self.assertEquals(self.returncode, 1) self.assertIsNotNone(self.stderr) - # execute a command with kwargs + # test 3: execute a command with kwargs + # Note: + # terminal command has been escaped to avoid .format string replacement + # The returned value has quotes which have been trimmed for the test self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""") self.assertEquals(self.returncode, 0) self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType')) From 89ddeab996433a028ceab8c5289a16dffdd586ae Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 Aug 2020 11:49:11 +0530 Subject: [PATCH 07/31] fix(Activity Log): Handle delete and new actions via Permissions --- .../doctype/activity_log/activity_log.json | 649 ++---------------- .../core/doctype/activity_log/activity_log.py | 3 - 2 files changed, 51 insertions(+), 601 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.json b/frappe/core/doctype/activity_log/activity_log.json index 580882968c..0d85855504 100644 --- a/frappe/core/doctype/activity_log/activity_log.json +++ b/frappe/core/doctype/activity_log/activity_log.json @@ -1,734 +1,187 @@ - { - "allow_copy": 0, - "allow_guest_to_view": 0, +{ + "actions": [], "allow_import": 1, - "allow_rename": 0, - "autoname": "", - "beta": 0, "creation": "2017-10-05 11:10:38.780133", - "custom": 0, "description": "Keep track of all update feeds", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "subject", + "section_break_8", + "content", + "column_break_5", + "additional_info", + "communication_date", + "column_break_7", + "operation", + "status", + "reference_section", + "reference_doctype", + "reference_name", + "reference_owner", + "column_break_14", + "timeline_doctype", + "timeline_name", + "link_doctype", + "link_name", + "user", + "full_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "subject", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "content", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "400" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "additional_info", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "More Information" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Now", "fieldname": "communication_date", "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Date" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "operation", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Operation", - "length": 0, - "no_copy": 0, - "options": "\nLogin\nLogout", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nLogin\nLogout" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nSuccess\nFailed\nLinked\nClosed", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nSuccess\nFailed\nLinked\nClosed" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "reference_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reference" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reference Document Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "DocType" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_name", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "reference_doctype" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "reference_name.owner", "fieldname": "reference_owner", "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reference Owner", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "timeline_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Timeline DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "DocType" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "timeline_name", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Timeline Name", - "length": 0, - "no_copy": 0, - "options": "timeline_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "timeline_doctype" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "link_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Link DocType", - "length": 0, - "no_copy": 0, "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "link_name", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Link Name", - "length": 0, - "no_copy": 0, "options": "link_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "__user", "fieldname": "user", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "User", - "length": 0, - "no_copy": 0, "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "full_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Full Name" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-comment", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-09-05 14:22:27.664645", - "modified_by": "Administrator", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-08-28 11:43:57.504565", + "modified_by": "gavin18d@gmail.com", "module": "Core", "name": "Activity Log", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "share": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 1, + "if_owner": 1, "print": 1, "read": 1, "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 1, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "share": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "search_fields": "subject", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "subject", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 8b7941c086..27a2892ca8 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -25,9 +25,6 @@ class ActivityLog(Document): if self.reference_doctype and self.reference_name: self.status = "Linked" - def on_trash(self): # pylint: disable=no-self-use - frappe.throw(_("Sorry! You cannot delete auto-generated comments")) - def on_doctype_update(): """Add indexes in `tabActivity Log`""" frappe.db.add_index("Activity Log", ["reference_doctype", "reference_name"]) From f9e6f0e8e41a64e9eed308c54037f2b3dd5509d9 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 31 Aug 2020 21:07:51 +0530 Subject: [PATCH 08/31] fix(Blog): Ability to set meta_title separately --- frappe/website/doctype/blog_post/blog_post.js | 3 ++- frappe/website/doctype/blog_post/blog_post.json | 10 ++++++++-- frappe/website/doctype/blog_post/blog_post.py | 7 ++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js index 7aa83f536d..545051db7f 100644 --- a/frappe/website/doctype/blog_post/blog_post.js +++ b/frappe/website/doctype/blog_post/blog_post.js @@ -21,8 +21,9 @@ frappe.ui.form.on('Blog Post', { }); function generate_google_search_preview(frm) { + if (!(frm.doc.meta_title && frm.doc.title)) return; let google_preview = frm.get_field("google_preview"); - let seo_title = (frm.doc.title).slice(0, 60); + let seo_title = (frm.doc.meta_title || frm.doc.title).slice(0, 60); let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160); let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : ''; let route_array = frm.doc.route ? frm.doc.route.split('/') : []; diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json index 25bca28e85..48e9a18e71 100644 --- a/frappe/website/doctype/blog_post/blog_post.json +++ b/frappe/website/doctype/blog_post/blog_post.json @@ -26,6 +26,7 @@ "content_html", "email_sent", "meta_tags", + "meta_title", "meta_description", "column_break_18", "meta_image", @@ -110,7 +111,6 @@ "depends_on": "eval:doc.content_type === 'Markdown'", "fieldname": "content_md", "fieldtype": "Markdown Editor", - "ignore_xss_filter": 1, "label": "Content (Markdown)" }, { @@ -185,6 +185,12 @@ "fieldtype": "Check", "hidden": 1, "label": "Hide CTA" + }, + { + "fieldname": "meta_title", + "fieldtype": "Data", + "label": "Meta Title", + "length": 60 } ], "has_web_view": 1, @@ -194,7 +200,7 @@ "is_published_field": "published", "links": [], "max_attachments": 5, - "modified": "2020-08-31 16:55:03.687862", + "modified": "2020-08-31 21:01:51.100349", "modified_by": "Administrator", "module": "Website", "name": "Blog Post", diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index beffcdca25..1a07dbed86 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -36,6 +36,11 @@ class BlogPost(WebsiteGenerator): if self.blog_intro: self.blog_intro = self.blog_intro[:200] + if not self.meta_title: + self.meta_title = self.title[:60] + else: + self.meta_title = self.meta_title[:60] + if not self.meta_description: self.meta_description = self.blog_intro[:140] else: @@ -88,7 +93,7 @@ class BlogPost(WebsiteGenerator): context.description = self.meta_description or self.blog_intro or strip_html_tags(context.content[:140]) context.metatags = { - "name": self.title, + "name": self.meta_title, "description": context.description, } From 6ad3156233f9bc585995daa55eda4d5f0a4b1bef Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Mon, 7 Sep 2020 19:30:15 +0530 Subject: [PATCH 09/31] fix(package): Only allow System Managers to import a package --- .../custom/doctype/package_publish_tool/package_publish_tool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/custom/doctype/package_publish_tool/package_publish_tool.py b/frappe/custom/doctype/package_publish_tool/package_publish_tool.py index a01dd0ba47..b73f93a628 100644 --- a/frappe/custom/doctype/package_publish_tool/package_publish_tool.py +++ b/frappe/custom/doctype/package_publish_tool/package_publish_tool.py @@ -100,6 +100,7 @@ def export_package(): @frappe.whitelist() def import_package(package=None): """Import package from JSON.""" + frappe.only_for("System Manager") if isinstance(package, string_types): package = json.loads(package) From 8261eded1966002e44c513834871ca14821a9f39 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 7 Sep 2020 18:51:24 +0530 Subject: [PATCH 10/31] fix: Report Include Indentation UI --- .../js/frappe/views/reports/query_report.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 706cde13b7..5cfd54ee1e 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -1257,7 +1257,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { return; } - this.export_dialog = frappe.prompt([ + let export_dialog_fields = [ { label: __('Select File Format'), fieldname: 'file_format', @@ -1265,13 +1265,18 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { options: ['Excel', 'CSV'], default: 'Excel', reqd: 1 - }, - { + } + ]; + + if (this.tree_report) { + export_dialog_fields.push({ label: __("Include indentation"), fieldname: "include_indentation", fieldtype: "Check", - } - ], ({ file_format, include_indentation }) => { + }); + } + + this.export_dialog = frappe.prompt(export_dialog_fields, ({ file_format, include_indentation }) => { this.make_access_log('Export', file_format); if (file_format === 'CSV') { const column_row = this.columns.reduce((acc, col) => { From dba454f94136c9480db3bb32081fb4b1b2df47fe Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 8 Sep 2020 10:37:25 +0530 Subject: [PATCH 11/31] fix(security): Remove ignore_permissions flag from API request --- frappe/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/__init__.py b/frappe/__init__.py index 46792e82a8..7d3d34428e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1110,6 +1110,7 @@ def get_newargs(fn, kwargs): if (a in fnargs) or varkw: newargs[a] = kwargs.get(a) + newargs.pop('ignore_permissions', None) if "flags" in newargs: del newargs["flags"] From 9e43e887555d90f73b539cab4085f409a3691636 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 8 Sep 2020 10:38:26 +0530 Subject: [PATCH 12/31] fix: Remove unnecessary whitelisting of rename_doc method --- frappe/model/rename_doc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 1e3f127b99..7a2129e76e 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -25,7 +25,6 @@ def update_document_title(doctype, docname, title_field=None, old_title=None, ne return docname -@frappe.whitelist() def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False, show_alert=True): """ Renames a doc(dt, old) to doc(dt, new) and From 0449ba0b6d25bf87c3821ad853dccd322cac2740 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 8 Sep 2020 10:58:03 +0530 Subject: [PATCH 13/31] refactor: Use .pop instead of del --- frappe/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 7d3d34428e..4b60181bd1 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1110,9 +1110,8 @@ def get_newargs(fn, kwargs): if (a in fnargs) or varkw: newargs[a] = kwargs.get(a) - newargs.pop('ignore_permissions', None) - if "flags" in newargs: - del newargs["flags"] + newargs.pop("ignore_permissions", None) + newargs.pop("flags", None) return newargs From 864e8441eb588add6e6d341b452f3508ce0ed19e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 8 Sep 2020 11:53:58 +0530 Subject: [PATCH 14/31] fix: condition Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/website/doctype/blog_post/blog_post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js index 1c602cd5e1..97916b6fc6 100644 --- a/frappe/website/doctype/blog_post/blog_post.js +++ b/frappe/website/doctype/blog_post/blog_post.js @@ -33,7 +33,7 @@ frappe.ui.form.on('Blog Post', { }); function generate_google_search_preview(frm) { - if (!(frm.doc.meta_title && frm.doc.title)) return; + if (!(frm.doc.meta_title || frm.doc.title)) return; let google_preview = frm.get_field("google_preview"); let seo_title = (frm.doc.meta_title || frm.doc.title).slice(0, 60); let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160); From 936af9916a4ab442f344fb860aa6da8e81b30115 Mon Sep 17 00:00:00 2001 From: gavin Date: Tue, 8 Sep 2020 12:28:20 +0530 Subject: [PATCH 15/31] fix(Activity Log): Update modified details ref: https://github.com/frappe/frappe/pull/11373 --- frappe/core/doctype/activity_log/activity_log.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.json b/frappe/core/doctype/activity_log/activity_log.json index 0d85855504..a1ee4dafdb 100644 --- a/frappe/core/doctype/activity_log/activity_log.json +++ b/frappe/core/doctype/activity_log/activity_log.json @@ -155,7 +155,7 @@ "index_web_pages_for_search": 1, "links": [], "modified": "2020-08-28 11:43:57.504565", - "modified_by": "gavin18d@gmail.com", + "modified_by": "Administrator", "module": "Core", "name": "Activity Log", "owner": "Administrator", @@ -184,4 +184,4 @@ "title_field": "subject", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} From 770730489036e13033a76add4edf60b709197363 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 8 Sep 2020 17:08:57 +0530 Subject: [PATCH 16/31] fix: Handle None value --- frappe/core/doctype/file/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 316915e43a..aa7584d7d3 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -938,7 +938,7 @@ def attach_files_to_document(doc, event): # we dont want the update to fail if file cannot be attached for some reason try: value = doc.get(df.fieldname) - if not value.startswith(("/files", "/private/files")): + if not (value or '').startswith(("/files", "/private/files")): return if frappe.db.exists("File", { From d7ea913605b0d14d493d467012848d88b6a67e80 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Sep 2020 19:01:08 +0530 Subject: [PATCH 17/31] fix: event streaming fixes --- .../event_consumer/event_consumer.json | 8 +++-- .../doctype/event_consumer/event_consumer.py | 26 ++++++++------ .../event_producer/event_producer.json | 11 +++--- .../doctype/event_producer/event_producer.py | 36 ++++++++++++++----- .../event_producer/test_event_producer.py | 30 +++++++++++++--- 5 files changed, 80 insertions(+), 31 deletions(-) diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.json b/frappe/event_streaming/doctype/event_consumer/event_consumer.json index 85970dc277..42b47ce949 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.json +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.json @@ -21,6 +21,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Callback URL", + "read_only": 1, "reqd": 1, "unique": 1 }, @@ -28,19 +29,20 @@ "fieldname": "api_key", "fieldtype": "Data", "label": "API Key", - "read_only": 1 + "reqd": 1 }, { "fieldname": "api_secret", "fieldtype": "Password", "label": "API Secret", - "read_only": 1 + "reqd": 1 }, { "fieldname": "user", "fieldtype": "Link", "label": "Event Subscriber", "options": "User", + "read_only": 1, "reqd": 1 }, { @@ -69,7 +71,7 @@ ], "in_create": 1, "links": [], - "modified": "2020-09-06 15:42:00.746493", + "modified": "2020-09-08 16:42:39.828085", "modified_by": "Administrator", "module": "Event Streaming", "name": "Event Consumer", diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 2e10c71d0d..122e11a3c5 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -7,6 +7,7 @@ import frappe import json import requests import os +from frappe import _ from frappe.model.document import Document from frappe.frappeclient import FrappeClient from frappe.utils.data import get_url @@ -58,17 +59,26 @@ class EventConsumer(Document): return 'offline' return 'online' - -@frappe.whitelist(allow_guest=True) +@frappe.whitelist() def register_consumer(data): """create an event consumer document for registering a consumer""" data = json.loads(data) # to ensure that consumer is created only once if frappe.db.exists('Event Consumer', data['event_consumer']): return None + + user = data['user'] + if not frappe.db.exists('User', user): + frappe.throw(_('User {0} not found on the producer site').format(user)) + + if not "System Manager" in frappe.get_roles(user): + frappe.throw(_("Event Subscriber has to be a System Manager.")) + consumer = frappe.new_doc('Event Consumer') consumer.callback_url = data['event_consumer'] consumer.user = data['user'] + consumer.api_key = data['api_key'] + consumer.api_secret = data['api_secret'] consumer.incoming_change = True consumer_doctypes = json.loads(data['consumer_doctypes']) @@ -78,18 +88,13 @@ def register_consumer(data): 'status': 'Pending' }) - api_key = frappe.generate_hash(length=10) - api_secret = frappe.generate_hash(length=10) - consumer.api_key = api_key - consumer.api_secret = api_secret - consumer.insert(ignore_permissions=True) - frappe.db.commit() + consumer.insert() # consumer's 'last_update' field should point to the latest update # in producer's update log when subscribing # so that, updates after subscribing are consumed and not the old ones. last_update = str(get_last_update()) - return json.dumps({'api_key': api_key, 'api_secret': api_secret, 'last_update': last_update}) + return json.dumps({'last_update': last_update}) def get_consumer_site(consumer_url): @@ -98,8 +103,7 @@ def get_consumer_site(consumer_url): consumer_site = FrappeClient( url=consumer_url, api_key=consumer_doc.api_key, - api_secret=consumer_doc.get_password('api_secret'), - frappe_authorization_source='Event Producer' + api_secret=consumer_doc.get_password('api_secret') ) return consumer_site diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.json b/frappe/event_streaming/doctype/event_producer/event_producer.json index 8eba1924f5..8fafdc3bb2 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.json +++ b/frappe/event_streaming/doctype/event_producer/event_producer.json @@ -32,23 +32,26 @@ "read_only": 1 }, { + "description": "API Key of the user(Event Subscriber) on the producer site", "fieldname": "api_key", "fieldtype": "Data", "label": "API Key", - "read_only": 1 + "reqd": 1 }, { + "description": "API Secret of the user(Event Subscriber) on the producer site", "fieldname": "api_secret", "fieldtype": "Password", "label": "API Secret", - "read_only": 1 + "reqd": 1 }, { "fieldname": "user", "fieldtype": "Link", "label": "Event Subscriber", "options": "User", - "reqd": 1 + "reqd": 1, + "set_only_once": 1 }, { "fieldname": "column_break_6", @@ -74,7 +77,7 @@ } ], "links": [], - "modified": "2019-12-26 13:04:11.438349", + "modified": "2020-09-08 18:50:57.687979", "modified_by": "Administrator", "module": "Event Streaming", "name": "Event Producer", diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py index 555b71f851..5fb2c22d89 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/event_producer.py @@ -12,7 +12,8 @@ from frappe import _ from frappe.model.document import Document from frappe.frappeclient import FrappeClient from frappe.utils.background_jobs import get_jobs -from frappe.utils.data import get_url +from frappe.utils.data import get_url, get_link_to_form +from frappe.utils.password import get_decrypted_password from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.integrations.oauth2 import validate_url @@ -25,14 +26,25 @@ class EventProducer(Document): self.create_custom_fields() def validate(self): + self.validate_event_subscriber() if frappe.flags.in_test: for entry in self.producer_doctypes: entry.status = 'Approved' + def validate_event_subscriber(self): + if not frappe.db.get_value('User', self.user, 'api_key'): + frappe.throw(_('Please generate keys for the Event Subscriber User {0} first.').format( + frappe.bold(get_link_to_form('User', self.user)) + )) + def on_update(self): if not self.incoming_change: - self.update_event_consumer() - self.create_custom_fields() + if frappe.db.exists('Event Producer', self.name): + if not self.api_key or not self.api_secret: + frappe.throw(_('Please set API Key and Secret on the producer and consumer sites first.')) + else: + self.update_event_consumer() + self.create_custom_fields() else: # when producer doc is updated it updates the consumer doc, set flag to avoid deadlock self.db_set('incoming_change', 0) @@ -50,15 +62,18 @@ class EventProducer(Document): def create_event_consumer(self): """register event consumer on the producer site""" if self.is_producer_online(): - producer_site = FrappeClient(self.producer_url, verify=False) + producer_site = FrappeClient( + url=self.producer_url, + api_key=self.api_key, + api_secret=self.get_password('api_secret') + ) + response = producer_site.post_api( 'frappe.event_streaming.doctype.event_consumer.event_consumer.register_consumer', params={'data': json.dumps(self.get_request_data())} ) if response: response = json.loads(response) - self.api_key = response['api_key'] - self.api_secret = response['api_secret'] self.last_update = response['last_update'] else: frappe.throw(_('Failed to create an Event Consumer or an Event Consumer for the current site is already registered.')) @@ -72,10 +87,14 @@ class EventProducer(Document): else: consumer_doctypes.append(entry.ref_doctype) + user_key = frappe.db.get_value('User', self.user, 'api_key') + user_secret = get_decrypted_password('User', self.user, 'api_secret') return { 'event_consumer': get_url(), 'consumer_doctypes': json.dumps(consumer_doctypes), - 'user': self.user + 'user': self.user, + 'api_key': user_key, + 'api_secret': user_secret } def create_custom_fields(self): @@ -131,8 +150,7 @@ def get_producer_site(producer_url): producer_site = FrappeClient( url=producer_url, api_key=producer_doc.api_key, - api_secret=producer_doc.get_password('api_secret'), - frappe_authorization_source='Event Consumer' + api_secret=producer_doc.get_password('api_secret') ) return producer_site diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 4fea55eb39..7724830bc7 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -8,6 +8,7 @@ import unittest import json from frappe.frappeclient import FrappeClient from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node +from frappe.core.doctype.user.user import generate_keys producer_url = 'http://test_site_producer:8000' @@ -171,8 +172,7 @@ class TestEventProducer(unittest.TestCase): producer_site = FrappeClient( url=producer_doc.producer_url, api_key=producer_doc.api_key, - api_secret=producer_doc.get_password('api_secret'), - frappe_authorization_source='Event Consumer' + api_secret=producer_doc.get_password('api_secret') ) return producer_site @@ -291,6 +291,27 @@ def get_mapping(mapping_name, local, remote, field_map): def create_event_producer(producer_url): + generate_keys('Administrator') + frappe.db.commit() + + producer_site = FrappeClient( + url=producer_url, + username='Administrator', + password='admin' + ) + + response = producer_site.post_api( + 'frappe.core.doctype.user.user.generate_keys', + params={'user': 'Administrator'} + ) + + response = json.loads(response) + api_secret = response.api_secret + + response = producer_site.get_value('User', 'api_key', {'name': 'Administrator'}) + response = json.loads(response) + api_key = response.api_key + if frappe.db.exists('Event Producer', producer_url): event_producer = frappe.get_doc('Event Producer', producer_url) for entry in event_producer.producer_doctypes: @@ -310,6 +331,8 @@ def create_event_producer(producer_url): 'use_same_name': 1 }) event_producer.user = 'Administrator' + event_producer.api_key = api_key + event_producer.api_secret = api_secret event_producer.save() def reset_configuration(producer_url): @@ -332,8 +355,7 @@ def get_remote_site(): producer_site = FrappeClient( url=producer_doc.producer_url, api_key=producer_doc.api_key, - api_secret=producer_doc.get_password('api_secret'), - frappe_authorization_source='Event Consumer' + api_secret=producer_doc.get_password('api_secret') ) return producer_site From ed5f332bb9f6946d4ff7d558f08a4b3ecd835192 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Sep 2020 19:29:46 +0530 Subject: [PATCH 18/31] fix: sider issue --- frappe/event_streaming/doctype/event_consumer/event_consumer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 122e11a3c5..000126bbed 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -71,7 +71,7 @@ def register_consumer(data): if not frappe.db.exists('User', user): frappe.throw(_('User {0} not found on the producer site').format(user)) - if not "System Manager" in frappe.get_roles(user): + if "System Manager" not in frappe.get_roles(user): frappe.throw(_("Event Subscriber has to be a System Manager.")) consumer = frappe.new_doc('Event Consumer') From 3b5c4043781f6bd8fbfd9f5a281dc7a009af6310 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Sep 2020 20:55:53 +0530 Subject: [PATCH 19/31] fix: tests --- .../event_producer/test_event_producer.py | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 7724830bc7..e20b6e53b3 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -167,15 +167,6 @@ class TestEventProducer(unittest.TestCase): def pull_producer_data(self): pull_from_node(producer_url) - def get_remote_site(self): - producer_doc = frappe.get_doc('Event Producer', producer_url) - producer_site = FrappeClient( - url=producer_doc.producer_url, - api_key=producer_doc.api_key, - api_secret=producer_doc.get_password('api_secret') - ) - return producer_site - def test_mapping(self): producer = get_remote_site() event_producer = frappe.get_doc('Event Producer', producer_url, for_update=True) @@ -291,13 +282,21 @@ def get_mapping(mapping_name, local, remote, field_map): def create_event_producer(producer_url): + if frappe.db.exists('Event Producer', producer_url): + event_producer = frappe.get_doc('Event Producer', producer_url) + for entry in event_producer.producer_doctypes: + entry.unsubscribe = 0 + event_producer.save() + return + generate_keys('Administrator') frappe.db.commit() producer_site = FrappeClient( url=producer_url, username='Administrator', - password='admin' + password='admin', + verify=False ) response = producer_site.post_api( @@ -305,19 +304,10 @@ def create_event_producer(producer_url): params={'user': 'Administrator'} ) - response = json.loads(response) - api_secret = response.api_secret + api_secret = response.get('api_secret') response = producer_site.get_value('User', 'api_key', {'name': 'Administrator'}) - response = json.loads(response) - api_key = response.api_key - - if frappe.db.exists('Event Producer', producer_url): - event_producer = frappe.get_doc('Event Producer', producer_url) - for entry in event_producer.producer_doctypes: - entry.unsubscribe = 0 - event_producer.save() - return + api_key = response.get('api_key') event_producer = frappe.new_doc('Event Producer') event_producer.producer_doctypes = [] @@ -354,8 +344,9 @@ def get_remote_site(): producer_doc = frappe.get_doc('Event Producer', producer_url) producer_site = FrappeClient( url=producer_doc.producer_url, - api_key=producer_doc.api_key, - api_secret=producer_doc.get_password('api_secret') + username='Administrator', + password='admin', + verify=False ) return producer_site From 220c4b734e4c0944b39e8bf88eb6e918a7c9dcf6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Sep 2020 22:56:18 +0530 Subject: [PATCH 20/31] fix: event subscriber validation --- frappe/event_streaming/doctype/event_producer/event_producer.py | 1 + .../doctype/event_producer/test_event_producer.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py index 5fb2c22d89..f69458731e 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/event_producer.py @@ -21,6 +21,7 @@ from frappe.integrations.oauth2 import validate_url class EventProducer(Document): def before_insert(self): self.check_url() + self.validate_event_subscriber() self.incoming_change = True self.create_event_consumer() self.create_custom_fields() diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index e20b6e53b3..d1fa76329f 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -290,7 +290,6 @@ def create_event_producer(producer_url): return generate_keys('Administrator') - frappe.db.commit() producer_site = FrappeClient( url=producer_url, From 21a299752bd9a0b12d71d53957b1b72e74682dd9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 9 Sep 2020 00:02:25 +0530 Subject: [PATCH 21/31] fix: test --- .../event_producer/test_event_producer.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index d1fa76329f..3ac053117d 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import unittest import json +import time from frappe.frappeclient import FrappeClient from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node from frappe.core.doctype.user.user import generate_keys @@ -291,12 +292,7 @@ def create_event_producer(producer_url): generate_keys('Administrator') - producer_site = FrappeClient( - url=producer_url, - username='Administrator', - password='admin', - verify=False - ) + producer_site = connect() response = producer_site.post_api( 'frappe.core.doctype.user.user.generate_keys', @@ -353,4 +349,18 @@ def unsubscribe_doctypes(producer_url): event_producer = frappe.get_doc('Event Producer', producer_url) for entry in event_producer.producer_doctypes: entry.unsubscribe = 1 - event_producer.save() \ No newline at end of file + event_producer.save() + +def connect(): + try: + producer_site = FrappeClient( + url=producer_url, + username='Administrator', + password='admin', + verify=False + ) + return producer_site + + except json.decoder.JSONDecodeError: + time.sleep(1) + connect() \ No newline at end of file From efdeab6dad292992755e9d6cedcb6535b2d93f1b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 9 Sep 2020 00:43:50 +0530 Subject: [PATCH 22/31] fix: test --- .../doctype/event_producer/test_event_producer.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 3ac053117d..843b343688 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import frappe import unittest import json -import time from frappe.frappeclient import FrappeClient from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node from frappe.core.doctype.user.user import generate_keys @@ -359,8 +358,7 @@ def connect(): password='admin', verify=False ) - return producer_site - - except json.decoder.JSONDecodeError: - time.sleep(1) - connect() \ No newline at end of file + except Exception: + connect() + else: + return producer_site \ No newline at end of file From 8cffea5c8e599e21be65a2e70b043d8a8414b134 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 9 Sep 2020 01:13:06 +0530 Subject: [PATCH 23/31] fix: test --- .../doctype/event_producer/test_event_producer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 843b343688..313ccbc4d6 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -361,4 +361,7 @@ def connect(): except Exception: connect() else: - return producer_site \ No newline at end of file + if not producer_site: + connect() + else: + return producer_site \ No newline at end of file From 386b4847b4afcf53d8d788008a0298c57721755a Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 9 Sep 2020 09:36:20 +0530 Subject: [PATCH 24/31] chore: Attempt at fixing event-streaming tests --- .../doctype/event_producer/test_event_producer.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 313ccbc4d6..fa2461a9d8 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -351,17 +351,14 @@ def unsubscribe_doctypes(producer_url): event_producer.save() def connect(): - try: - producer_site = FrappeClient( + def _connect(): + return FrappeClient( url=producer_url, username='Administrator', password='admin', verify=False ) + try: + return _connect() except Exception: - connect() - else: - if not producer_site: - connect() - else: - return producer_site \ No newline at end of file + return _connect() From f24d87726ae1bc797d51ca09b177cc72a84dddd0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 9 Sep 2020 11:10:47 +0530 Subject: [PATCH 25/31] fix: added patch for deleting old keys --- .../doctype/event_consumer/event_consumer.py | 4 ++++ .../doctype/event_producer/event_producer.py | 4 ++++ frappe/patches.txt | 1 + .../v13_0/delete_event_producer_and_consumer_keys.py | 11 +++++++++++ 4 files changed, 20 insertions(+) create mode 100644 frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 000126bbed..1505c3a05d 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -24,6 +24,10 @@ class EventConsumer(Document): def on_update(self): if not self.incoming_change: + doc_before_save = self.get_doc_before_save() + if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret: + return + self.update_consumer_status() else: frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py index f69458731e..b0ec998ab9 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/event_producer.py @@ -44,6 +44,10 @@ class EventProducer(Document): if not self.api_key or not self.api_secret: frappe.throw(_('Please set API Key and Secret on the producer and consumer sites first.')) else: + doc_before_save = self.get_doc_before_save() + if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret: + return + self.update_event_consumer() self.create_custom_fields() else: diff --git a/frappe/patches.txt b/frappe/patches.txt index 35389eee43..5dfe0c61b0 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -307,3 +307,4 @@ frappe.patches.v13_0.rename_notification_fields frappe.patches.v13_0.remove_duplicate_navbar_items frappe.patches.v13_0.enable_custom_script frappe.patches.v13_0.update_newsletter_content_type +frappe.patches.v13_0.delete_event_producer_and_consumer_keys \ No newline at end of file diff --git a/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py new file mode 100644 index 0000000000..1eba5871c2 --- /dev/null +++ b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py @@ -0,0 +1,11 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + if frappe.db.exists("DocType", "Event Producer"): + frappe.db.sql("""UPDATE `tabEvent Producer` SET api_key='', api_secret=''""") + if frappe.db.exists("DocType", "Event Consumer"): + frappe.db.sql("""UPDATE `tabEvent Consumer` SET api_key='', api_secret=''""") From a706971b9b47fb2fb3daa51333dc26cf7e9961d6 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 9 Sep 2020 11:35:33 +0530 Subject: [PATCH 26/31] fix(recorder): Fix Cypress Tests --- cypress/integration/recorder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js index ed2a9c86ba..8a4aeddd0a 100644 --- a/cypress/integration/recorder.js +++ b/cypress/integration/recorder.js @@ -61,10 +61,10 @@ context('Recorder', () => { cy.visit('/desk#recorder'); - cy.get('.list-row-container span').contains('frappe.desk.reportview.get').click(); + cy.get('.list-row-container span').contains('/api/method/frappe').click(); cy.location('hash').should('contain', '#recorder/request/'); - cy.get('form').should('contain', 'frappe.desk.reportview.get'); + cy.get('form').should('contain', '/api/method/frappe'); cy.get('#page-recorder .primary-action').should('contain', 'Stop').click(); cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click(); From 2b12a34d7ecc931bd148ccfa3b400285ead06746 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:45:17 +0530 Subject: [PATCH 27/31] refactor(translation linter): Handle multiline translation syntax (#11461) --- .github/frappe_linter/translation.py | 36 ++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/.github/frappe_linter/translation.py b/.github/frappe_linter/translation.py index d9fc98c76e..5d33355a1b 100644 --- a/.github/frappe_linter/translation.py +++ b/.github/frappe_linter/translation.py @@ -7,22 +7,28 @@ start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") # skip first argument files = sys.argv[1:] -for _file in files: - if not _file.endswith(('.py', '.js')): - continue +files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))] + +for _file in files_to_scan: with open(_file, 'r') as f: print(f'Checking: {_file}') - for num, line in enumerate(f, 1): - all_matches = start_pattern.finditer(line) - if all_matches: - for match in all_matches: - verify = pattern.search(line) - if not verify: - errors_encounter += 1 - print(f'A syntax error has been discovered at line number: {num}') - print(f'Syntax error occurred with: {line}') + file_lines = f.readlines() + for line_number, line in enumerate(file_lines, 1): + start_matches = start_pattern.search(line) + if start_matches: + match = pattern.search(line) + if not match and line.endswith(',\n'): + # concat remaining text to validate multiline pattern + line = "".join(file_lines[line_number - 1:]) + line = line[start_matches.start() + 1:] + match = pattern.match(line) + + if not match: + errors_encounter += 1 + print(f'\nTranslation syntax error at line number: {line_number + 1}\n{line.strip()[:100]}') + if errors_encounter > 0: - print('You can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.') - assert 1+1 == 3 + print('\nYou can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.') + sys.exit(1) else: - print('Good To Go!') + print('\nGood To Go!') From ee7d119546c488969a6893e9e343219f0c86f527 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 9 Sep 2020 14:03:56 +0530 Subject: [PATCH 28/31] fix: Escape invalid email in throw --- frappe/utils/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 1da220dc30..557a2fd647 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -135,7 +135,8 @@ def validate_email_address(email_str, throw=False): if not _valid: if throw: - frappe.throw(frappe._("{0} is not a valid Email Address").format(e), + invalid_email = frappe.utils.escape_html(e) + frappe.throw(frappe._("{0} is not a valid Email Address").format(invalid_email), frappe.InvalidEmailAddressError) return None else: From 3f42f4b5256e75618871aebb31183489ae6e24c4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 9 Sep 2020 17:12:38 +0530 Subject: [PATCH 29/31] fix: set overflow auto for report builder wrapper (cherry picked from commit 2ec014228637d40a23b794d108a282b8529e94b3) --- frappe/public/less/report.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/less/report.less b/frappe/public/less/report.less index f519640f7c..0a8d37b9ba 100644 --- a/frappe/public/less/report.less +++ b/frappe/public/less/report.less @@ -72,7 +72,7 @@ margin-bottom: 10px; } -.report-wrapper { +.report-wrapper, .datatable-wrapper { overflow: auto; } From 40748d7fa88c18f64600081b986d95f3b5cf0721 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 9 Sep 2020 23:42:46 +0530 Subject: [PATCH 30/31] fix: boot if module is not present in desk --- frappe/desk/desktop.py | 2 +- frappe/desk/doctype/desk_page/desk_page.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 94a38a5304..72c4519120 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -40,7 +40,7 @@ class Workspace: self.doc = self.get_page_for_user() - if self.doc.module not in self.allowed_modules: + if self.doc.module and self.doc.module not in self.allowed_modules: raise frappe.PermissionError self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items) diff --git a/frappe/desk/doctype/desk_page/desk_page.py b/frappe/desk/doctype/desk_page/desk_page.py index cc2db53481..e92844ac0b 100644 --- a/frappe/desk/doctype/desk_page/desk_page.py +++ b/frappe/desk/doctype/desk_page/desk_page.py @@ -38,7 +38,7 @@ class DeskPage(Document): pages = frappe.get_all("Desk Page", fields=["name", "module"], filters=filters, as_list=1) - return { page[1]: page[0] for page in pages } + return { page[1]: page[0] for page in pages if page[1] } def disable_saving_as_standard(): return frappe.flags.in_install or \ From 2b413319c1244d814fdd7d606dc3a1b3cec6f5f3 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 10 Sep 2020 12:36:59 +0530 Subject: [PATCH 31/31] fix(Dashboard Chart): only encode key if type is not string --- frappe/desk/doctype/dashboard_chart/dashboard_chart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 4ea61ec6a9..7e2d952928 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -60,11 +60,11 @@ def has_permission(doc, ptype, user): if doc.chart_type == 'Report': - allowed_reports = tuple([key.encode('UTF8') for key in get_allowed_reports()]) + allowed_reports = [key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()] if doc.report_name in allowed_reports: return True else: - allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read()) + allowed_doctypes = [frappe.permissions.get_doctypes_with_read()] if doc.document_type in allowed_doctypes: return True