From 072f0026c26b9821f615c8c470daf72f90a79b78 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 27 Jul 2020 20:38:29 +0530 Subject: [PATCH 001/154] 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 6a2248eef315fd5efe38d8a50ebd385725d4a5ce Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Fri, 7 Aug 2020 20:12:33 +0530 Subject: [PATCH 002/154] fix: move node-sass to dependencies from devDependencies node-sass is required in production for Website Theme yarn --prod will install only dependencies and reduce size fixes #11219 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6bb3ccda4..07cc91e011 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "jsbarcode": "^3.9.0", "moment": "^2.20.1", "moment-timezone": "^0.5.28", + "node-sass": "^4.13.1", "quagga": "^0.12.1", "quill": "2.0.0-dev.4", "qz-tray": "^2.0.8", @@ -56,7 +57,6 @@ "cypress-file-upload": "^3.1.0", "graphlib": "^2.1.8", "less": "^3.11.1", - "node-sass": "^4.13.1", "rollup": "^1.2.2", "rollup-plugin-buble": "^0.19.2", "rollup-plugin-commonjs": "^8.3.0", From af98a1ea38998f00d889b8e58ec2d1bc5119069b Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 17 Aug 2020 13:26:57 +0200 Subject: [PATCH 003/154] fix(login): namespace css and include in website --- frappe/public/scss/login.scss | 203 ++++++++++++++++++++++ frappe/public/scss/website.scss | 1 + frappe/templates/includes/login/login.css | 167 ------------------ frappe/www/login.html | 6 - 4 files changed, 204 insertions(+), 173 deletions(-) create mode 100644 frappe/public/scss/login.scss delete mode 100644 frappe/templates/includes/login/login.css diff --git a/frappe/public/scss/login.scss b/frappe/public/scss/login.scss new file mode 100644 index 0000000000..19d8b50343 --- /dev/null +++ b/frappe/public/scss/login.scss @@ -0,0 +1,203 @@ +/* login-css */ + +#page-login { + .hero-and-content { + /*background-color: #f5f7fa;*/ + background-color: #fafbfc; + } + + .page-sidebar, + #wrap-footer, + .page-header { + display: none; + } + + .page-content { + right: 0%; + width: 100%; + } + + .icon-twitter, + .icon-twitter-sign { + color: #00a0d1; + } + + .icon-linkedin, + .icon-linkedin-sign { + color: #4875b4; + } + + #wrap { + background-color: #7575ff; + } + + .for-login { + display: none; + } + + .for-forgot { + display: none; + } + + .for-signup { + display: none; + } + + .form-signin { + .form-signin-heading, + .checkbox { + margin-bottom: 10px; + } + .checkbox { + font-weight: normal; + } + .form-control { + position: relative; + height: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 6px; + font-size: 14px; + margin-bottom: 10px; + } + .form-control:focus { + z-index: 2; + } + } + + .btn-social { + margin: 10px; + } + + .social-logins .fa { + margin-right: 5px; + color: #8d99a6; + } + + .form-footer { + margin-top: -45px; + text-align: center; + font-size: 12px; + color: #8d99a6; + font-weight: bold; + + a { + font-size: 12px; + color: #8d99a6; + font-weight: bold; + } + + h6 { + font-size: 12px; + color: #8d99a6; + font-weight: bold; + } + + .btn-default { + color: #36414c; + } + } + + h5 { + position: relative; + text-align: center; + margin-top: 20px; + margin-bottom: 20px; + } + + p { + margin-bottom: 20px; + } + + .login-content .btn { + font-size: 14px; + margin-top: 45px; + } + + .page-card { + max-width: 360px; + padding: 15px; + margin: 70px auto; + border: 1px solid #d1d8dd; + border-radius: 4px; + background-color: #fff; + box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); + + .page-card-head { + padding: 10px 15px; + margin: -15px; + margin-bottom: 15px; + border-bottom: 1px solid #d1d8dd; + } + + .page-card-head .indicator { + color: #36414c; + font-size: 14px; + } + + .page-card-head .indicator::before { + margin: 0 6px 0.5px 0px; + } + + .btn { + margin-top: 30px; + } + } + + .bordered { + border: 1px solid #d1d8dd; + padding: 10px; + border-radius: 4px; + } + + .toggle-password { + right: 9px; + top: 9px; + position: absolute; + z-index: 2; + cursor: pointer; + font-size: 12px; + } + + .invalid-login { + -webkit-animation: wiggle 0.5s linear; + animation: wiggle 0.5s linear; + } + + @-webkit-keyframes wiggle { + 8%, 41% { + -webkit-transform: translateX(-10px); + } + 25%, 58% { + -webkit-transform: translateX(10px); + } + 75% { + -webkit-transform: translateX(-5px); + } + 92% { + -webkit-transform: translateX(5px); + } + 0%, 100% { + -webkit-transform: translateX(0); + } + } + + @keyframes wiggle { + 8%, 41% { + transform: translate(-10px); + } + 25%, 58% { + transform: translate(10px); + } + 75% { + transform: translate(-5px); + } + 92% { + transform: translate(5px); + } + 0%, 100% { + transform: translate(0); + } + } +} diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss index 3c11d23252..f0e9540a3e 100644 --- a/frappe/public/scss/website.scss +++ b/frappe/public/scss/website.scss @@ -11,6 +11,7 @@ @import 'sidebar'; @import 'portal'; @import 'doc'; +@import 'login'; .ql-editor.read-mode { padding: 0; diff --git a/frappe/templates/includes/login/login.css b/frappe/templates/includes/login/login.css deleted file mode 100644 index c336ae742e..0000000000 --- a/frappe/templates/includes/login/login.css +++ /dev/null @@ -1,167 +0,0 @@ -/* login-css */ - -.hero-and-content { - /*background-color: #f5f7fa;*/ - background-color: #fafbfc; -} - -.page-sidebar, #wrap-footer, .page-header { - display: none; -} - -.page-content { - right: 0%; - width: 100%; -} - -.icon-twitter, .icon-twitter-sign{ - color: #00a0d1; -} - -.icon-linkedin, .icon-linkedin-sign{ - color: #4875B4; -} - -#wrap { - background-color: #7575ff; -} - -.for-login { - display: none; -} - -.for-forgot { - display: none; -} - -.for-signup { - display: none; -} - -.form-signin .form-signin-heading, -.form-signin .checkbox { - margin-bottom: 10px; -} -.form-signin .checkbox { - font-weight: normal; -} -.form-signin .form-control { - position: relative; - height: auto; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 6px; - font-size: 14px; - margin-bottom: 10px; -} -.form-signin .form-control:focus { - z-index: 2; -} - -.btn-social { - margin: 10px; -} - -.social-logins .fa { - margin-right: 5px; - color: #8D99A6; -} - -.form-footer { - margin-top: -45px; - text-align: center; -} - -.form-footer, .form-footer a, .form-footer h6 { - font-size: 12px; - color: #8D99A6; - font-weight: bold; -} - -.form-footer .btn-default { - color: #36414C; -} - -h5 { - position: relative; - text-align: center; - margin-top:20px; - margin-bottom:20px; -} - -p { - margin-bottom:20px; -} - -.login-content .btn { - font-size: 14px; - margin-top: 45px; -} - -.page-card { - max-width: 360px; - padding: 15px; - margin: 70px auto; - border: 1px solid #d1d8dd; - border-radius: 4px; - background-color: #fff; - box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); -} -.page-card .page-card-head { - padding: 10px 15px; - margin: -15px; - margin-bottom: 15px; - border-bottom: 1px solid #d1d8dd; -} -.page-card .page-card-head .indicator { - color: #36414C; - font-size: 14px; -} -.page-card .page-card-head .indicator::before { - margin: 0 6px 0.5px 0px; -} -.page-card .btn { - margin-top: 30px; -} - -.bordered { - border: 1px solid #d1d8dd; - padding: 10px; - border-radius: 4px; -} - -.toggle-password { - right: 9px; - top: 9px; - position: absolute; - z-index: 2; - cursor: pointer; - font-size: 12px; -} - -.invalid-login { - -webkit-animation: wiggle 0.5s linear; -} - -@-webkit-keyframes wiggle { - 8%, - 41% { - -webkit-transform: translateX(-10px); - } - 25%, - 58% { - -webkit-transform: translateX(10px); - } - 75% { - -webkit-transform: translateX(-5px); - } - 92% { - -webkit-transform: translateX(5px); - } - 0%, - 100% { - -webkit-transform: translateX(0); - } -} - diff --git a/frappe/www/login.html b/frappe/www/login.html index 9d00892b18..96b5bc7026 100644 --- a/frappe/www/login.html +++ b/frappe/www/login.html @@ -1,11 +1,5 @@ {% extends "templates/web.html" %} -{% block style %} - -{% endblock %} - {% block page_content %}
From cb2b07ceae75d7eeb06681c6d6652643b3305a94 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 18 Aug 2020 15:41:59 +0530 Subject: [PATCH 004/154] refactor: rename Minutes to First Response to First Response Time --- frappe/core/doctype/communication/communication.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 232d485f36..fc929351d4 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -455,18 +455,18 @@ def update_parent_document_on_communication(doc): # update the modified date for document parent.update_modified() - update_mins_to_first_communication(parent, doc) + update_first_response_time(parent, doc) set_avg_response_time(parent, doc) parent.run_method("notify_communication", doc) parent.notify_update() -def update_mins_to_first_communication(parent, communication): - if parent.meta.has_field("mins_to_first_response") and not parent.get("mins_to_first_response"): +def update_first_response_time(parent, communication): + if parent.meta.has_field("first_response_time") and not parent.get("first_response_time"): if is_system_user(communication.sender): first_responded_on = communication.creation if parent.meta.has_field("first_responded_on") and communication.sent_or_received == "Sent": parent.db_set("first_responded_on", first_responded_on) - parent.db_set("mins_to_first_response", round(time_diff_in_seconds(first_responded_on, parent.creation) / 60), 2) + parent.db_set("first_response_time", round(time_diff_in_seconds(first_responded_on, parent.creation), 2)) def set_avg_response_time(parent, communication): if parent.meta.has_field("avg_response_time") and communication.sent_or_received == "Sent": From aae0309cedf567ef4a2f3c4dd7c9ca5eb79096e9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 19 Aug 2020 14:19:52 +0530 Subject: [PATCH 005/154] 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 f48470bb72428a7a9e60c689de2bb03f4094c8ac Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 20 Aug 2020 11:51:56 +0530 Subject: [PATCH 006/154] refactor: Schedule Job Type * Updating Schedule Job Type when scheduler_events in hooks change * Doesn't update objects defined in other functions. Eliminating possible bugs --- .../scheduled_job_type/scheduled_job_type.py | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 765ae5fe93..f8b50ea292 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -124,42 +124,49 @@ def run_scheduled_job(job_type): def sync_jobs(): frappe.reload_doc('core', 'doctype', 'scheduled_job_type') - all_events = [] - scheduler_events = frappe.get_hooks("scheduler_events") - insert_events(all_events, scheduler_events) - clear_events(all_events, scheduler_events) + scheduler_events = frappe.get_hooks('scheduler_events') + all_events = insert_events(scheduler_events) + clear_events(all_events) -def insert_events(all_events, scheduler_events): +def insert_events(scheduler_events): + cron_jobs, event_jobs = [], [] for event_type in scheduler_events: events = scheduler_events.get(event_type) if isinstance(events, dict): - insert_cron_event(events, all_events) + cron_jobs += insert_cron_jobs(events) else: # hourly, daily etc - insert_event_list(events, event_type, all_events) + event_jobs += insert_event_jobs(events, event_type) + return cron_jobs + event_jobs -def insert_cron_event(events, all_events): +def insert_cron_jobs(events): + cron_jobs = [] for cron_format in events: for event in events.get(cron_format): - all_events.append(event) + cron_jobs.append(event) insert_single_event('Cron', event, cron_format) + return cron_jobs -def insert_event_list(events, event_type, all_events): +def insert_event_jobs(events, event_type): + event_jobs = [] for event in events: - all_events.append(event) + event_jobs.append(event) frequency = event_type.replace('_', ' ').title() insert_single_event(frequency, event) + return event_jobs -def insert_single_event(frequency, event, cron_format = None): - if not frappe.db.exists('Scheduled Job Type', dict(method=event)): - frappe.get_doc(dict( - doctype = 'Scheduled Job Type', - method = event, - cron_format = cron_format, - frequency = frequency - )).insert() +def insert_single_event(frequency, event, cron_format=None): + cron_expr = {'cron_format': cron_format} if cron_format else {} -def clear_events(all_events, scheduler_events): + if not frappe.db.exists('Scheduled Job Type', {'method': event, 'frequency': frequency, **cron_expr }): + frappe.get_doc({ + 'doctype': 'Scheduled Job Type', + 'method': event, + 'cron_format': cron_format, + 'frequency': frequency + }).insert() + +def clear_events(all_events): for event in frappe.get_all('Scheduled Job Type', ('name', 'method')): if event.method not in all_events: frappe.delete_doc('Scheduled Job Type', event.name) From 7bcb0b53773b48a173861bff5e70dbabc62a12f0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 20 Aug 2020 11:54:29 +0530 Subject: [PATCH 007/154] style: Consistent spacing and quotes --- .../scheduled_job_type/scheduled_job_type.py | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index f8b50ea292..649c79df88 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -109,12 +109,14 @@ class ScheduledJobType(Document): def on_trash(self): frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) + @frappe.whitelist() def execute_event(doc): frappe.only_for('System Manager') doc = json.loads(doc) frappe.get_doc('Scheduled Job Type', doc.get('name')).enqueue() + def run_scheduled_job(job_type): '''This is a wrapper function that runs a hooks.scheduler_events method''' try: @@ -122,12 +124,14 @@ def run_scheduled_job(job_type): except Exception: print(frappe.get_traceback()) + def sync_jobs(): - frappe.reload_doc('core', 'doctype', 'scheduled_job_type') - scheduler_events = frappe.get_hooks('scheduler_events') + frappe.reload_doc("core", "doctype", "scheduled_job_type") + scheduler_events = frappe.get_hooks("scheduler_events") all_events = insert_events(scheduler_events) clear_events(all_events) + def insert_events(scheduler_events): cron_jobs, event_jobs = [], [] for event_type in scheduler_events: @@ -139,14 +143,16 @@ def insert_events(scheduler_events): event_jobs += insert_event_jobs(events, event_type) return cron_jobs + event_jobs + def insert_cron_jobs(events): cron_jobs = [] for cron_format in events: for event in events.get(cron_format): cron_jobs.append(event) - insert_single_event('Cron', event, cron_format) + insert_single_event("Cron", event, cron_format) return cron_jobs + def insert_event_jobs(events, event_type): event_jobs = [] for event in events: @@ -155,18 +161,20 @@ def insert_event_jobs(events, event_type): insert_single_event(frequency, event) return event_jobs -def insert_single_event(frequency, event, cron_format=None): - cron_expr = {'cron_format': cron_format} if cron_format else {} - if not frappe.db.exists('Scheduled Job Type', {'method': event, 'frequency': frequency, **cron_expr }): +def insert_single_event(frequency, event, cron_format=None): + cron_expr = {"cron_format": cron_format} if cron_format else {} + + if not frappe.db.exists("Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr }): frappe.get_doc({ - 'doctype': 'Scheduled Job Type', - 'method': event, - 'cron_format': cron_format, - 'frequency': frequency + "doctype": "Scheduled Job Type", + "method": event, + "cron_format": cron_format, + "frequency": frequency }).insert() + def clear_events(all_events): - for event in frappe.get_all('Scheduled Job Type', ('name', 'method')): + for event in frappe.get_all("Scheduled Job Type", ("name", "method")): if event.method not in all_events: - frappe.delete_doc('Scheduled Job Type', event.name) + frappe.delete_doc("Scheduled Job Type", event.name) From 557bceed027a032c1ec2c3ba6941a4b381b64948 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 20 Aug 2020 12:03:03 +0530 Subject: [PATCH 008/154] 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 009/154] 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 010/154] 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 011/154] 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 bb11a55b5666957ac2777bec0aed6fb8d5d43c31 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 21 Aug 2020 16:23:20 +0530 Subject: [PATCH 012/154] test: Added tests to check if sync jobs updates on changes in hooks --- .../scheduled_job_type/scheduled_job_type.py | 4 ++-- .../scheduled_job_type/test_scheduled_job_type.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 649c79df88..445cf91649 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -125,9 +125,9 @@ def run_scheduled_job(job_type): print(frappe.get_traceback()) -def sync_jobs(): +def sync_jobs(hooks=None): frappe.reload_doc("core", "doctype", "scheduled_job_type") - scheduler_events = frappe.get_hooks("scheduler_events") + scheduler_events = hooks or frappe.get_hooks("scheduler_events") all_events = insert_events(scheduler_events) clear_events(all_events) diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index ec1e70ad6a..3ff639e854 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -32,6 +32,20 @@ class TestScheduledJobType(unittest.TestCase): self.assertEqual(cron_job.frequency, 'Cron') self.assertEqual(cron_job.cron_format, '0/15 * * * *') + # check if jobs are synced after change in hooks + scheduler_events = { "cron": { "0/15 * * * *": ["frappe.email.queue.flush"] } } + sync_jobs(scheduler_events) + frappe.db.commit() + scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"}) + self.assertEqual(scheduled_job.frequency, "Cron") + self.assertEqual(scheduled_job.cron_format, "0/15 * * * *") + + updated_scheduler_events = { "hourly": ["frappe.email.queue.flush"] } + sync_jobs(updated_scheduler_events) + frappe.db.commit() + updated_scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"}) + self.assertEqual(scheduled_job.frequency, "Hourly") + def test_daily_job(self): job = frappe.get_doc('Scheduled Job Type', dict(method = 'frappe.email.queue.clear_outbox')) job.db_set('last_execution', '2019-01-01 00:00:00') From 5948248740c7880e30cb7c988155f5265ff1f101 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 21 Aug 2020 16:27:52 +0530 Subject: [PATCH 013/154] fix: Use correct document to test --- .../core/doctype/scheduled_job_type/test_scheduled_job_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index 3ff639e854..e5e92f9e5b 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -44,7 +44,7 @@ class TestScheduledJobType(unittest.TestCase): sync_jobs(updated_scheduler_events) frappe.db.commit() updated_scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"}) - self.assertEqual(scheduled_job.frequency, "Hourly") + self.assertEqual(updated_scheduled_job.frequency, "Hourly") def test_daily_job(self): job = frappe.get_doc('Scheduled Job Type', dict(method = 'frappe.email.queue.clear_outbox')) From e9429aacff56a4fa0cb0a1c6095517926e47b811 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 21 Aug 2020 16:42:26 +0530 Subject: [PATCH 014/154] fix: Remove unnecessary addition --- .../doctype/scheduled_job_type/test_scheduled_job_type.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index e5e92f9e5b..e580837810 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -33,13 +33,6 @@ class TestScheduledJobType(unittest.TestCase): self.assertEqual(cron_job.cron_format, '0/15 * * * *') # check if jobs are synced after change in hooks - scheduler_events = { "cron": { "0/15 * * * *": ["frappe.email.queue.flush"] } } - sync_jobs(scheduler_events) - frappe.db.commit() - scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"}) - self.assertEqual(scheduled_job.frequency, "Cron") - self.assertEqual(scheduled_job.cron_format, "0/15 * * * *") - updated_scheduler_events = { "hourly": ["frappe.email.queue.flush"] } sync_jobs(updated_scheduler_events) frappe.db.commit() From 88e4ab2bcaa74e7f13b89923a332688999d26c4e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 21 Aug 2020 17:12:01 +0530 Subject: [PATCH 015/154] fix: Delete if DuplicateEntry and insert again Sometimes I add a fix for something. After some time, I forget the reason for it. And when something breaks, I remember why I added it in the first place. This is one of those times. --- .../scheduled_job_type/scheduled_job_type.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 445cf91649..fa854f579e 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -164,14 +164,19 @@ def insert_event_jobs(events, event_type): def insert_single_event(frequency, event, cron_format=None): cron_expr = {"cron_format": cron_format} if cron_format else {} + doc = frappe.get_doc({ + "doctype": "Scheduled Job Type", + "method": event, + "cron_format": cron_format, + "frequency": frequency + }) if not frappe.db.exists("Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr }): - frappe.get_doc({ - "doctype": "Scheduled Job Type", - "method": event, - "cron_format": cron_format, - "frequency": frequency - }).insert() + try: + doc.insert() + except frappe.DuplicateEntryError: + doc.delete() + doc.insert() def clear_events(all_events): From e3580357820653946b3875c32e90db56fccfdb9c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 21 Aug 2020 17:44:55 +0530 Subject: [PATCH 016/154] test: Rollback and sync jobs for each test --- .../scheduled_job_type/test_scheduled_job_type.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index e580837810..e7db6f9045 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -11,11 +11,10 @@ from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs class TestScheduledJobType(unittest.TestCase): def setUp(self): - if not frappe.get_all('Scheduled Job Type', limit=1): - frappe.db.rollback() - frappe.db.sql('truncate `tabScheduled Job Type`') - sync_jobs() - frappe.db.commit() + frappe.db.rollback() + frappe.db.sql('truncate `tabScheduled Job Type`') + sync_jobs() + frappe.db.commit() def test_sync_jobs(self): all_job = frappe.get_doc('Scheduled Job Type', @@ -35,7 +34,6 @@ class TestScheduledJobType(unittest.TestCase): # check if jobs are synced after change in hooks updated_scheduler_events = { "hourly": ["frappe.email.queue.flush"] } sync_jobs(updated_scheduler_events) - frappe.db.commit() updated_scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"}) self.assertEqual(updated_scheduled_job.frequency, "Hourly") From 8143f551663cf4c9c478dcedfc41928022acbc73 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 24 Aug 2020 22:22:18 +0530 Subject: [PATCH 017/154] fix: Bundle minified version of vue in libs.min.js --- frappe/public/build.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/build.json b/frappe/public/build.json index 997a3092ad..844e436e43 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -128,7 +128,7 @@ "public/js/lib/Sortable.min.js", "public/js/lib/jquery/jquery.hotkeys.js", "public/js/lib/bootstrap.min.js", - "node_modules/vue/dist/vue.js", + "node_modules/vue/dist/vue.min.js", "node_modules/moment/min/moment-with-locales.min.js", "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js", "public/js/lib/socket.io.min.js", From 77bb439429ac0a625395db280d2639ba34a4577b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 4 Aug 2020 13:54:54 +0530 Subject: [PATCH 018/154] feat: don't let users override route for blog category --- frappe/website/doctype/blog_category/blog_category.py | 3 +-- frappe/website/doctype/blog_post/blog_post.py | 2 +- frappe/website/doctype/blog_post/templates/blog_post.html | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index a293158a09..aa5287bcb6 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -14,6 +14,5 @@ class BlogCategory(WebsiteGenerator): clear_cache() def validate(self): - if not self.route: - self.route = 'blog/' + self.scrub(self.name) + self.route = 'blog/' + self.scrub(self.name) super(BlogCategory, self).validate() diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index beffcdca25..bd9c801b08 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -99,7 +99,7 @@ class BlogPost(WebsiteGenerator): self.load_comments(context) context.category = frappe.db.get_value("Blog Category", - context.doc.blog_category, ["title", "route"], as_dict=1) + context.doc.blog_category, ["title", "name"], as_dict=1) context.parents = [{"name": _("Home"), "route":"/"}, {"name": "Blog", "route": "/blog"}, {"label": context.category.title, "route":context.category.route}] diff --git a/frappe/website/doctype/blog_post/templates/blog_post.html b/frappe/website/doctype/blog_post/templates/blog_post.html index dd3e59c3c1..c5580603d0 100644 --- a/frappe/website/doctype/blog_post/templates/blog_post.html +++ b/frappe/website/doctype/blog_post/templates/blog_post.html @@ -12,7 +12,7 @@

{{ title }}

From 494e95d0735debc9e75504721197d5b6ddec0a72 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 4 Aug 2020 14:01:54 +0530 Subject: [PATCH 019/154] feat: enhancements to blog category * changed order of fields * make route read only * add description to category name --- .../website/doctype/blog_category/blog_category.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json index b2180047cd..ec5b48cdf1 100644 --- a/frappe/website/doctype/blog_category/blog_category.json +++ b/frappe/website/doctype/blog_category/blog_category.json @@ -8,13 +8,14 @@ "document_type": "Setup", "engine": "InnoDB", "field_order": [ - "category_name", "title", + "category_name", "published", "route" ], "fields": [ { + "description": "Name used in URLs, example \"product-updates\"", "fieldname": "category_name", "fieldtype": "Data", "in_list_view": 1, @@ -31,7 +32,7 @@ "reqd": 1 }, { - "default": "0", + "default": "1", "fieldname": "published", "fieldtype": "Check", "in_list_view": 1, @@ -42,15 +43,17 @@ "fieldname": "route", "fieldtype": "Data", "label": "Route", + "read_only": 1, "unique": 1 } ], "has_web_view": 1, "icon": "fa fa-tag", "idx": 1, + "index_web_pages_for_search": 1, "is_published_field": "published", "links": [], - "modified": "2020-07-29 21:14:47.210446", + "modified": "2020-08-04 14:00:19.842606", "modified_by": "Administrator", "module": "Website", "name": "Blog Category", @@ -78,5 +81,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "title_field": "title", "track_changes": 1 } \ No newline at end of file From 2e81bccd3c693a97ccb3f519d1b3882cb13b8865 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 4 Aug 2020 14:02:14 +0530 Subject: [PATCH 020/154] feat: don't let users override route --- frappe/website/doctype/blog_category/blog_category.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index aa5287bcb6..47b363383a 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -13,6 +13,5 @@ class BlogCategory(WebsiteGenerator): def on_update(self): clear_cache() - def validate(self): + def set_route(self): self.route = 'blog/' + self.scrub(self.name) - super(BlogCategory, self).validate() From 88c4838458a19672853862bd85cf6f99b457e2c0 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 4 Aug 2020 14:07:43 +0530 Subject: [PATCH 021/154] feat: patch to set route for blog category --- frappe/patches.txt | 1 + frappe/patches/v13_0/set_route_for_blog_category.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 frappe/patches/v13_0/set_route_for_blog_category.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 985911314e..310f88baa9 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -304,3 +304,4 @@ frappe.patches.v13_0.increase_password_length frappe.patches.v13_0.add_toggle_width_in_navbar_settings frappe.patches.v13_0.rename_notification_fields frappe.patches.v13_0.remove_duplicate_navbar_items +frappe.patches.v13_0.set_route_for_blog_category diff --git a/frappe/patches/v13_0/set_route_for_blog_category.py b/frappe/patches/v13_0/set_route_for_blog_category.py new file mode 100644 index 0000000000..7ea26bc2c0 --- /dev/null +++ b/frappe/patches/v13_0/set_route_for_blog_category.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + categories = frappe.get_list("Blog Category") + for category in categories: + doc = frappe.get_doc("Blog Category", category["name"]) + doc.set_route() + doc.save() From de0bde046a0546c89295855960055e1d43ed7e69 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 4 Aug 2020 14:07:57 +0530 Subject: [PATCH 022/154] feat: add test to check blog route --- frappe/website/doctype/blog_category/blog_category.py | 1 + .../website/doctype/blog_category/test_blog_category.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index 47b363383a..b036b561d0 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -14,4 +14,5 @@ class BlogCategory(WebsiteGenerator): clear_cache() def set_route(self): + # Override blog route since it has to been templated self.route = 'blog/' + self.scrub(self.name) diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index d033b84786..6b0bd60c82 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -4,4 +4,11 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Blog Category') \ No newline at end of file +class TestBlogCategory(unittest.TestCase): + def test_route(self): + cat = frappe.new_doc("Blog Categroy", { + "title": "_Yet Another Category", + "category_name": "test-category-yet-another-category", + }) + cat.insert() + self.assertEqual(cat.route, 'blog/test-category-yet-another-category') From 57b5f2fbbbf67206538a77a2d48be9bafa26365e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 11 Aug 2020 11:51:51 +0530 Subject: [PATCH 023/154] feat: don't scrub name when setting route --- frappe/website/doctype/blog_category/blog_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index b036b561d0..323217dad1 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -15,4 +15,4 @@ class BlogCategory(WebsiteGenerator): def set_route(self): # Override blog route since it has to been templated - self.route = 'blog/' + self.scrub(self.name) + self.route = 'blog/' + self.name From 2dc96252bd7126a6af1a98b3be5774470abbe6f5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 11 Aug 2020 14:23:47 +0530 Subject: [PATCH 024/154] feat(blog): remove category name field --- .../doctype/blog_category/blog_category.json | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json index ec5b48cdf1..c5114a811b 100644 --- a/frappe/website/doctype/blog_category/blog_category.json +++ b/frappe/website/doctype/blog_category/blog_category.json @@ -2,27 +2,16 @@ "actions": [], "allow_guest_to_view": 1, "allow_import": 1, - "autoname": "field:category_name", "creation": "2013-03-08 09:41:11", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", "field_order": [ "title", - "category_name", "published", "route" ], "fields": [ - { - "description": "Name used in URLs, example \"product-updates\"", - "fieldname": "category_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Category Name", - "reqd": 1, - "unique": 1 - }, { "fieldname": "title", "fieldtype": "Data", @@ -53,7 +42,7 @@ "index_web_pages_for_search": 1, "is_published_field": "published", "links": [], - "modified": "2020-08-04 14:00:19.842606", + "modified": "2020-08-11 11:57:19.672807", "modified_by": "Administrator", "module": "Website", "name": "Blog Category", From 5df22dcdcc26ebcca71253798ea9d67a2f588c76 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 11 Aug 2020 14:29:06 +0530 Subject: [PATCH 025/154] feat: update test records --- frappe/core/doctype/user_permission/test_user_permission.py | 3 +-- frappe/website/doctype/blog_category/test_blog_category.py | 3 +-- frappe/website/doctype/blog_category/test_records.json | 3 --- frappe/website/doctype/blog_post/test_blog_post.py | 3 +-- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 5ccc8752cf..82dd2ab27e 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -26,8 +26,7 @@ class TestUserPermission(unittest.TestCase): user = create_user('test_user_perm1@example.com', 'Website Manager') for category in ['general', 'public']: if not frappe.db.exists('Blog Category', category): - frappe.get_doc({'doctype': 'Blog Category', - 'category_name': category, 'title': category}).insert() + frappe.get_doc({'doctype': 'Blog Category', 'title': category}).insert() param = get_params(user, 'Blog Category', 'general', is_default=1) add_user_permissions(param) diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index 6b0bd60c82..d9c001ee73 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -7,8 +7,7 @@ import frappe class TestBlogCategory(unittest.TestCase): def test_route(self): cat = frappe.new_doc("Blog Categroy", { - "title": "_Yet Another Category", - "category_name": "test-category-yet-another-category", + "title": "Test Category Yet Another Category", }) cat.insert() self.assertEqual(cat.route, 'blog/test-category-yet-another-category') diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json index 3334bbc4f9..4bd4ac35b7 100644 --- a/frappe/website/doctype/blog_category/test_records.json +++ b/frappe/website/doctype/blog_category/test_records.json @@ -1,18 +1,15 @@ [ { - "category_name": "_Test Blog Category", "doctype": "Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category" }, { - "category_name": "_Test Blog Category 1", "doctype": "Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category 1" }, { - "category_name": "_Test Blog Category 2", "doctype": "Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category 2" diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 15634a7caf..c73cf2caa0 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -36,7 +36,6 @@ def make_test_blog(): if not frappe.db.exists('Blog Category', 'Test Blog Category'): frappe.get_doc(dict( doctype = 'Blog Category', - category_name = 'Test Blog Category', title='Test Blog Category')).insert() if not frappe.db.exists('Blogger', 'test-blogger'): frappe.get_doc(dict( @@ -45,7 +44,7 @@ def make_test_blog(): full_name='Test Blogger')).insert() test_blog = frappe.get_doc(dict( doctype = 'Blog Post', - blog_category = 'Test Blog Category', + blog_category = 'test-blog-category', blogger = 'test-blogger', title = random_string(20), route = random_string(20), From fa6ef9731d33c8477c92dcc7d4edb2edcc85df33 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 11 Aug 2020 14:29:18 +0530 Subject: [PATCH 026/154] feat: set name using scrubbed title --- frappe/website/doctype/blog_category/blog_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index 323217dad1..375ba5b6a3 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -8,7 +8,7 @@ from frappe.website.render import clear_cache class BlogCategory(WebsiteGenerator): def autoname(self): # to override autoname of WebsiteGenerator - self.name = self.category_name + self.name = self.scrub(self.title) def on_update(self): clear_cache() From 425d29f314e61833aa1aea72758c42ab9ec36140 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Sat, 18 Jul 2020 16:56:07 +0530 Subject: [PATCH 027/154] test(blog): Blog Post should have correct link to the Blog Category page --- .../doctype/blog_post/test_blog_post.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index c73cf2caa0..4b4d062a2d 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe import unittest +from bs4 import BeautifulSoup from frappe.utils import set_request from frappe.website.render import render @@ -32,8 +33,36 @@ class TestBlogPost(unittest.TestCase): self.assertTrue(response.status_code, 404) + def test_category_link(self): + # Make a temporary Blog Post (and a Blog Category) + blog = make_test_blog() + + # Visit the blog post page + set_request(path=blog.route) + blog_page_response = render() + blog_page_html = frappe.safe_decode(blog_page_response.get_data()) + + # On blog post page find link to the category page + soup = BeautifulSoup(blog_page_html, "lxml") + category_title = frappe.db.get_value("Blog Category", blog.blog_category, "title") + category_page_link = list(soup.find_all('a', string=category_title))[0] + category_page_url = category_page_link["href"] + + # Visit the category page (by following the link found in above stage) + set_request(path=category_page_url) + category_page_response = render() + category_page_html = frappe.safe_decode(category_page_response.get_data()) + + # Category page should contain the blog post title + self.assertIn(blog.title, category_page_html) + + # Cleanup afterwords + frappe.delete_doc("Blog Post", blog.name) + frappe.delete_doc("Blog Category", blog.blog_category) + def make_test_blog(): if not frappe.db.exists('Blog Category', 'Test Blog Category'): + # Set different title and name for the category frappe.get_doc(dict( doctype = 'Blog Category', title='Test Blog Category')).insert() From 534cdb4c83b9ae784c02f3daa95deeaeda55ab97 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 21 Aug 2020 12:06:24 +0530 Subject: [PATCH 028/154] feat: allow renaming blog category --- frappe/website/doctype/blog_category/blog_category.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json index c5114a811b..67e17f49fb 100644 --- a/frappe/website/doctype/blog_category/blog_category.json +++ b/frappe/website/doctype/blog_category/blog_category.json @@ -2,6 +2,7 @@ "actions": [], "allow_guest_to_view": 1, "allow_import": 1, + "allow_rename": 1, "creation": "2013-03-08 09:41:11", "doctype": "DocType", "document_type": "Setup", @@ -42,7 +43,7 @@ "index_web_pages_for_search": 1, "is_published_field": "published", "links": [], - "modified": "2020-08-11 11:57:19.672807", + "modified": "2020-08-21 11:40:36.919321", "modified_by": "Administrator", "module": "Website", "name": "Blog Category", From 43c8d9886c49802dbba2759fcf70ddf579da796a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 21 Aug 2020 12:06:43 +0530 Subject: [PATCH 029/154] feat: remove test --- frappe/website/doctype/blog_category/test_blog_category.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index d9c001ee73..38b7ea31a0 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -5,9 +5,4 @@ from __future__ import unicode_literals import frappe class TestBlogCategory(unittest.TestCase): - def test_route(self): - cat = frappe.new_doc("Blog Categroy", { - "title": "Test Category Yet Another Category", - }) - cat.insert() - self.assertEqual(cat.route, 'blog/test-category-yet-another-category') + pass From 5d15dff428716646f90ec7c14fdb873da164f578 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 21 Aug 2020 12:07:05 +0530 Subject: [PATCH 030/154] feat: use category route directly --- frappe/website/doctype/blog_post/blog_post.py | 2 +- frappe/website/doctype/blog_post/templates/blog_post.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index bd9c801b08..beffcdca25 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -99,7 +99,7 @@ class BlogPost(WebsiteGenerator): self.load_comments(context) context.category = frappe.db.get_value("Blog Category", - context.doc.blog_category, ["title", "name"], as_dict=1) + context.doc.blog_category, ["title", "route"], as_dict=1) context.parents = [{"name": _("Home"), "route":"/"}, {"name": "Blog", "route": "/blog"}, {"label": context.category.title, "route":context.category.route}] diff --git a/frappe/website/doctype/blog_post/templates/blog_post.html b/frappe/website/doctype/blog_post/templates/blog_post.html index c5580603d0..8244b9e6c2 100644 --- a/frappe/website/doctype/blog_post/templates/blog_post.html +++ b/frappe/website/doctype/blog_post/templates/blog_post.html @@ -12,7 +12,7 @@

{{ title }}

From 877260610330c7c4ba8bc0cb3f85bd6350dbb3e4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 21 Aug 2020 12:11:36 +0530 Subject: [PATCH 031/154] fix: document None is not subscriptable --- frappe/search/full_text_search.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/search/full_text_search.py b/frappe/search/full_text_search.py index dd6e69111d..ecb018dbb4 100644 --- a/frappe/search/full_text_search.py +++ b/frappe/search/full_text_search.py @@ -46,7 +46,8 @@ class FullTextSearch: doc_name (str): name of the document to be updated """ document = self.get_document_to_index(doc_name) - self.update_index(document) + if document: + self.update_index(document) def remove_document_from_index(self, doc_name): """Remove document from search index From 87c6d413fab29440c9b1fe3f89d278869447505d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 24 Aug 2020 14:04:22 +0530 Subject: [PATCH 032/154] fix: import error --- frappe/website/doctype/blog_category/test_blog_category.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index 38b7ea31a0..fe8f4544cd 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +import unittest class TestBlogCategory(unittest.TestCase): pass From 1356609f06fc9ff66839484466bda9c838249893 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 24 Aug 2020 15:26:49 +0530 Subject: [PATCH 033/154] test: add name explicitly to blog cateogry --- frappe/website/doctype/blog_category/test_records.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json index 4bd4ac35b7..89ff56d4fd 100644 --- a/frappe/website/doctype/blog_category/test_records.json +++ b/frappe/website/doctype/blog_category/test_records.json @@ -2,16 +2,19 @@ { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category" + "title": "_Test Blog Category", + "name": "_Test Blog Category" }, { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category 1" + "title": "_Test Blog Category 1", + "name": "_Test Blog Category 1" }, { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category 2" + "title": "_Test Blog Category 2", + "name": "_Test Blog Category 2" } ] \ No newline at end of file From faffec0f37cb53f04e48679476fa0944cd7b170b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 26 Aug 2020 11:37:58 +0530 Subject: [PATCH 034/154] feat: update name in test records --- frappe/tests/test_form_load.py | 2 +- frappe/tests/test_permissions.py | 34 +++++++++---------- .../doctype/blog_category/test_records.json | 9 ++--- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py index 34fc58465e..459a3f52bd 100644 --- a/frappe/tests/test_form_load.py +++ b/frappe/tests/test_form_load.py @@ -24,7 +24,7 @@ class TestFormLoad(unittest.TestCase): def test_fieldlevel_permissions_in_load(self): blog = frappe.get_doc({ "doctype": "Blog Post", - "blog_category": "_Test Blog Category 1", + "blog_category": "-test-blog-category-1", "blog_intro": "Test Blog Intro", "blogger": "_Test Blogger 1", "content": "Test Blog Content", diff --git a/frappe/tests/test_permissions.py b/frappe/tests/test_permissions.py index 364469f168..dddc790c94 100644 --- a/frappe/tests/test_permissions.py +++ b/frappe/tests/test_permissions.py @@ -59,7 +59,7 @@ class TestPermissions(unittest.TestCase): self.assertTrue(post.has_permission("read")) def test_user_permissions_in_doc(self): - add_user_permission("Blog Category", "_Test Blog Category 1", + add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com") frappe.set_user("test2@example.com") @@ -73,7 +73,7 @@ class TestPermissions(unittest.TestCase): self.assertTrue(get_doc_permissions(post1).get("read")) def test_user_permissions_in_report(self): - add_user_permission("Blog Category", "_Test Blog Category 1", "test2@example.com") + add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com") frappe.set_user("test2@example.com") names = [d.name for d in frappe.get_list("Blog Post", fields=["name", "blog_category"])] @@ -86,23 +86,23 @@ class TestPermissions(unittest.TestCase): self.assertFalse(doc.get("blog_category")) # Fetch default based on single user permission - add_user_permission("Blog Category", "_Test Blog Category 1", "test2@example.com") + add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com") frappe.set_user("test2@example.com") doc = frappe.new_doc("Blog Post") - self.assertEqual(doc.get("blog_category"), "_Test Blog Category 1") + self.assertEqual(doc.get("blog_category"), "-test-blog-category-1") # Don't fetch default if user permissions is more than 1 - add_user_permission("Blog Category", "_Test Blog Category", "test2@example.com", ignore_permissions=True) + add_user_permission("Blog Category", "-test-blog-category", "test2@example.com", ignore_permissions=True) frappe.clear_cache() doc = frappe.new_doc("Blog Post") self.assertFalse(doc.get("blog_category")) # Fetch user permission set as default from multiple user permission - add_user_permission("Blog Category", "_Test Blog Category 2", "test2@example.com", ignore_permissions=True, is_default=1) + add_user_permission("Blog Category", "-test-blog-category-2", "test2@example.com", ignore_permissions=True, is_default=1) frappe.clear_cache() doc = frappe.new_doc("Blog Post") - self.assertEqual(doc.get("blog_category"), "_Test Blog Category 2") + self.assertEqual(doc.get("blog_category"), "-test-blog-category-2") def test_user_link_match_doc(self): blogger = frappe.get_doc("Blogger", "_Test Blogger 1") @@ -215,7 +215,7 @@ class TestPermissions(unittest.TestCase): frappe.clear_cache(doctype='DocType') def test_user_permission_doctypes(self): - add_user_permission("Blog Category", "_Test Blog Category 1", + add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com") add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com") @@ -235,7 +235,7 @@ class TestPermissions(unittest.TestCase): def if_owner_setup(self): update('Blog Post', 'Blogger', 0, 'if_owner', 1) - add_user_permission("Blog Category", "_Test Blog Category 1", + add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com") add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com") @@ -254,7 +254,7 @@ class TestPermissions(unittest.TestCase): doc = frappe.get_doc({ "doctype": "Blog Post", - "blog_category": "_Test Blog Category", + "blog_category": "-test-blog-category", "blogger": "_Test Blogger 1", "title": "_Test Blog Post Title", "content": "_Test Blog Post Content" @@ -263,14 +263,14 @@ class TestPermissions(unittest.TestCase): self.assertRaises(frappe.PermissionError, doc.insert) frappe.set_user('test1@example.com') - add_user_permission("Blog Category", "_Test Blog Category", + add_user_permission("Blog Category", "-test-blog-category", "test2@example.com") frappe.set_user("test2@example.com") doc.insert() frappe.set_user("Administrator") - remove_user_permission("Blog Category", "_Test Blog Category", + remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com") frappe.set_user("test2@example.com") @@ -286,13 +286,13 @@ class TestPermissions(unittest.TestCase): def test_ignore_user_permissions_if_missing(self): """If there are no user permissions, then allow as per role""" - add_user_permission("Blog Category", "_Test Blog Category", + add_user_permission("Blog Category", "-test-blog-category", "test2@example.com") frappe.set_user("test2@example.com") doc = frappe.get_doc({ "doctype": "Blog Post", - "blog_category": "_Test Blog Category 2", + "blog_category": "-test-blog-category-2", "blogger": "_Test Blogger 1", "title": "_Test Blog Post Title", "content": "_Test Blog Post Content" @@ -301,7 +301,7 @@ class TestPermissions(unittest.TestCase): self.assertFalse(doc.has_permission("write")) frappe.set_user("Administrator") - remove_user_permission("Blog Category", "_Test Blog Category", + remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com") frappe.set_user("test2@example.com") @@ -420,7 +420,7 @@ class TestPermissions(unittest.TestCase): doc = frappe.get_doc({ "doctype": "Blog Post", - "blog_category": "_Test Blog Category", + "blog_category": "-test-blog-category", "blogger": "_Test Blogger 1", "title": "_Test Blog Post Title", "content": "_Test Blog Post Content" @@ -454,7 +454,7 @@ class TestPermissions(unittest.TestCase): add_user_permission('Blog Post', '-test-blog-post-1', 'test2@example.com') add_user_permission('Blog Post', '-test-blog-post-2', 'test2@example.com') - add_user_permission("Blog Category", '_Test Blog Category 1', 'test2@example.com') + add_user_permission("Blog Category", '-test-blog-category-1', 'test2@example.com') deleted_user_permission_count = clear_user_permissions('test2@example.com', 'Blog Post') diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json index 89ff56d4fd..4bd4ac35b7 100644 --- a/frappe/website/doctype/blog_category/test_records.json +++ b/frappe/website/doctype/blog_category/test_records.json @@ -2,19 +2,16 @@ { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category", - "name": "_Test Blog Category" + "title": "_Test Blog Category" }, { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category 1", - "name": "_Test Blog Category 1" + "title": "_Test Blog Category 1" }, { "doctype": "Blog Category", "parent_website_route": "blog", - "title": "_Test Blog Category 2", - "name": "_Test Blog Category 2" + "title": "_Test Blog Category 2" } ] \ No newline at end of file From 2ae5ce7d0e157a3ca5e3b04cc71f4b6ccb997d6f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Aug 2020 20:48:29 +0530 Subject: [PATCH 035/154] fix: Subtitle in Section With Tabs web template --- .../web_template/section_with_tabs/section_with_tabs.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/website/web_template/section_with_tabs/section_with_tabs.html b/frappe/website/web_template/section_with_tabs/section_with_tabs.html index 0c206b1c48..bb90611b4c 100644 --- a/frappe/website/web_template/section_with_tabs/section_with_tabs.html +++ b/frappe/website/web_template/section_with_tabs/section_with_tabs.html @@ -1,5 +1,8 @@

{{ title }}

+ +{%- if subtitle-%}

{{ subtitle }}

+{%- endif -%}
{% set ns = namespace(tabs=[]) %} From 2414dd386c24dcd0334a8c491eeb21d65d21bb14 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 27 Aug 2020 11:36:43 +0530 Subject: [PATCH 036/154] fix: formatting --- .../web_template/section_with_tabs/section_with_tabs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/web_template/section_with_tabs/section_with_tabs.html b/frappe/website/web_template/section_with_tabs/section_with_tabs.html index bb90611b4c..9a5bb20e0f 100644 --- a/frappe/website/web_template/section_with_tabs/section_with_tabs.html +++ b/frappe/website/web_template/section_with_tabs/section_with_tabs.html @@ -1,6 +1,6 @@

{{ title }}

-{%- if subtitle-%} +{%- if subtitle -%}

{{ subtitle }}

{%- endif -%} From 5c7f34b15dd91a00bee7c44cca72881f4ece0de5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 27 Aug 2020 14:22:48 +0530 Subject: [PATCH 037/154] test: update records --- frappe/website/doctype/blog_post/test_records.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_records.json b/frappe/website/doctype/blog_post/test_records.json index 79a3525801..4b29eadfa4 100644 --- a/frappe/website/doctype/blog_post/test_records.json +++ b/frappe/website/doctype/blog_post/test_records.json @@ -1,6 +1,6 @@ [ { - "blog_category": "_Test Blog Category", + "blog_category": "-test-blog-category", "blog_intro": "Test Blog Intro", "blogger": "_Test Blogger", "content": "Test Blog Content", @@ -9,7 +9,7 @@ "published": 1 }, { - "blog_category": "_Test Blog Category 1", + "blog_category": "-test-blog-category-1", "blog_intro": "Test Blog Intro", "blogger": "_Test Blogger", "content": "Test Blog Content", @@ -18,7 +18,7 @@ "published": 1 }, { - "blog_category": "_Test Blog Category 1", + "blog_category": "-test-blog-category-1", "blog_intro": "Test Blog Intro", "blogger": "_Test Blogger 1", "content": "Test Blog Content", @@ -27,7 +27,7 @@ "published": 0 }, { - "blog_category": "_Test Blog Category 1", + "blog_category": "-test-blog-category-1", "blog_intro": "Test Blog Intro", "blogger": "_Test Blogger 2", "content": "Test Blog Content", @@ -35,4 +35,4 @@ "title": "_Test Blog Post 3", "published": 0 } -] \ No newline at end of file +] From 89ddeab996433a028ceab8c5289a16dffdd586ae Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 Aug 2020 11:49:11 +0530 Subject: [PATCH 038/154] 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 4de73810765f43d149b8aff6432452c0b154672c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 31 Aug 2020 17:23:41 +0530 Subject: [PATCH 039/154] feat: update category in tests --- frappe/website/doctype/blog_post/test_blog_post.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 4b4d062a2d..eccc1258f3 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -61,7 +61,7 @@ class TestBlogPost(unittest.TestCase): frappe.delete_doc("Blog Category", blog.blog_category) def make_test_blog(): - if not frappe.db.exists('Blog Category', 'Test Blog Category'): + if not frappe.db.exists('Blog Category', '-test-blog-category'): # Set different title and name for the category frappe.get_doc(dict( doctype = 'Blog Category', @@ -73,7 +73,7 @@ def make_test_blog(): full_name='Test Blogger')).insert() test_blog = frappe.get_doc(dict( doctype = 'Blog Post', - blog_category = 'test-blog-category', + blog_category = '-test-blog-category', blogger = 'test-blogger', title = random_string(20), route = random_string(20), From c6f6ff6b5cee9345a650695e667beb11b8c0734c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 31 Aug 2020 17:33:03 +0530 Subject: [PATCH 040/154] feat: use blog category name --- frappe/website/doctype/blog_post/test_blog_post.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index eccc1258f3..ea1b6125db 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -44,8 +44,8 @@ class TestBlogPost(unittest.TestCase): # On blog post page find link to the category page soup = BeautifulSoup(blog_page_html, "lxml") - category_title = frappe.db.get_value("Blog Category", blog.blog_category, "title") - category_page_link = list(soup.find_all('a', string=category_title))[0] + + category_page_link = list(soup.find_all('a', string=blog.blog_category))[0] category_page_url = category_page_link["href"] # Visit the category page (by following the link found in above stage) From 4b2c82b9c90bb02432a76b34c2611cd1b51acd09 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 31 Aug 2020 20:14:34 +0530 Subject: [PATCH 041/154] fix: Set blog route if title and category are set Set blog route on client side as soon as title and category are selected. This behavious is in line with Web Page, where the route is set as soon as the title is entered. This will ensure blog routes are consistent and contain category as the part of the route. The user can change the route ofcourse, but this behaviour is the most common expectation. --- frappe/website/doctype/blog_post/blog_post.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js index 7aa83f536d..bfff947948 100644 --- a/frappe/website/doctype/blog_post/blog_post.js +++ b/frappe/website/doctype/blog_post/blog_post.js @@ -11,16 +11,29 @@ frappe.ui.form.on('Blog Post', { }, title: function(frm) { generate_google_search_preview(frm); + frm.trigger('set_route'); }, meta_description: function(frm) { generate_google_search_preview(frm); }, blog_intro: function(frm) { generate_google_search_preview(frm); + }, + blog_category(frm) { + frm.trigger('set_route'); + }, + set_route(frm) { + if (frm.doc.route) return; + if (frm.doc.title && frm.doc.blog_category) { + frm.call('make_route').then(r => { + frm.set_value('route', r.message); + }); + } } }); function generate_google_search_preview(frm) { + if (!frm.doc.title) return; let google_preview = frm.get_field("google_preview"); let seo_title = (frm.doc.title).slice(0, 60); let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160); From 14d70b274317a703c9a97ab351151c3c4ed7b8e9 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 31 Aug 2020 20:51:25 +0530 Subject: [PATCH 042/154] fix: Refresh datatable when sidebar is toggled --- frappe/public/js/frappe/views/reports/report_view.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index c7d001ed94..aec24d9d13 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -48,6 +48,10 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { setup_view() { this.setup_columns(); super.setup_new_doc_event(); + // refresh datatable when sidebar is toggled to accomodate extra space + $(document.body) + .off('toggleListSidebar.report_view') + .on('toggleListSidebar.report_view', () => this.render(true)); } setup_result_area() { From f9e6f0e8e41a64e9eed308c54037f2b3dd5509d9 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 31 Aug 2020 21:07:51 +0530 Subject: [PATCH 043/154] 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 f533acabe6927cfe296197ecb05272be2e3131cf Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 1 Sep 2020 13:46:08 +0530 Subject: [PATCH 044/154] test: use regex to search for link --- frappe/website/doctype/blog_post/test_blog_post.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index ea1b6125db..7c6e64dc5a 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe import unittest from bs4 import BeautifulSoup +import re from frappe.utils import set_request from frappe.website.render import render @@ -44,8 +45,7 @@ class TestBlogPost(unittest.TestCase): # On blog post page find link to the category page soup = BeautifulSoup(blog_page_html, "lxml") - - category_page_link = list(soup.find_all('a', string=blog.blog_category))[0] + category_page_link = list(soup.find_all('a', href=re.compile(blog.blog_category)))[0] category_page_url = category_page_link["href"] # Visit the category page (by following the link found in above stage) @@ -61,7 +61,7 @@ class TestBlogPost(unittest.TestCase): frappe.delete_doc("Blog Category", blog.blog_category) def make_test_blog(): - if not frappe.db.exists('Blog Category', '-test-blog-category'): + if not frappe.db.exists('Blog Category', 'test-blog-category'): # Set different title and name for the category frappe.get_doc(dict( doctype = 'Blog Category', @@ -73,7 +73,7 @@ def make_test_blog(): full_name='Test Blogger')).insert() test_blog = frappe.get_doc(dict( doctype = 'Blog Post', - blog_category = '-test-blog-category', + blog_category = 'test-blog-category', blogger = 'test-blogger', title = random_string(20), route = random_string(20), From 428080bc56003427a113826c488f2d55459c1841 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Sep 2020 21:04:57 +0530 Subject: [PATCH 045/154] fix: Send email notification to all document assignees --- .../email/doctype/notification/notification.json | 13 +++++++++++-- frappe/email/doctype/notification/notification.py | 15 +++++++++++++-- .../notification_recipient.json | 3 ++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index 95f218ad73..8918709fa0 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -34,6 +34,7 @@ "set_property_after_alert", "property_value", "column_break_5", + "send_to_all_assignees", "recipients", "message_sb", "message", @@ -216,7 +217,7 @@ "fieldname": "recipients", "fieldtype": "Table", "label": "Recipients", - "mandatory_depends_on": "eval:doc.channel!=='Slack'", + "mandatory_depends_on": "eval:doc.channel!=='Slack' && !doc.send_to_all_assignees", "options": "Notification Recipient" }, { @@ -277,11 +278,19 @@ "fieldname": "send_system_notification", "fieldtype": "Check", "label": "Send System Notification" + }, + { + "default": "0", + "depends_on": "eval:doc.channel == 'Email'", + "fieldname": "send_to_all_assignees", + "fieldtype": "Check", + "label": "Send To All Assignees" } ], "icon": "fa fa-envelope", + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-11 19:24:35.479373", + "modified": "2020-09-01 18:36:22.550891", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 2ec208c89d..cfa3c43ff7 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -189,6 +189,7 @@ def get_context(context): recipients, cc, bcc = self.get_list_of_recipients(doc, context) if not (recipients or cc or bcc): return + sender = None if self.sender and self.sender_email: sender = formataddr((self.sender, self.sender_email)) @@ -239,8 +240,6 @@ def get_context(context): email_ids = email_ids_value.replace(",", "\n") recipients = recipients + email_ids.split("\n") - # else: - # print "invalid email" if recipient.cc and "{" in recipient.cc: recipient.cc = frappe.render_template(recipient.cc, context) @@ -262,6 +261,9 @@ def get_context(context): for email in emails: recipients = recipients + email.split("\n") + if self.send_to_all_assignees: + recipients = recipients + get_assignees(doc) + if not recipients and not cc and not bcc: return None, None, None return list(set(recipients)), list(set(cc)), list(set(bcc)) @@ -405,3 +407,12 @@ def evaluate_alert(doc, alert, event): def get_context(doc): return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)} + +def get_assignees(doc): + assignees = [] + assignees = frappe.get_all('ToDo', filters={'status': 'Open', 'reference_name': doc.name, + 'reference_type': doc.doctype}, fields=['owner']) + + recipients = [d.owner for d in assignees] + + return recipients \ No newline at end of file diff --git a/frappe/email/doctype/notification_recipient/notification_recipient.json b/frappe/email/doctype/notification_recipient/notification_recipient.json index 201899cd57..0670320a77 100644 --- a/frappe/email/doctype/notification_recipient/notification_recipient.json +++ b/frappe/email/doctype/notification_recipient/notification_recipient.json @@ -46,9 +46,10 @@ "options": "Role" } ], + "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-02-21 11:18:40.125233", + "modified": "2020-09-01 17:40:27.289105", "modified_by": "Administrator", "module": "Email", "name": "Notification Recipient", From fda7999bdd6b67561e7ded71f12e9264f417dd27 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 15:48:49 +0530 Subject: [PATCH 046/154] fix: Send email notification by child table fields --- .../doctype/notification/notification.js | 22 ++++++++++++++----- .../doctype/notification/notification.py | 15 +++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 454514f922..5eae016573 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -19,9 +19,11 @@ frappe.notification = { } frappe.model.with_doctype(frm.doc.document_type, function() { - let get_select_options = function(df) { + let get_select_options = function(df, parent_field) { + let select_value = parent_field ? df.fieldname + ',' + parent_field : df.fieldname; + return { - value: df.fieldname, + value: select_value, label: df.fieldname + ' (' + __(df.label) + ')' }; }; @@ -59,9 +61,19 @@ frappe.notification = { let receiver_fields = []; if (frm.doc.channel === 'Email') { receiver_fields = $.map(fields, function(d) { - return d.options == 'Email' || - (d.options == 'User' && d.fieldtype == 'Link') - ? get_select_options(d) : null; + + if (d.fieldtype == 'Table') { + let child_fields = frappe.get_doc('DocType', d.options).fields; + return $.map(child_fields, function(df) { + return df.options == 'Email' || + (df.options == 'User' && df.fieldtype == 'Link') + ? get_select_options(df, d.fieldname) : null; + }); + } else { + return d.options == 'Email' || + (d.options == 'User' && d.fieldtype == 'Link') + ? get_select_options(d) : null; + } }); } else if (in_list(['WhatsApp', 'SMS'], frm.doc.channel)) { receiver_fields = $.map(fields, function(d) { diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index cfa3c43ff7..a51afbf7aa 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -235,10 +235,17 @@ def get_context(context): if not frappe.safe_eval(recipient.condition, None, context): continue if recipient.receiver_by_document_field: - email_ids_value = doc.get(recipient.receiver_by_document_field) - if validate_email_address(email_ids_value): - email_ids = email_ids_value.replace(",", "\n") - recipients = recipients + email_ids.split("\n") + fields = recipient.receiver_by_document_field.split(',') + if len(fields) > 1: + for d in doc.get(fields[1]): + email_id = d.get(fields[0]) + if validate_email_address(email_id): + recipients.append(email_id) + else: + email_ids_value = doc.get(fields[0]) + if validate_email_address(email_ids_value): + email_ids = email_ids_value.replace(",", "\n") + recipients = recipients + email_ids.split("\n") if recipient.cc and "{" in recipient.cc: recipient.cc = frappe.render_template(recipient.cc, context) From 07728df16af98858accc34cb339bce6e92ebc26f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 15:49:55 +0530 Subject: [PATCH 047/154] fix: Add test cases --- .../doctype/notification/test_notification.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 9bdf09375d..71d46cbdfa 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe, frappe.utils, frappe.utils.scheduler +from frappe.desk.form import assign_to import unittest test_records = frappe.get_test_records('Notification') @@ -177,3 +178,92 @@ class TestNotification(unittest.TestCase): frappe.db.sql("""delete from `tabUser` where email='test_jinja@example.com'""") frappe.db.sql("""delete from `tabEmail Queue`""") frappe.db.sql("""delete from `tabEmail Queue Recipient`""") + + def test_notification_to_assignee(self): + frappe.set_user("Administrator") + + todo = frappe.new_doc('ToDo') + todo.description = 'Test Notification' + todo.save() + + assign_to.add({ + "assign_to": ["test2@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo" + }) + + assign_to.add({ + "assign_to": ["test1@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo" + }) + + if not frappe.db.exists('Notification', {'name': 'ToDo Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'ToDo Status Update' + notification.subject = 'ToDo Status Update' + notification.document_type = 'ToDo' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.send_to_all_assignees = 1 + notification.save() + + #change status of todo + todo.status = 'Closed' + todo.save() + + email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'ToDo', + 'reference_name': todo.name}) + + self.assertTrue(email_queue) + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue('test2@example.com' in recipients) + self.assertTrue('test1@example.com' in recipients) + + def test_notification_by_child_table_field(self): + frappe.set_user("Administrator") + + if not frappe.db.exists('Notification', {'name': 'Contact Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'Contact Status Update' + notification.subject = 'Contact Status Update' + notification.document_type = 'Contact' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.message = 'Test Contact Update' + notification.append('recipients', { + 'receiver_by_document_field': 'email_id,email_ids' + }) + notification.save() + + contact = frappe.new_doc('Contact') + contact.first_name = 'John Doe' + contact.status = 'Open' + contact.append('email_ids', { + 'email_id': 'test2@example.com', + 'is_primary': 1 + }) + + contact.append('email_ids', { + 'email_id': 'test1@example.com' + }) + + contact.save() + + #change status of todo + contact.status = 'Replied' + contact.save() + + email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'Contact', + 'reference_name': contact.name}) + + self.assertTrue(email_queue) + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue('test2@example.com' in recipients) + self.assertTrue('test1@example.com' in recipients) + + From 6ef4536c697885d1fdd617edafd30ab21733b578 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 16:20:32 +0530 Subject: [PATCH 048/154] fix: Update comment --- frappe/email/doctype/notification/test_notification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 71d46cbdfa..e370bac9e5 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -253,7 +253,7 @@ class TestNotification(unittest.TestCase): contact.save() - #change status of todo + #change status of contact contact.status = 'Replied' contact.save() From b373f10432cc5be1df2f5922eeae1eaecd227890 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 20:00:36 +0530 Subject: [PATCH 049/154] fix: Add sleep to fix test cases --- frappe/email/doctype/notification/test_notification.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index e370bac9e5..5b8ceb03d4 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, frappe.utils, frappe.utils.scheduler from frappe.desk.form import assign_to import unittest +import time test_records = frappe.get_test_records('Notification') @@ -214,6 +215,8 @@ class TestNotification(unittest.TestCase): todo.status = 'Closed' todo.save() + # adding sleep so that email queue is fetched once its created + time.sleep(10) email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'ToDo', 'reference_name': todo.name}) @@ -257,6 +260,8 @@ class TestNotification(unittest.TestCase): contact.status = 'Replied' contact.save() + # adding sleep so that email queue is fetched once its created + time.sleep(10) email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'Contact', 'reference_name': contact.name}) From b65e6588282eb29843b91cb6a868ccb945751f93 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 20:42:43 +0530 Subject: [PATCH 050/154] fix: Move notification creation in setup --- .../doctype/notification/test_notification.py | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 5b8ceb03d4..45a1587c1a 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import frappe, frappe.utils, frappe.utils.scheduler from frappe.desk.form import assign_to import unittest -import time test_records = frappe.get_test_records('Notification') @@ -15,7 +14,31 @@ test_dependencies = ["User"] class TestNotification(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabEmail Queue`""") - frappe.set_user("test1@example.com") + frappe.set_user("test@example.com") + + if not frappe.db.exists('Notification', {'name': 'ToDo Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'ToDo Status Update' + notification.subject = 'ToDo Status Update' + notification.document_type = 'ToDo' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.send_to_all_assignees = 1 + notification.save() + + if not frappe.db.exists('Notification', {'name': 'Contact Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'Contact Status Update' + notification.subject = 'Contact Status Update' + notification.document_type = 'Contact' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.message = 'Test Contact Update' + notification.append('recipients', { + 'receiver_by_document_field': 'email_id,email_ids' + }) + notification.save() + def tearDown(self): frappe.set_user("Administrator") @@ -181,8 +204,6 @@ class TestNotification(unittest.TestCase): frappe.db.sql("""delete from `tabEmail Queue Recipient`""") def test_notification_to_assignee(self): - frappe.set_user("Administrator") - todo = frappe.new_doc('ToDo') todo.description = 'Test Notification' todo.save() @@ -201,22 +222,10 @@ class TestNotification(unittest.TestCase): "description": "Close this Todo" }) - if not frappe.db.exists('Notification', {'name': 'ToDo Status Update'}, 'name'): - notification = frappe.new_doc('Notification') - notification.name = 'ToDo Status Update' - notification.subject = 'ToDo Status Update' - notification.document_type = 'ToDo' - notification.event = 'Value Change' - notification.value_changed = 'status' - notification.send_to_all_assignees = 1 - notification.save() - #change status of todo todo.status = 'Closed' todo.save() - # adding sleep so that email queue is fetched once its created - time.sleep(10) email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'ToDo', 'reference_name': todo.name}) @@ -227,21 +236,6 @@ class TestNotification(unittest.TestCase): self.assertTrue('test1@example.com' in recipients) def test_notification_by_child_table_field(self): - frappe.set_user("Administrator") - - if not frappe.db.exists('Notification', {'name': 'Contact Status Update'}, 'name'): - notification = frappe.new_doc('Notification') - notification.name = 'Contact Status Update' - notification.subject = 'Contact Status Update' - notification.document_type = 'Contact' - notification.event = 'Value Change' - notification.value_changed = 'status' - notification.message = 'Test Contact Update' - notification.append('recipients', { - 'receiver_by_document_field': 'email_id,email_ids' - }) - notification.save() - contact = frappe.new_doc('Contact') contact.first_name = 'John Doe' contact.status = 'Open' @@ -260,8 +254,6 @@ class TestNotification(unittest.TestCase): contact.status = 'Replied' contact.save() - # adding sleep so that email queue is fetched once its created - time.sleep(10) email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'Contact', 'reference_name': contact.name}) From 0bdb4c8b4e00e37a5353561419dff82337fe3f97 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Sep 2020 21:12:26 +0530 Subject: [PATCH 051/154] fix: Linting issues --- frappe/email/doctype/notification/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 5eae016573..cb49232906 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -65,7 +65,7 @@ frappe.notification = { if (d.fieldtype == 'Table') { let child_fields = frappe.get_doc('DocType', d.options).fields; return $.map(child_fields, function(df) { - return df.options == 'Email' || + return df.options == 'Email' || (df.options == 'User' && df.fieldtype == 'Link') ? get_select_options(df, d.fieldname) : null; }); From 1359c1c8b6aaa2cef6c4fc784ac563a5adfbb206 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 3 Sep 2020 12:35:12 +0530 Subject: [PATCH 052/154] fix: add correct slack webhook url link --- frappe/email/doctype/notification/notification.js | 1 + frappe/email/doctype/notification/notification.json | 4 ++-- .../integrations/doctype/twilio_settings/twilio_settings.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 454514f922..02fc6b31ca 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -151,6 +151,7 @@ frappe.ui.form.on('Notification', { }, refresh: function(frm) { frappe.notification.setup_fieldname_select(frm); + frappe.notification.setup_example_message(frm); frm.get_field('is_standard').toggle(frappe.boot.developer_mode); frm.trigger('event'); }, diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index 95f218ad73..1e3c0d5b14 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -66,7 +66,7 @@ }, { "depends_on": "eval:doc.channel=='Slack'", - "description": "To use Slack Channel, add a Slack Webhook URL.", + "description": "To use Slack Channel, add a Slack Webhook URL.", "fieldname": "slack_webhook_url", "fieldtype": "Link", "label": "Slack Channel", @@ -281,7 +281,7 @@ ], "icon": "fa fa-envelope", "links": [], - "modified": "2020-08-11 19:24:35.479373", + "modified": "2020-09-03 08:45:21.289300", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/integrations/doctype/twilio_settings/twilio_settings.py b/frappe/integrations/doctype/twilio_settings/twilio_settings.py index 6c698d719a..80c5162987 100644 --- a/frappe/integrations/doctype/twilio_settings/twilio_settings.py +++ b/frappe/integrations/doctype/twilio_settings/twilio_settings.py @@ -11,7 +11,7 @@ from frappe.utils.password import get_decrypted_password from six import string_types class TwilioSettings(Document): - def validate(self): + def on_update(self): self.validate_twilio_credentials() def validate_twilio_credentials(self): From 21d58f3acd15ea45347e0da17c41a9880e00ea7a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 3 Sep 2020 12:47:00 +0530 Subject: [PATCH 053/154] fix: update message to be more concised --- frappe/email/doctype/notification/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 02fc6b31ca..24ebf8d01b 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -87,7 +87,7 @@ frappe.notification = {
Message Example
-Your {{ doc.name }} order of {{ doc.total }} has shipped and should be delivered on {{ doc.date }}. Details : {{doc.customer}}
+Your appointment is coming up on {{ doc.date }} at {{ doc.time }}
 
`; } else if (frm.doc.channel === 'Email') { template = `
Message Example
From ba9c7bdc09b658238391c913b45922b06340a57c Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 3 Sep 2020 13:55:55 +0530 Subject: [PATCH 054/154] fix: Also check if name is set, since empty object is truthy --- frappe/public/js/frappe/form/controls/data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/data.js b/frappe/public/js/frappe/form/controls/data.js index 355f35891a..bbf9a89072 100644 --- a/frappe/public/js/frappe/form/controls/data.js +++ b/frappe/public/js/frappe/form/controls/data.js @@ -61,7 +61,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ // check if name exists frappe.db.get_value(this.doctype, this.$input.val(), 'name', (val) => { - if (val) { + if (val && val.name) { this.set_description(__('{0} already exists. Select another name', [val.name])); } }, From b9e07604876fe65faaeb8e1c1d8fb476b69bd15f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 3 Sep 2020 14:11:31 +0530 Subject: [PATCH 055/154] fix: add a check to enable the twilio settings --- .../email/doctype/notification/notification.json | 3 ++- .../email/doctype/notification/notification.py | 7 +++++++ .../doctype/twilio_settings/twilio_settings.json | 16 +++++++++++++--- .../doctype/twilio_settings/twilio_settings.py | 15 +++++++++------ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index 1e3c0d5b14..4c4e89745f 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -268,6 +268,7 @@ "fieldname": "twilio_number", "fieldtype": "Link", "label": "Twilio Number", + "mandatory_depends_on": "eval: doc.channel==='WhatsApp'", "options": "Twilio Number Group" }, { @@ -281,7 +282,7 @@ ], "icon": "fa fa-envelope", "links": [], - "modified": "2020-09-03 08:45:21.289300", + "modified": "2020-09-03 10:33:23.084590", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 2ec208c89d..19324ffaaa 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -42,6 +42,7 @@ class Notification(Document): self.validate_forbidden_types() self.validate_condition() self.validate_standard() + self.validate_twilio_settings() frappe.cache().hdel('notifications', self.document_type) def on_update(self): @@ -68,6 +69,12 @@ def get_context(context): if self.is_standard and not frappe.conf.developer_mode: frappe.throw(_('Cannot edit Standard Notification. To edit, please disable this and duplicate it')) + def validate_twilio_settings(self): + if self.enabled and self.channel == "WhatsApp" \ + and not frappe.db.get_single_value("Twilio Settings", "enabled"): + print(frappe.db.get_single_value("Twilio Settings", "enabled")) + frappe.throw(_("Please enable Twilio settings to send WhatsApp messages")) + def validate_condition(self): temp_doc = frappe.new_doc(self.document_type) if self.condition: diff --git a/frappe/integrations/doctype/twilio_settings/twilio_settings.json b/frappe/integrations/doctype/twilio_settings/twilio_settings.json index e54500fd5d..9eb2c0c512 100644 --- a/frappe/integrations/doctype/twilio_settings/twilio_settings.json +++ b/frappe/integrations/doctype/twilio_settings/twilio_settings.json @@ -5,6 +5,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "enabled", "account_sid", "auth_token", "column_break_2", @@ -14,12 +15,14 @@ { "fieldname": "account_sid", "fieldtype": "Data", - "label": "Account SID" + "label": "Account SID", + "mandatory_depends_on": "eval: doc.enabled" }, { "fieldname": "auth_token", "fieldtype": "Password", - "label": "Auth Token" + "label": "Auth Token", + "mandatory_depends_on": "eval: doc.enabled" }, { "fieldname": "column_break_2", @@ -30,11 +33,18 @@ "fieldtype": "Table", "label": "Twilio Number", "options": "Twilio Number Group" + }, + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" } ], + "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-08-11 15:28:57.860554", + "modified": "2020-09-03 10:17:21.318743", "modified_by": "Administrator", "module": "Integrations", "name": "Twilio Settings", diff --git a/frappe/integrations/doctype/twilio_settings/twilio_settings.py b/frappe/integrations/doctype/twilio_settings/twilio_settings.py index 80c5162987..b8f991e829 100644 --- a/frappe/integrations/doctype/twilio_settings/twilio_settings.py +++ b/frappe/integrations/doctype/twilio_settings/twilio_settings.py @@ -5,14 +5,16 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from twilio.rest import Client from frappe import _ from frappe.utils.password import get_decrypted_password +from twilio.rest import Client from six import string_types +from json import loads class TwilioSettings(Document): def on_update(self): - self.validate_twilio_credentials() + if self.enabled: + self.validate_twilio_credentials() def validate_twilio_credentials(self): try: @@ -23,14 +25,15 @@ class TwilioSettings(Document): frappe.throw(_("Invalid Account SID or Auth Token.")) def send_whatsapp_message(sender, receiver_list, message): - import json + twilio_settings = frappe.get_doc("Twilio Settings") + if not twilio_settings.enabled: + frappe.throw(_("Please enable twilio settings before sending WhatsApp messages")) + if isinstance(receiver_list, string_types): - receiver_list = json.loads(receiver_list) + receiver_list = loads(receiver_list) if not isinstance(receiver_list, list): receiver_list = [receiver_list] - - twilio_settings = frappe.get_doc("Twilio Settings") auth_token = get_decrypted_password("Twilio Settings", "Twilio Settings", 'auth_token') client = Client(twilio_settings.account_sid, auth_token) args = { From 5b449d1084abba1016940bd49975b38225ed9e9e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 3 Sep 2020 14:24:19 +0530 Subject: [PATCH 056/154] chore: Add comments --- frappe/email/doctype/notification/notification.js | 3 +++ frappe/email/doctype/notification/notification.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index cb49232906..059f5518fc 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -20,6 +20,7 @@ frappe.notification = { frappe.model.with_doctype(frm.doc.document_type, function() { let get_select_options = function(df, parent_field) { + // Append parent_field name along with fieldname for child table fields let select_value = parent_field ? df.fieldname + ',' + parent_field : df.fieldname; return { @@ -62,6 +63,7 @@ frappe.notification = { if (frm.doc.channel === 'Email') { receiver_fields = $.map(fields, function(d) { + // Add User and Email fields from child into select dropdown if (d.fieldtype == 'Table') { let child_fields = frappe.get_doc('DocType', d.options).fields; return $.map(child_fields, function(df) { @@ -69,6 +71,7 @@ frappe.notification = { (df.options == 'User' && df.fieldtype == 'Link') ? get_select_options(df, d.fieldname) : null; }); + // Add User and Email fields from parent into select dropdown } else { return d.options == 'Email' || (d.options == 'User' && d.fieldtype == 'Link') diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index a51afbf7aa..cd5aacabbd 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -236,11 +236,13 @@ def get_context(context): continue if recipient.receiver_by_document_field: fields = recipient.receiver_by_document_field.split(',') + # fields from child table if len(fields) > 1: for d in doc.get(fields[1]): email_id = d.get(fields[0]) if validate_email_address(email_id): recipients.append(email_id) + # field from parent doc else: email_ids_value = doc.get(fields[0]) if validate_email_address(email_ids_value): From 8c8f7313f41d9d6e350295403d0a21c59537e6b4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 3 Sep 2020 14:26:01 +0530 Subject: [PATCH 057/154] fix(minor): make utils explicit in safe_globals (#11408) * fix(minor): make utils explicit in safe_globals * fix(minor): import subprocess * fix(minor): fix globals in safe_eval; * fix(minor): import subprocess * fix(minor): add test * fix(minor): webhook.py * fix(minor): document_type_mapping.py --- .../data_migration_mapping.py | 3 +- .../document_type_mapping.py | 2 +- .../integrations/doctype/webhook/webhook.py | 4 +- frappe/tests/test_safe_exec.py | 11 +- frappe/utils/data.py | 15 --- frappe/utils/pdf.py | 17 ++- frappe/utils/safe_exec.py | 110 +++++++++++++++++- 7 files changed, 139 insertions(+), 23 deletions(-) diff --git a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py index b346864f02..097652fb88 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.utils.safe_exec import get_safe_globals class DataMigrationMapping(Document): def get_filters(self): if self.condition: - return frappe.safe_eval(self.condition, dict(frappe=frappe)) + return frappe.safe_eval(self.condition, get_safe_globals()) def get_fields(self): fields = [] diff --git a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py index 8ace4f57d3..bf96e4e27b 100644 --- a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py +++ b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py @@ -102,7 +102,7 @@ class DocumentTypeMapping(Document): filters = json.loads(mapping.remote_value_filters) for key, value in iteritems(filters): if value.startswith('eval:'): - val = frappe.safe_eval(value[5:], dict(frappe=frappe)) + val = frappe.safe_eval(value[5:], None, dict(doc=doc)) filters[key] = val if doc.get(value): filters[key] = doc.get(value) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index 8e6c8d58e4..f1556aa661 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -18,6 +18,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.utils.jinja import validate_template +from frappe.utils.safe_exec import get_safe_globals WEBHOOK_SECRET_HEADER = "X-Frappe-Webhook-Signature" @@ -75,8 +76,7 @@ class Webhook(Document): def get_context(doc): - return {"doc": doc, "utils": frappe.utils} - + return {'doc': doc, 'utils': get_safe_globals().get('frappe').get('utils')} def enqueue_webhook(doc, webhook): webhook = frappe.get_doc("Webhook", webhook.get("name")) diff --git a/frappe/tests/test_safe_exec.py b/frappe/tests/test_safe_exec.py index 95bbae6746..d7b25b8194 100644 --- a/frappe/tests/test_safe_exec.py +++ b/frappe/tests/test_safe_exec.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals import unittest, frappe -from frappe.utils.safe_exec import safe_exec +from frappe.utils.safe_exec import safe_exec, get_safe_globals class TestSafeExec(unittest.TestCase): def test_import_fails(self): @@ -9,6 +9,15 @@ class TestSafeExec(unittest.TestCase): def test_internal_attributes(self): self.assertRaises(SyntaxError, safe_exec, '().__class__.__call__') + def test_utils(self): + _locals = dict(out=None) + safe_exec('''out = frappe.utils.cint("1")''', None, _locals) + self.assertEqual(_locals['out'], 1) + + def test_safe_eval(self): + self.assertEqual(frappe.safe_eval('1+1'), 2) + self.assertRaises(AttributeError, frappe.safe_eval, 'frappe.utils.os.path', get_safe_globals()) + def test_sql(self): _locals = dict(out=None) safe_exec('''out = frappe.db.sql("select name from tabDocType where name='DocType'")''', None, _locals) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 8dad5f3e92..8a25c84276 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -3,10 +3,8 @@ from __future__ import unicode_literals -# IMPORTANT: only import safe functions as this module will be included in jinja environment import frappe from dateutil.parser._parser import ParserError -import subprocess import operator import json import re, datetime, math, time @@ -427,19 +425,6 @@ def flt(s, precision=None): return num -def get_wkhtmltopdf_version(): - wkhtmltopdf_version = frappe.cache().hget("wkhtmltopdf_version", None) - - if not wkhtmltopdf_version: - try: - res = subprocess.check_output(["wkhtmltopdf", "--version"]) - wkhtmltopdf_version = res.decode('utf-8').split(" ")[1] - frappe.cache().hset("wkhtmltopdf_version", None, wkhtmltopdf_version) - except Exception: - pass - - return (wkhtmltopdf_version or '0') - def cint(s): """Convert to integer""" try: num = int(float(s)) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index f3d2e75c2b..51febb5f72 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -6,6 +6,7 @@ import io import os import re from distutils.version import LooseVersion +import subprocess import pdfkit import six @@ -14,7 +15,7 @@ from PyPDF2 import PdfFileReader, PdfFileWriter import frappe from frappe import _ -from frappe.utils import get_wkhtmltopdf_version, scrub_urls +from frappe.utils import scrub_urls PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError", @@ -191,7 +192,6 @@ def cleanup(fname, options): if options.get(key) and os.path.exists(options[key]): os.remove(options[key]) - def toggle_visible_pdf(soup): for tag in soup.find_all(attrs={"class": "visible-pdf"}): # remove visible-pdf class to unhide @@ -200,3 +200,16 @@ def toggle_visible_pdf(soup): for tag in soup.find_all(attrs={"class": "hidden-pdf"}): # remove tag from html tag.extract() + +def get_wkhtmltopdf_version(): + wkhtmltopdf_version = frappe.cache().hget("wkhtmltopdf_version", None) + + if not wkhtmltopdf_version: + try: + res = subprocess.check_output(["wkhtmltopdf", "--version"]) + wkhtmltopdf_version = res.decode('utf-8').split(" ")[1] + frappe.cache().hset("wkhtmltopdf_version", None, wkhtmltopdf_version) + except Exception: + pass + + return (wkhtmltopdf_version or '0') diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 738ce2ba0f..12382e85cd 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -39,7 +39,7 @@ def get_safe_globals(): date_format = "yyyy-mm-dd" time_format = "HH:mm:ss" - add_module_properties(frappe.utils.data, datautils, lambda obj: hasattr(obj, "__call__")) + add_data_utils(datautils) if "_" in getattr(frappe.local, 'form_dict', {}): del frappe.local.form_dict["_"] @@ -162,6 +162,11 @@ def _write(obj): # allow writing to any object return obj +def add_data_utils(data): + for key, obj in frappe.utils.data.__dict__.items(): + if key in VALID_UTILS: + data[key] = obj + def add_module_properties(module, data, filter_method): for key, obj in module.__dict__.items(): if key.startswith("_"): @@ -171,3 +176,106 @@ def add_module_properties(module, data, filter_method): if filter_method(obj): # only allow functions data[key] = obj + +VALID_UTILS = ( +"DATE_FORMAT", +"TIME_FORMAT", +"DATETIME_FORMAT", +"is_invalid_date_string", +"getdate", +"get_datetime", +"to_timedelta", +"add_to_date", +"add_days", +"add_months", +"add_years", +"date_diff", +"month_diff", +"time_diff", +"time_diff_in_seconds", +"time_diff_in_hours", +"now_datetime", +"get_timestamp", +"get_eta", +"get_time_zone", +"convert_utc_to_user_timezone", +"now", +"nowdate", +"today", +"nowtime", +"get_first_day", +"get_quarter_start", +"get_first_day_of_week", +"get_year_start", +"get_last_day_of_week", +"get_last_day", +"get_time", +"get_datetime_str", +"get_date_str", +"get_time_str", +"get_user_date_format", +"get_user_time_format", +"format_date", +"format_time", +"format_datetime", +"format_duration", +"get_weekdays", +"get_weekday", +"get_timespan_date_range", +"global_date_format", +"has_common", +"flt", +"cint", +"floor", +"ceil", +"cstr", +"rounded", +"remainder", +"safe_div", +"round_based_on_smallest_currency_fraction", +"encode", +"parse_val", +"fmt_money", +"get_number_format_info", +"money_in_words", +"in_words", +"is_html", +"is_image", +"get_thumbnail_base64_for_image", +"image_to_base64", +"strip_html", +"escape_html", +"pretty_date", +"comma_or", +"comma_and", +"comma_sep", +"new_line_sep", +"filter_strip_join", +"get_url", +"get_host_name_from_request", +"url_contains_port", +"get_host_name", +"get_link_to_form", +"get_link_to_report", +"get_absolute_url", +"get_url_to_form", +"get_url_to_list", +"get_url_to_report", +"get_url_to_report_with_filters", +"evaluate_filters", +"compare", +"get_filter", +"make_filter_tuple", +"make_filter_dict", +"sanitize_column", +"scrub_urls", +"expand_relative_urls", +"quoted", +"quote_urls", +"unique", +"strip", +"to_markdown", +"md_to_html", +"is_subset", +"generate_hash" +) \ No newline at end of file From 55964c66cb00af6c86e4fd1e7ce9ae654a18cf96 Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 4 Sep 2020 11:55:17 +0530 Subject: [PATCH 058/154] feat: Publish built assets to S3 --- .github/workflows/publish-assets.yml | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/publish-assets.yml diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml new file mode 100644 index 0000000000..b673220286 --- /dev/null +++ b/.github/workflows/publish-assets.yml @@ -0,0 +1,37 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Build Assets on develop and Publish to Public bucket + +on: + push: + branches: [ develop ] + +jobs: + build: + runs-on: ubuntu-latest + container: frappe/bench:latest + + steps: + - name: Set up bench + run: | + bench init frappe-bench --skip-assets --python $(which python) + cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA + bench build + + - name: Package assets + run: | + cd frappe-bench/sites/assets + tar -cvpzf $GITHUB_SHA.tar.gz /js /css + + - name: Publish assets + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: 'frappe-assets' + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} + AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' + AWS_REGION: 'fr-par' + SOURCE_DIR: $GITHUB_SHA.tar.gz From 662a7ac56ede1f8c1b5f01006c877cdfdbe1162b Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 4 Sep 2020 12:02:10 +0530 Subject: [PATCH 059/154] feat: Generate artifacts and publish to S3 --- .github/workflows/publish-assets.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index b673220286..50367566bd 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Set up bench run: | - bench init frappe-bench --skip-assets --python $(which python) + sudo bench init frappe-bench --skip-assets --python $(which python) cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA bench build @@ -34,4 +34,10 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' AWS_REGION: 'fr-par' - SOURCE_DIR: $GITHUB_SHA.tar.gz + SOURCE_DIR: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz + + - name: Generate Artifacts + uses: actions/upload-artifact@v2 + with: + name: $GITHUB_SHA.tar.gz + path: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz From 2727fafdc1b2a2b3f6f77bbebc881ff0c9f4f7cf Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 4 Sep 2020 12:09:20 +0530 Subject: [PATCH 060/154] fix: Run bench as frappe user --- .github/workflows/publish-assets.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 50367566bd..2f3820a79a 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -15,9 +15,9 @@ jobs: steps: - name: Set up bench run: | - sudo bench init frappe-bench --skip-assets --python $(which python) + sudo -H -u frappe bash -c 'bench init frappe-bench --skip-assets --python $(which python)' cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA - bench build + sudo -H -u frappe bash -c 'bench build' - name: Package assets run: | From f398be17cca4ded53f02636c8f143b3440806a28 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 12:21:12 +0530 Subject: [PATCH 061/154] fix: Don't use bench docker image --- .github/workflows/publish-assets.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 2f3820a79a..5d395b8696 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -1,23 +1,28 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Build Assets on develop and Publish to Public bucket +name: Build and Publish Assets built on: push: - branches: [ develop ] + branches: [ build-assets ] # for testing only + # branches: [ develop ] jobs: build: runs-on: ubuntu-latest - container: frappe/bench:latest steps: + - uses: actions/setup-node@v1 + with: + python-version: '12.x' + - uses: actions/setup-python@v2 + with: + python-version: '3.x' - name: Set up bench run: | - sudo -H -u frappe bash -c 'bench init frappe-bench --skip-assets --python $(which python)' + npm install -g yarn + pip3 install -U frappe-bench + bench init frappe-bench --skip-assets --python $(which python) cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA - sudo -H -u frappe bash -c 'bench build' + bench build - name: Package assets run: | From e326825fde6001dd5e6c259c8e786b8801c3f08b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 13:05:09 +0530 Subject: [PATCH 062/154] fix: Reduce bulk --- .github/workflows/publish-assets.yml | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 5d395b8696..13469973e9 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -10,39 +10,39 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v1 - with: - python-version: '12.x' - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Set up bench - run: | - npm install -g yarn - pip3 install -U frappe-bench - bench init frappe-bench --skip-assets --python $(which python) - cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA - bench build + - uses: actions/setup-node@v1 + with: + python-version: '12.x' + - uses: actions/setup-python@v2 + with: + python-version: '3.6' + - name: Set up bench + run: | + npm install -g yarn + pip3 install -U frappe-bench + bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) + cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA + bench build - - name: Package assets - run: | - cd frappe-bench/sites/assets - tar -cvpzf $GITHUB_SHA.tar.gz /js /css + - name: Package assets + run: | + cd frappe-bench/sites/assets + tar -cvpzf $GITHUB_SHA.tar.gz /js /css - - name: Publish assets - uses: jakejarvis/s3-sync-action@master - with: - args: --acl public-read - env: - AWS_S3_BUCKET: 'frappe-assets' - AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} - AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' - AWS_REGION: 'fr-par' - SOURCE_DIR: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz + - name: Publish assets + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: 'frappe-assets' + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} + AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' + AWS_REGION: 'fr-par' + SOURCE_DIR: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz - - name: Generate Artifacts - uses: actions/upload-artifact@v2 - with: - name: $GITHUB_SHA.tar.gz - path: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz + - name: Generate Artifacts + uses: actions/upload-artifact@v2 + with: + name: $GITHUB_SHA.tar.gz + path: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz From cdecb5cd7bf5567e9459a13e863434d8e9988120 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 13:13:38 +0530 Subject: [PATCH 063/154] fix: Use checkout and --frappe-path --- .github/workflows/publish-assets.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 13469973e9..07d7ce37e9 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -10,19 +10,21 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 + with: + path: 'frappe' - uses: actions/setup-node@v1 with: python-version: '12.x' - uses: actions/setup-python@v2 with: python-version: '3.6' - - name: Set up bench + - name: Set up bench for current push run: | npm install -g yarn pip3 install -U frappe-bench - bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) - cd frappe-bench/apps/frappe && git checkout $GITHUB_SHA - bench build + bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe + cd frappe-bench && bench build - name: Package assets run: | From dffc0ca5b7fc230fbb9925efbc0e85838b036007 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Fri, 4 Sep 2020 13:15:12 +0530 Subject: [PATCH 064/154] fix(minor): Use get_safe_globals --- .../doctype/data_migration_mapping/data_migration_mapping.py | 2 +- frappe/email/doctype/notification/notification.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py index 097652fb88..1cc54a0d1a 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py @@ -64,7 +64,7 @@ def get_value_from_fieldname(field_map, fieldname_field, doc): field_name = get_source_value(field_map, fieldname_field) if field_name.startswith('eval:'): - value = frappe.safe_eval(field_name[5:], dict(frappe=frappe)) + value = frappe.safe_eval(field_name[5:], get_safe_globals()) elif field_name[0] in ('"', "'"): value = field_name[1:-1] else: diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index cd5aacabbd..5355d56b35 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -10,6 +10,7 @@ from frappe.model.document import Document from frappe.core.doctype.role.role import get_info_based_on_role, get_user_info from frappe.utils import validate_email_address, nowdate, parse_val, is_html, add_to_date from frappe.utils.jinja import validate_template +from frappe.utils.safe_exec import get_safe_globals from frappe.modules.utils import export_module_json, get_doc_module from six import string_types from frappe.integrations.doctype.slack_webhook_url.slack_webhook_url import send_slack_message @@ -415,7 +416,7 @@ def evaluate_alert(doc, alert, event): frappe.utils.get_link_to_form('Error Log', error_log.name))) def get_context(doc): - return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)} + return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))} def get_assignees(doc): assignees = [] From e1f8f6e18bd1ece66a5df99d5d28afbbae8382f0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 13:23:51 +0530 Subject: [PATCH 065/154] fix: Use relative paths for tarring asset files --- .github/workflows/publish-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 07d7ce37e9..de76274bc8 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -29,7 +29,7 @@ jobs: - name: Package assets run: | cd frappe-bench/sites/assets - tar -cvpzf $GITHUB_SHA.tar.gz /js /css + tar -cvpzf $GITHUB_SHA.tar.gz ./js ./css - name: Publish assets uses: jakejarvis/s3-sync-action@master From ed598fef491555034baebcad81b3d4c9043c9a68 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 13:34:44 +0530 Subject: [PATCH 066/154] fix: Path for builds push and artifacts generation --- .github/workflows/publish-assets.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index de76274bc8..44917ddeef 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -28,8 +28,8 @@ jobs: - name: Package assets run: | - cd frappe-bench/sites/assets - tar -cvpzf $GITHUB_SHA.tar.gz ./js ./css + mkdir -p $GITHUB_WORKSPACE/build + tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css - name: Publish assets uses: jakejarvis/s3-sync-action@master @@ -41,10 +41,11 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' AWS_REGION: 'fr-par' - SOURCE_DIR: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz + SOURCE_DIR: '$GITHUB_WORKSPACE/build' - name: Generate Artifacts + if: always() uses: actions/upload-artifact@v2 with: name: $GITHUB_SHA.tar.gz - path: frappe-bench/sites/assets/$GITHUB_SHA.tar.gz + path: $GITHUB_WORKSPACE/build From 84b9670b947679990519f085eeff8a255b035e93 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 14:18:08 +0530 Subject: [PATCH 067/154] feat: Add built asset to release made --- .github/workflows/publish-assets.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 44917ddeef..e3f1f1bc8a 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -1,9 +1,12 @@ -name: Build and Publish Assets built +name: Build and Publish Assets on: push: branches: [ build-assets ] # for testing only # branches: [ develop ] + release: + types: + - published jobs: build: @@ -21,6 +24,7 @@ jobs: python-version: '3.6' - name: Set up bench for current push run: | + echo $GITHUB_EVENT_NAME npm install -g yarn pip3 install -U frappe-bench bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe @@ -31,7 +35,8 @@ jobs: mkdir -p $GITHUB_WORKSPACE/build tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css - - name: Publish assets + - name: Publish assets to S3 + if: $GITHUB_EVENT_NAME == "pull_request" uses: jakejarvis/s3-sync-action@master with: args: --acl public-read @@ -43,9 +48,20 @@ jobs: AWS_REGION: 'fr-par' SOURCE_DIR: '$GITHUB_WORKSPACE/build' - - name: Generate Artifacts + - name: Attach Assets to Release + if: $GITHUB_EVENT_NAME == "release" + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz + asset_name: assets.tar.gz + tag: ${{ github.ref }} + overwrite: true + body: "Assets automatically generated which may be used to avoid re-building on local benches" + + - name: Generate Action Artifacts if: always() uses: actions/upload-artifact@v2 with: - name: $GITHUB_SHA.tar.gz - path: $GITHUB_WORKSPACE/build + name: assets + path: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz From 93dcb8663f80272e49caeb354d2de6c7ece08e7a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 14:47:36 +0530 Subject: [PATCH 068/154] fix: Conditonal expressions for steps --- .github/workflows/publish-assets.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index e3f1f1bc8a..d1f1ba3a11 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -24,7 +24,6 @@ jobs: python-version: '3.6' - name: Set up bench for current push run: | - echo $GITHUB_EVENT_NAME npm install -g yarn pip3 install -U frappe-bench bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe @@ -36,7 +35,7 @@ jobs: tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css - name: Publish assets to S3 - if: $GITHUB_EVENT_NAME == "pull_request" + if: github.event == 'pull_request' uses: jakejarvis/s3-sync-action@master with: args: --acl public-read @@ -49,7 +48,7 @@ jobs: SOURCE_DIR: '$GITHUB_WORKSPACE/build' - name: Attach Assets to Release - if: $GITHUB_EVENT_NAME == "release" + if: github.event == 'release' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} From af8d94530b87c89f3b001eaa54a8faf56fd7d7a0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 15:06:31 +0530 Subject: [PATCH 069/154] fix: ready-for-test --- .github/workflows/publish-assets.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index d1f1ba3a11..8155d36f35 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -2,8 +2,7 @@ name: Build and Publish Assets on: push: - branches: [ build-assets ] # for testing only - # branches: [ develop ] + branches: [ develop ] release: types: - published From 997e5b9c48143e87349acb2ae518ec6656ebb807 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 15:18:31 +0530 Subject: [PATCH 070/154] fix: Seperate workflows for releases and develop --- ...-assets.yml => publish-assets-develop.yml} | 22 ---------- .github/workflows/publish-assets-releases.yml | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 22 deletions(-) rename .github/workflows/{publish-assets.yml => publish-assets-develop.yml} (65%) create mode 100644 .github/workflows/publish-assets-releases.yml diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets-develop.yml similarity index 65% rename from .github/workflows/publish-assets.yml rename to .github/workflows/publish-assets-develop.yml index 8155d36f35..f13d98e6a5 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -3,9 +3,6 @@ name: Build and Publish Assets on: push: branches: [ develop ] - release: - types: - - published jobs: build: @@ -34,7 +31,6 @@ jobs: tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css - name: Publish assets to S3 - if: github.event == 'pull_request' uses: jakejarvis/s3-sync-action@master with: args: --acl public-read @@ -45,21 +41,3 @@ jobs: AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' AWS_REGION: 'fr-par' SOURCE_DIR: '$GITHUB_WORKSPACE/build' - - - name: Attach Assets to Release - if: github.event == 'release' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz - asset_name: assets.tar.gz - tag: ${{ github.ref }} - overwrite: true - body: "Assets automatically generated which may be used to avoid re-building on local benches" - - - name: Generate Action Artifacts - if: always() - uses: actions/upload-artifact@v2 - with: - name: assets - path: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml new file mode 100644 index 0000000000..faeba15af4 --- /dev/null +++ b/.github/workflows/publish-assets-releases.yml @@ -0,0 +1,42 @@ +name: Build and Publish Assets built for releases + +on: + release: + types: + - created + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + path: 'frappe' + - uses: actions/setup-node@v1 + with: + python-version: '12.x' + - uses: actions/setup-python@v2 + with: + python-version: '3.6' + - name: Set up bench for current push + run: | + npm install -g yarn + pip3 install -U frappe-bench + bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe + cd frappe-bench && bench build + + - name: Package assets + run: | + mkdir -p $GITHUB_WORKSPACE/build + tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css + + - name: Attach Assets to Release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_ASSETS_TOKEN }} + file: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz + asset_name: assets.tar.gz + tag: ${{ github.ref }} + overwrite: true + body: "Assets automatically generated which may be used to avoid re-building on local benches" From 1b90e713c639d02c7a052f3ab99d3d8112e5ac23 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 15:47:06 +0530 Subject: [PATCH 071/154] fix: Use Github token --- .github/workflows/publish-assets-develop.yml | 2 +- .github/workflows/publish-assets-releases.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index f13d98e6a5..8546011ebe 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -1,4 +1,4 @@ -name: Build and Publish Assets +name: Build and Publish Assets for Development on: push: diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml index faeba15af4..c914133fbf 100644 --- a/.github/workflows/publish-assets-releases.yml +++ b/.github/workflows/publish-assets-releases.yml @@ -1,4 +1,4 @@ -name: Build and Publish Assets built for releases +name: Build and Publish Assets built for Releases on: release: @@ -34,7 +34,7 @@ jobs: - name: Attach Assets to Release uses: svenstaro/upload-release-action@v2 with: - repo_token: ${{ secrets.GITHUB_ASSETS_TOKEN }} + repo_token: ${{ secrets.GITHUB_TOKEN }} file: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz asset_name: assets.tar.gz tag: ${{ github.ref }} From 0109bde0657f74c784c044e505f6109ac75bb512 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 4 Sep 2020 15:52:13 +0530 Subject: [PATCH 072/154] feat: add razorpay dependency --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index af3104cce7..3c0a984f7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -71,4 +71,5 @@ zxcvbn-python==4.4.24 pycryptodome==3.9.8 paytmchecksum==1.7.0 wrapt==1.10.11 -twilio==6.44.2 \ No newline at end of file +twilio==6.44.2 +razorpay=1.2.0 \ No newline at end of file From cd39bbc33813ad01906f91118377795c12ef6d01 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 4 Sep 2020 15:52:21 +0530 Subject: [PATCH 073/154] feat: add init client API --- .../doctype/razorpay_settings/razorpay_settings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py index 1d2f7f9495..5e7e75c745 100644 --- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py +++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py @@ -65,6 +65,7 @@ import frappe from frappe import _ import json import hmac +import razorpay import hashlib from six.moves.urllib.parse import urlencode from frappe.model.document import Document @@ -75,6 +76,11 @@ from frappe.integrations.utils import (make_get_request, make_post_request, crea class RazorpaySettings(Document): supported_currencies = ["INR"] + def init_client(self): + if self.api_key: + self.secret = self.get_password(fieldname="api_secret", raise_exception=False) + self.client = razorpay.Client(auth=(self.api_key, self.secret)) + def validate(self): create_payment_gateway('Razorpay') call_hook_method('payment_gateway_enabled', gateway='Razorpay') From 3cf9569e4946949916d4eaa5f83ae180913eb8f6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Sep 2020 16:04:10 +0530 Subject: [PATCH 074/154] fix: Use a different action to attach built asset --- .github/workflows/publish-assets-releases.yml | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml index c914133fbf..5c412ea1b0 100644 --- a/.github/workflows/publish-assets-releases.yml +++ b/.github/workflows/publish-assets-releases.yml @@ -2,8 +2,10 @@ name: Build and Publish Assets built for Releases on: release: - types: - - created + types: [ created ] + +env: + GITHUB_TOKEN: ${{ github.token }} jobs: build: @@ -29,14 +31,17 @@ jobs: - name: Package assets run: | mkdir -p $GITHUB_WORKSPACE/build - tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css + tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css - - name: Attach Assets to Release - uses: svenstaro/upload-release-action@v2 + - name: Get release + id: get_release + uses: bruceadams/get-release@v1.2.0 + + - name: Upload built Assets to Release + uses: actions/upload-release-asset@v1.0.2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: build/assets.tar.gz asset_name: assets.tar.gz - tag: ${{ github.ref }} - overwrite: true - body: "Assets automatically generated which may be used to avoid re-building on local benches" + asset_content_type: application/octet-stream + From 3349c9f9426b9ad6f346abea28b894baa1c78cf9 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Fri, 4 Sep 2020 16:28:50 +0530 Subject: [PATCH 075/154] fix(minor): Prevent path traversal --- frappe/utils/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 8a25c84276..e9593ff89e 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -738,7 +738,7 @@ def get_thumbnail_base64_for_image(src): if not src: frappe.throw('Invalid source for image: {0}'.format(src)) - if not src.startswith('/files'): + if not src.startswith('/files') or '..' in src: return def _get_base64(): From d94a1313f068b48a1f76759822aa07400cff572d Mon Sep 17 00:00:00 2001 From: prssanna Date: Sat, 5 Sep 2020 13:14:12 +0530 Subject: [PATCH 076/154] fix: catch exception while setting In-Reply-To in email header --- frappe/email/email_body.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index d545190c47..6d0c6f8f33 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -207,7 +207,11 @@ class EMail: def set_in_reply_to(self, in_reply_to): """Used to send the Message-Id of a received email back as In-Reply-To""" - self.msg_root["In-Reply-To"] = in_reply_to + try: + self.msg_root["In-Reply-To"] = in_reply_to + except ValueError: + # in_reply_to may contain line feed characters, so ignore in that case + pass def make(self): """build into msg_root""" From e48ed816c3b2802b389c231f1fb8d902f22048e8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 6 Sep 2020 16:35:28 +0530 Subject: [PATCH 077/154] fix(Event Streaming): remove in_test flag from Event Consumer --- .../doctype/event_consumer/event_consumer.json | 13 ++----------- .../doctype/event_consumer/event_consumer.py | 7 ++++--- .../doctype/event_producer/event_producer.py | 5 +---- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.json b/frappe/event_streaming/doctype/event_consumer/event_consumer.json index d863677e03..85970dc277 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.json +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.json @@ -13,8 +13,7 @@ "api_secret", "column_break_6", "user", - "incoming_change", - "in_test" + "incoming_change" ], "fields": [ { @@ -60,14 +59,6 @@ "label": "Incoming Change", "read_only": 1 }, - { - "default": "0", - "fieldname": "in_test", - "fieldtype": "Check", - "hidden": 1, - "label": "In Test", - "read_only": 1 - }, { "fieldname": "consumer_doctypes", "fieldtype": "Table", @@ -78,7 +69,7 @@ ], "in_create": 1, "links": [], - "modified": "2019-12-30 11:52:16.276047", + "modified": "2020-09-06 15:42:00.746493", "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 a53d046be5..2e10c71d0d 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import json import requests +import os from frappe.model.document import Document from frappe.frappeclient import FrappeClient from frappe.utils.data import get_url @@ -14,10 +15,11 @@ from frappe.utils.background_jobs import get_jobs class EventConsumer(Document): def validate(self): - if self.in_test: + # approve subscribed doctypes for tests + # frappe.flags.in_test won't work here as tests are running on the consumer site + if os.environ.get('CI'): for entry in self.consumer_doctypes: entry.status = 'Approved' - self.in_test = False def on_update(self): if not self.incoming_change: @@ -80,7 +82,6 @@ def register_consumer(data): api_secret = frappe.generate_hash(length=10) consumer.api_key = api_key consumer.api_secret = api_secret - consumer.in_test = data['in_test'] consumer.insert(ignore_permissions=True) frappe.db.commit() diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py index 73aea114ab..555b71f851 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/event_producer.py @@ -75,8 +75,7 @@ class EventProducer(Document): return { 'event_consumer': get_url(), 'consumer_doctypes': json.dumps(consumer_doctypes), - 'user': self.user, - 'in_test': frappe.flags.in_test + 'user': self.user } def create_custom_fields(self): @@ -110,8 +109,6 @@ class EventProducer(Document): 'status': get_approval_status(config, ref_doctype), 'unsubscribed': entry.unsubscribe }) - if frappe.flags.in_test: - event_consumer.in_test = True event_consumer.user = self.user event_consumer.incoming_change = True producer_site.update(event_consumer) From 3f5a50d677e7a3bc3a58e32377956689904e1a00 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 6 Sep 2020 18:37:22 +0530 Subject: [PATCH 078/154] fix: Restrict ui test helper method to dev server mode --- frappe/__init__.py | 1 + frappe/tests/ui_test_helpers.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/frappe/__init__.py b/frappe/__init__.py index 36a8b48ecd..46792e82a8 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -182,6 +182,7 @@ def init(site, sites_path=None, new_site=False): local.meta_cache = {} local.form_dict = _dict() local.session = _dict() + local.dev_server = os.environ.get('DEV_SERVER', False) setup_module_map() diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 40ebc8ea6e..ef572c6971 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -9,6 +9,9 @@ def create_if_not_exists(doc): :param doc: dict of field value pairs. can be a list of dict for multiple records. ''' + if not frappe.local.dev_server: + frappe.throw('This method can only be accessed in development', frappe.PermissionError) + doc = frappe.parse_json(doc) if not isinstance(doc, list): From 4f5acf3d9b31609d60e12e0220790ff07f7b29e9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2020 09:21:07 +0530 Subject: [PATCH 079/154] chore(deps): [security] bump js-yaml from 3.12.2 to 3.14.0 (#11413) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.2 to 3.14.0. **This update includes security fixes.** - [Release notes](https://github.com/nodeca/js-yaml/releases) - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/3.12.2...3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index e5c975c357..23bfe25255 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3965,7 +3965,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.10.0: +js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -3973,22 +3973,6 @@ js-yaml@^3.10.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.12.0, js-yaml@^3.9.0: - version "3.12.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc" - integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbarcode@^3.9.0: version "3.11.0" resolved "https://registry.yarnpkg.com/jsbarcode/-/jsbarcode-3.11.0.tgz#20623e008b101ef45d0cce9c8022cdf49be28547" From 59a74c0634440d3ad13305b62825891e436bd698 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 7 Sep 2020 10:08:27 +0530 Subject: [PATCH 080/154] fix: minor typo in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3c0a984f7b..67952d54b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -72,4 +72,4 @@ pycryptodome==3.9.8 paytmchecksum==1.7.0 wrapt==1.10.11 twilio==6.44.2 -razorpay=1.2.0 \ No newline at end of file +razorpay==1.2.0 From a24104da539d804109feb825b370c098caf5ee32 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 6 Sep 2020 21:54:30 -0700 Subject: [PATCH 081/154] feat(treeview): Display multiple roots automatically in tree view (#10967) Co-authored-by: Prssanna Desai --- frappe/desk/treeview.py | 5 ++-- frappe/public/js/frappe/ui/tree.js | 30 +++++++++++++---------- frappe/public/js/frappe/views/treeview.js | 17 +++++++++++-- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index 2944f20a37..811143be03 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -6,12 +6,11 @@ import frappe from frappe import _ @frappe.whitelist() -def get_all_nodes(doctype, parent, tree_method, **filters): +def get_all_nodes(doctype, label, parent, tree_method, **filters): '''Recursively gets all data from tree nodes''' if 'cmd' in filters: del filters['cmd'] - filters.pop('data', None) tree_method = frappe.get_attr(tree_method) @@ -20,7 +19,7 @@ def get_all_nodes(doctype, parent, tree_method, **filters): frappe.throw(_("Not Permitted"), frappe.PermissionError) data = tree_method(doctype, parent, **filters) - out = [dict(parent=parent, data=data)] + out = [dict(parent=label, data=data)] if 'is_root' in filters: del filters['is_root'] diff --git a/frappe/public/js/frappe/ui/tree.js b/frappe/public/js/frappe/ui/tree.js index a6287ed2c8..d50d78a7e5 100644 --- a/frappe/public/js/frappe/ui/tree.js +++ b/frappe/public/js/frappe/ui/tree.js @@ -5,17 +5,20 @@ frappe.provide('frappe.ui'); frappe.ui.Tree = class { constructor({ - parent, label, icon_set, toolbar, expandable, with_skeleton=1, // eslint-disable-line + parent, label, root_value, icon_set, toolbar, expandable, with_skeleton=1, // eslint-disable-line args, method, get_label, on_render, on_click // eslint-disable-line }) { $.extend(this, arguments[0]); + if (root_value == null) { + this.root_value = label; + } this.setup_treenode_class(); this.nodes = {}; this.wrapper = $('
').appendTo(this.parent); - if(with_skeleton) this.wrapper.addClass('with-skeleton'); + if (with_skeleton) this.wrapper.addClass('with-skeleton'); - if(!icon_set) { + if (!icon_set) { this.icon_set = { open: 'fa fa-fw fa-folder-open', closed: 'fa fa-fw fa-folder', @@ -42,8 +45,9 @@ frappe.ui.Tree = class { }); } - get_all_nodes(value, is_root) { + get_all_nodes(value, is_root, label) { var args = Object.assign({}, this.args); + args.label = label || value; args.parent = value; args.is_root = is_root; @@ -88,7 +92,7 @@ frappe.ui.Tree = class { expandable: true, is_root: true, data: { - value: this.label + value: this.root_value } }); this.expand_node(this.root_node, false); @@ -144,25 +148,25 @@ frappe.ui.Tree = class { } load_children(node, deep=false) { - let value = node.data.value, is_root = node.is_root; + let lab = node.label, value = node.data.value, is_root = node.is_root; if(!deep) { frappe.run_serially([ - () => {return this.get_nodes(value, is_root);}, - (data_set) => { this.render_node_children(node, data_set); }, - () => { this.set_selected_node(node); } + () => this.get_nodes(value, is_root), + (data_set) => this.render_node_children(node, data_set), + () => this.set_selected_node(node) ]); } else { frappe.run_serially([ - () => {return this.get_all_nodes(value, is_root);}, - (data_list) => { this.render_children_of_all_nodes(data_list); }, - () => { this.set_selected_node(node); } + () => this.get_all_nodes(value, is_root, lab), + (data_list) => this.render_children_of_all_nodes(data_list), + () => this.set_selected_node(node) ]); } } render_children_of_all_nodes(data_list) { - data_list.map(d => { this.render_node_children(this.nodes[d.parent], d.data); }); + data_list.map(d => this.render_node_children(this.nodes[d.parent], d.data)); } render_node_children(node, data_set) { diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js index 815e561f41..777ce14da6 100644 --- a/frappe/public/js/frappe/views/treeview.js +++ b/frappe/public/js/frappe/views/treeview.js @@ -39,6 +39,7 @@ frappe.views.TreeView = Class.extend({ this.get_permissions(); this.make_page(); this.make_filters(); + this.root_value = null; if (me.opts.get_tree_root) { this.get_root(); @@ -129,7 +130,13 @@ frappe.views.TreeView = Class.extend({ args: me.args, callback: function(r) { if (r.message) { - me.root_label = r.message[0]["value"]; + if (r.message.length > 1) { + me.root_label = me.doctype; + me.root_value = ""; + } else { + me.root_label = r.message[0]["value"]; + me.root_value = me.root_label; + } me.make_tree(); } } @@ -138,9 +145,15 @@ frappe.views.TreeView = Class.extend({ make_tree: function() { $(this.parent).find(".tree").remove(); + var use_label = this.args[this.opts.root_label] || this.root_label || this.opts.root_label; + var use_value = this.root_value; + if (use_value == null) { + use_value = use_label; + } this.tree = new frappe.ui.Tree({ parent: this.body, - label: this.args[this.opts.root_label] || this.root_label || this.opts.root_label, + label: use_label, + root_value: use_value, expandable: true, args: this.args, From 8413f920de8b14db26f79de5364cce16e9674af2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 7 Sep 2020 11:10:46 +0530 Subject: [PATCH 082/154] fix: Permission check on adding custom columns in query reports --- frappe/desk/query_report.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index a1cfd02132..411c3759cf 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -462,6 +462,9 @@ def add_total_row(result, columns, meta = None): @frappe.whitelist() def get_data_for_custom_field(doctype, field): + if not frappe.has_permission(doctype, "read"): + frappe.throw(_("Not permitted"), frappe.PermissionError) + value_map = frappe._dict(frappe.get_all(doctype, fields=["name", field], as_list=1)) From a8e7ffd7672a392dd22051c080fd7e7080cf349f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 7 Sep 2020 11:36:31 +0530 Subject: [PATCH 083/154] refactor: don't attach secret to self (#11428) --- .../doctype/razorpay_settings/razorpay_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py index 5e7e75c745..af7686c9b0 100644 --- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py +++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py @@ -78,8 +78,8 @@ class RazorpaySettings(Document): def init_client(self): if self.api_key: - self.secret = self.get_password(fieldname="api_secret", raise_exception=False) - self.client = razorpay.Client(auth=(self.api_key, self.secret)) + secret = self.get_password(fieldname="api_secret", raise_exception=False) + self.client = razorpay.Client(auth=(self.api_key, secret)) def validate(self): create_payment_gateway('Razorpay') From 22701346762a583b5c5ccd3b01fb714111deb2c4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 7 Sep 2020 12:47:56 +0530 Subject: [PATCH 084/154] fix(Newsletter): default content type while getting message --- frappe/email/doctype/newsletter/newsletter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 0a0a13a6ce..a4d60706eb 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -85,12 +85,12 @@ class Newsletter(WebsiteGenerator): self.db_set("scheduled_to_send", len(self.recipients)) def get_message(self): - + return { 'Rich Text': self.message, 'Markdown': markdown(self.message_md), 'HTML': self.message_html - }[self.content_type] + }[self.content_type or 'Rich Text'] def get_recipients(self): """Get recipients from Email Group""" From 99bd9fc3cfe3bc2f92b511c2d982e59d3dd29685 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 7 Sep 2020 13:17:24 +0530 Subject: [PATCH 085/154] fix: Not permitted in title case --- frappe/desk/query_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 411c3759cf..08ef7ae485 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -463,7 +463,7 @@ def add_total_row(result, columns, meta = None): def get_data_for_custom_field(doctype, field): if not frappe.has_permission(doctype, "read"): - frappe.throw(_("Not permitted"), frappe.PermissionError) + frappe.throw(_("Not Permitted"), frappe.PermissionError) value_map = frappe._dict(frappe.get_all(doctype, fields=["name", field], From 8a9545afdb457f177740cc825f393369d4660977 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 15:10:54 +0530 Subject: [PATCH 086/154] fix: Change assets endpoint --- .github/workflows/publish-assets-develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index 8546011ebe..522cfc815c 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -38,6 +38,6 @@ jobs: AWS_S3_BUCKET: 'frappe-assets' AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} - AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' + AWS_S3_ENDPOINT: 'http://assets.frappeframework.com' AWS_REGION: 'fr-par' SOURCE_DIR: '$GITHUB_WORKSPACE/build' From f53fe3e854572e67e7ec61cbee037e8ca3f7b160 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 15:14:37 +0530 Subject: [PATCH 087/154] feat: Ship Built Assets --- frappe/build.py | 136 ++++++++++++++++++++++++++++++++++----- frappe/commands/utils.py | 12 +++- rollup/build.js | 48 ++++++++++++-- rollup/config.js | 28 +++++++- 4 files changed, 202 insertions(+), 22 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 761541f7a9..7bfffb3c8c 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -11,24 +11,129 @@ import warnings import tempfile from distutils.spawn import find_executable -from six import iteritems, text_type - import frappe from frappe.utils.minify import JavascriptMinify +import click +from requests import get +from six import iteritems, text_type +from six.moves.urllib.parse import urlparse + timestamps = {} app_paths = None +sites_path = os.path.abspath(os.getcwd()) + + +def download_file(url, prefix): + filename = urlparse(url).path.split("/")[-1] + local_filename = os.path.join(prefix, filename) + with get(url, stream=True, allow_redirects=True) as r: + r.raise_for_status() + with open(local_filename, "wb") as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + return local_filename + + +def exists(url): + from requests import head + return head(url, allow_redirects=True) + + +def build_missing_files(): + # check which files dont exist yet from the build.json and tell build.js to build only those! + missing_assets = [] + current_asset_files = [ + "js/{0}".format(x) for x in os.listdir(os.path.join(sites_path, "assets", "js")) + ] + [ + "css/{0}".format(x) for x in os.listdir(os.path.join(sites_path, "assets", "css")) + ] + + with open(os.path.join(sites_path, "assets", "frappe", "build.json")) as f: + all_asset_files = json.load(f).keys() + + for asset in all_asset_files: + if asset.replace("concat:", "") not in current_asset_files: + missing_assets.append(asset) + + if missing_assets: + from subprocess import check_call + from shlex import split + + click.secho("Building Missing Assets...", fg="yellow") + command = split( + "node rollup/build.js --files {0} --no-concat".format(",".join(missing_assets)) + ) + check_call(command, cwd=os.path.join("..", "apps", "frappe")) + + +def download_frappe_assets(): + """Downloads and sets up Frappe assets if they exist based on the current + commit HEAD. + Returns True if correctly setup else returns False. + """ + from subprocess import getoutput + + frappe_head = getoutput("cd ../apps/frappe && git rev-parse HEAD") + exc = False + + if frappe_head: + from tempfile import mkdtemp + + tag = getoutput( + "cd ../apps/frappe && git show-ref --tags -d | grep %s | sed -e 's,.*" + " refs/tags/,,' -e 's/\^{}//'" + % frappe_head + ) + + if tag: + # if tag exists, download assets from github release + url = "https://github.com/frappe/frappe/releases/{0}/assets.tar.gz".format(tag) + else: + url = "http://assets.frappeframework.com/{0}.tar.gz".format(frappe_head) + + try: + click.secho("Retreiving Assets...", fg="yellow") + + if not exists(url): + return False + + prefix = mkdtemp(prefix="frappe-assets-", suffix=frappe_head) + assets_archive = download_file(url, prefix) + + if assets_archive: + import subprocess + + click.secho("Extracting Assets...", fg="yellow") + subprocess.check_output( + ["tar", "xf", assets_archive, "--strip", "3"], cwd=sites_path + ) + build_missing_files() + return True + else: + raise + except Exception: + exc = True + click.secho("No Assets Found...Building...", fg="yellow") + print(frappe.get_traceback()) + finally: + try: + shutil.rmtree(os.path.dirname(assets_archive)) + except Exception: + pass + + return not exc def symlink(target, link_name, overwrite=False): - ''' + """ Create a symbolic link named link_name pointing to target. If link_name exists then FileExistsError is raised, unless overwrite=True. When trying to overwrite a directory, IsADirectoryError is raised. Source: https://stackoverflow.com/a/55742015/10309266 - ''' + """ if not overwrite: return os.symlink(target, link_name) @@ -76,27 +181,28 @@ def setup(): def get_node_pacman(): - pacmans = ['yarn', 'npm'] - for exec_ in pacmans: - exec_ = find_executable(exec_) - if exec_: - return exec_ - raise ValueError('No Node.js Package Manager found.') + exec_ = find_executable("yarn") + if exec_: + return exec_ + raise ValueError("Yarn not found") -def bundle(no_compress, app=None, make_copy=False, restore=False, verbose=False): +def bundle(no_compress, app=None, make_copy=False, restore=False, verbose=False, skip_frappe=False): """concat / minify js files""" setup() make_asset_dirs(make_copy=make_copy, restore=restore) pacman = get_node_pacman() - mode = 'build' if no_compress else 'production' - command = '{pacman} run {mode}'.format(pacman=pacman, mode=mode) + mode = "build" if no_compress else "production" + command = "{pacman} run {mode}".format(pacman=pacman, mode=mode) if app: - command += ' --app {app}'.format(app=app) + command += " --app {app}".format(app=app) - frappe_app_path = os.path.abspath(os.path.join(app_paths[0], '..')) + if skip_frappe: + command += " --skip_frappe" + + frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) check_yarn() frappe.commands.popen(command, cwd=frappe_app_path) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 721376016c..d688d1ab0c 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -19,14 +19,22 @@ from six import StringIO @click.option('--make-copy', is_flag=True, default=False, help='Copy the files instead of symlinking') @click.option('--restore', is_flag=True, default=False, help='Copy the files instead of symlinking with force') @click.option('--verbose', is_flag=True, default=False, help='Verbose') -def build(app=None, make_copy=False, restore = False, verbose=False): +@click.option('--force', is_flag=True, default=False, help='Force build assets instead of downloading available') +def build(app=None, make_copy=False, restore=False, verbose=False, force=False): "Minify + concatenate JS and CSS files, build translations" import frappe.build import frappe frappe.init('') # don't minify in developer_mode for faster builds no_compress = frappe.local.conf.developer_mode or False - frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore = restore, verbose=verbose) + + if not (force or app): + # skip building frappe if assets exist remotely + skip_frappe = frappe.build.download_frappe_assets() + else: + skip_frappe = False + + frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore = restore, verbose=verbose, skip_frappe=skip_frappe) @click.command('watch') diff --git a/rollup/build.js b/rollup/build.js index ea1ac54c09..5fdfe80790 100644 --- a/rollup/build.js +++ b/rollup/build.js @@ -15,17 +15,35 @@ const { } = require('./rollup.utils'); const { - get_options_for + get_options_for, + get_options } = require('./config'); -const build_for_app = process.argv[2] === '--app' ? process.argv[3] : null; +const skip_frappe = process.argv.includes("--skip_frappe") + +if (skip_frappe) { + let idx = apps_list.indexOf("frappe"); + if (idx > -1) { + apps_list.splice(idx, 1); + } +} + +const exists = (flag) => process.argv.indexOf(flag) != -1 +const value = (flag) => (process.argv.indexOf(flag) != -1) ? process.argv[process.argv.indexOf(flag) + 1] : null; + +const files = exists("--files") ? value("--files").split(",") : false; +const build_for_app = exists("--app") ? value("--app") : null; +const concat = !exists("--no-concat"); show_production_message(); ensure_js_css_dirs(); -concatenate_files(); +if (concat) concatenate_files(); create_build_file(); -if (build_for_app) { + +if (files) { + build_files(files); +} else if (build_for_app) { build_assets_for_app(build_for_app) .then(() => { run_build_command_for_app(build_for_app); @@ -68,6 +86,28 @@ function build_assets(app) { }); } +function build_files(files, app="frappe") { + for (let file of files) { + let options = get_options(file, app); + if (!options.length) return Promise.resolve(); + log(chalk.yellow(`\nBuilding ${app} assets...\n`)); + + let promises = options.map(({ inputOptions, outputOptions, output_file}) => { + return build(inputOptions, outputOptions) + .then(() => { + log(`${chalk.green('✔')} Built ${output_file}`); + }); + }); + + let start = Date.now(); + return Promise.all(promises) + .then(() => { + let time = Date.now() - start; + log(chalk.green(`✨ Done in ${time / 1000}s`)); + }); + } +} + function build(inputOptions, outputOptions) { return rollup.rollup(inputOptions) .then(bundle => bundle.write(outputOptions)) diff --git a/rollup/config.js b/rollup/config.js index b1816cb4c6..cdeb8eb952 100644 --- a/rollup/config.js +++ b/rollup/config.js @@ -165,6 +165,31 @@ function get_rollup_options_for_css(output_file, input_files) { }; } +function get_options(file, app="frappe") { + const build_json = get_build_json(app); + if (!build_json) return []; + + return Object.keys(build_json) + .map(output_file => { + if (output_file === file) { + if (output_file.startsWith('concat:')) return null; + const input_files = build_json[output_file] + .map(input_file => { + let prefix = get_app_path(app); + if (input_file.startsWith('node_modules/')) { + prefix = path.resolve(get_app_path(app), '..'); + } + return path.resolve(prefix, input_file); + }); + return Object.assign( + get_rollup_options(output_file, input_files), { + output_file + }); + } + }) + .filter(Boolean); +} + function get_options_for(app) { const build_json = get_build_json(app); if (!build_json) return []; @@ -205,5 +230,6 @@ function ignore_css() { }; module.exports = { - get_options_for + get_options_for, + get_options }; From e9cba25b59d59491d8f6a0aae0920feacd852012 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 15:15:46 +0530 Subject: [PATCH 088/154] style: Black --- frappe/build.py | 84 +++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 7bfffb3c8c..7aa3b430a7 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -213,22 +213,22 @@ def watch(no_compress): pacman = get_node_pacman() - frappe_app_path = os.path.abspath(os.path.join(app_paths[0], '..')) + frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) check_yarn() - frappe_app_path = frappe.get_app_path('frappe', '..') - frappe.commands.popen('{pacman} run watch'.format(pacman=pacman), cwd=frappe_app_path) + frappe_app_path = frappe.get_app_path("frappe", "..") + frappe.commands.popen("{pacman} run watch".format(pacman=pacman), cwd=frappe_app_path) def check_yarn(): - if not find_executable('yarn'): - print('Please install yarn using below command and try again.\nnpm install -g yarn') + if not find_executable("yarn"): + print("Please install yarn using below command and try again.\nnpm install -g yarn") def make_asset_dirs(make_copy=False, restore=False): # don't even think of making assets_path absolute - rm -rf ahead. assets_path = os.path.join(frappe.local.sites_path, "assets") - for dir_path in [os.path.join(assets_path, 'js'), os.path.join(assets_path, 'css')]: + for dir_path in [os.path.join(assets_path, "js"), os.path.join(assets_path, "css")]: if not os.path.exists(dir_path): os.makedirs(dir_path) @@ -237,24 +237,27 @@ def make_asset_dirs(make_copy=False, restore=False): app_base_path = os.path.abspath(os.path.dirname(pymodule.__file__)) symlinks = [] - app_public_path = os.path.join(app_base_path, 'public') + app_public_path = os.path.join(app_base_path, "public") # app/public > assets/app symlinks.append([app_public_path, os.path.join(assets_path, app_name)]) # app/node_modules > assets/app/node_modules if os.path.exists(os.path.abspath(app_public_path)): - symlinks.append([os.path.join(app_base_path, '..', 'node_modules'), os.path.join( - assets_path, app_name, 'node_modules')]) + symlinks.append( + [ + os.path.join(app_base_path, "..", "node_modules"), + os.path.join(assets_path, app_name, "node_modules"), + ] + ) app_doc_path = None - if os.path.isdir(os.path.join(app_base_path, 'docs')): - app_doc_path = os.path.join(app_base_path, 'docs') + if os.path.isdir(os.path.join(app_base_path, "docs")): + app_doc_path = os.path.join(app_base_path, "docs") - elif os.path.isdir(os.path.join(app_base_path, 'www', 'docs')): - app_doc_path = os.path.join(app_base_path, 'www', 'docs') + elif os.path.isdir(os.path.join(app_base_path, "www", "docs")): + app_doc_path = os.path.join(app_base_path, "www", "docs") if app_doc_path: - symlinks.append([app_doc_path, os.path.join( - assets_path, app_name + '_docs')]) + symlinks.append([app_doc_path, os.path.join(assets_path, app_name + "_docs")]) for source, target in symlinks: source = os.path.abspath(source) @@ -268,7 +271,7 @@ def make_asset_dirs(make_copy=False, restore=False): shutil.copytree(source, target) elif make_copy: if os.path.exists(target): - warnings.warn('Target {target} already exists.'.format(target=target)) + warnings.warn("Target {target} already exists.".format(target=target)) else: shutil.copytree(source, target) else: @@ -280,7 +283,7 @@ def make_asset_dirs(make_copy=False, restore=False): try: symlink(source, target, overwrite=True) except OSError: - print('Cannot link {} to {}'.format(source, target)) + print("Cannot link {} to {}".format(source, target)) else: # warnings.warn('Source {source} does not exist.'.format(source = source)) pass @@ -299,7 +302,7 @@ def get_build_maps(): build_maps = {} for app_path in app_paths: - path = os.path.join(app_path, 'public', 'build.json') + path = os.path.join(app_path, "public", "build.json") if os.path.exists(path): with open(path) as f: try: @@ -308,8 +311,7 @@ def get_build_maps(): source_paths = [] for source in sources: if isinstance(source, list): - s = frappe.get_pymodule_path( - source[0], *source[1].split("/")) + s = frappe.get_pymodule_path(source[0], *source[1].split("/")) else: s = os.path.join(app_path, source) source_paths.append(s) @@ -317,36 +319,42 @@ def get_build_maps(): build_maps[target] = source_paths except ValueError as e: print(path) - print('JSON syntax error {0}'.format(str(e))) + print("JSON syntax error {0}".format(str(e))) return build_maps def pack(target, sources, no_compress, verbose): from six import StringIO - outtype, outtxt = target.split(".")[-1], '' + outtype, outtxt = target.split(".")[-1], "" jsm = JavascriptMinify() for f in sources: suffix = None - if ':' in f: - f, suffix = f.split(':') + if ":" in f: + f, suffix = f.split(":") if not os.path.exists(f) or os.path.isdir(f): print("did not find " + f) continue timestamps[f] = os.path.getmtime(f) try: - with open(f, 'r') as sourcefile: - data = text_type(sourcefile.read(), 'utf-8', errors='ignore') + with open(f, "r") as sourcefile: + data = text_type(sourcefile.read(), "utf-8", errors="ignore") extn = f.rsplit(".", 1)[1] - if outtype == "js" and extn == "js" and (not no_compress) and suffix != "concat" and (".min." not in f): - tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO() + if ( + outtype == "js" + and extn == "js" + and (not no_compress) + and suffix != "concat" + and (".min." not in f) + ): + tmpin, tmpout = StringIO(data.encode("utf-8")), StringIO() jsm.minify(tmpin, tmpout) minified = tmpout.getvalue() if minified: - outtxt += text_type(minified or '', 'utf-8').strip('\n') + ';' + outtxt += text_type(minified or "", "utf-8").strip("\n") + ";" if verbose: print("{0}: {1}k".format(f, int(len(minified) / 1024))) @@ -354,27 +362,27 @@ def pack(target, sources, no_compress, verbose): # add to frappe.templates outtxt += html_to_js_template(f, data) else: - outtxt += ('\n/*\n *\t%s\n */' % f) - outtxt += '\n' + data + '\n' + outtxt += "\n/*\n *\t%s\n */" % f + outtxt += "\n" + data + "\n" except Exception: print("--Error in:" + f + "--") print(frappe.get_traceback()) - with open(target, 'w') as f: + with open(target, "w") as f: f.write(outtxt.encode("utf-8")) - print("Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024)))) + print("Wrote %s - %sk" % (target, str(int(os.path.getsize(target) / 1024)))) def html_to_js_template(path, content): - '''returns HTML template content as Javascript code, adding it to `frappe.templates`''' + """returns HTML template content as Javascript code, adding it to `frappe.templates`""" return """frappe.templates["{key}"] = '{content}';\n""".format( key=path.rsplit("/", 1)[-1][:-5], content=scrub_html_template(content)) def scrub_html_template(content): - '''Returns HTML content with removed whitespace and comments''' + """Returns HTML content with removed whitespace and comments""" # remove whitespace to a single space content = re.sub("\s+", " ", content) @@ -387,12 +395,12 @@ def scrub_html_template(content): def files_dirty(): for target, sources in iteritems(get_build_maps()): for f in sources: - if ':' in f: - f, suffix = f.split(':') + if ":" in f: + f, suffix = f.split(":") if not os.path.exists(f) or os.path.isdir(f): continue if os.path.getmtime(f) != timestamps.get(f): - print(f + ' dirty') + print(f + " dirty") return True else: return False From a7e0e96e4990ed2563000fcda673c7e9807eebae Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 15:16:00 +0530 Subject: [PATCH 089/154] chore: Drop dead code --- frappe/utils/change_log.py | 1 - frappe/utils/gitutils.py | 24 ------------------------ 2 files changed, 25 deletions(-) delete mode 100644 frappe/utils/gitutils.py diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py index c75b3289db..29fee2bac0 100644 --- a/frappe/utils/change_log.py +++ b/frappe/utils/change_log.py @@ -9,7 +9,6 @@ import frappe import requests import subprocess # nosec from frappe.utils import cstr -from frappe.utils.gitutils import get_app_branch from frappe import _, safe_decode diff --git a/frappe/utils/gitutils.py b/frappe/utils/gitutils.py deleted file mode 100644 index 10268a6581..0000000000 --- a/frappe/utils/gitutils.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals - -import subprocess - -def get_app_branch(app): - '''Returns branch of an app''' - try: - branch = subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app), - shell=True) - branch = branch.decode('utf-8') - branch = branch.strip() - return branch - except Exception: - return '' - -def get_app_last_commit_ref(app): - try: - commit_id = subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD'.format(app), - shell=True) - commit_id = commit_id.decode('utf-8') - commit_id = commit_id.strip()[:7] - return commit_id - except Exception: - return '' From fe44d2b97d0478a438cb80f5bbfbcd6750d17552 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 15:25:47 +0530 Subject: [PATCH 090/154] chore: Optimize imports --- frappe/commands/utils.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index d688d1ab0c..1affeb5a5e 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import, print_function -import click -import json, os, sys, subprocess +import json +import os +import subprocess +import sys from distutils.spawn import find_executable + +import click + import frappe -from frappe.commands import pass_context, get_site +from frappe.commands import get_site, pass_context from frappe.exceptions import SiteNotSpecifiedError -from frappe.utils import update_progress_bar, get_bench_path -from frappe.utils.response import json_handler -from coverage import Coverage -import cProfile, pstats -from six import StringIO +from frappe.utils import get_bench_path, update_progress_bar @click.command('build') @@ -23,7 +23,6 @@ from six import StringIO def build(app=None, make_copy=False, restore=False, verbose=False, force=False): "Minify + concatenate JS and CSS files, build translations" import frappe.build - import frappe frappe.init('') # don't minify in developer_mode for faster builds no_compress = frappe.local.conf.developer_mode or False @@ -34,7 +33,7 @@ def build(app=None, make_copy=False, restore=False, verbose=False, force=False): else: skip_frappe = False - frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore = restore, verbose=verbose, skip_frappe=skip_frappe) + frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore=restore, verbose=verbose, skip_frappe=skip_frappe) @click.command('watch') @@ -159,12 +158,16 @@ def execute(context, method, args=None, kwargs=None, profile=False): kwargs = {} if profile: + import cProfile pr = cProfile.Profile() pr.enable() ret = frappe.get_attr(method)(*args, **kwargs) if profile: + import pstats + from six import StringIO + pr.disable() s = StringIO() pstats.Stats(pr, stream=s).sort_stats('cumulative').print_stats(.5) @@ -175,6 +178,7 @@ def execute(context, method, args=None, kwargs=None, profile=False): finally: frappe.destroy() if ret: + from frappe.utils.response import json_handler print(json.dumps(ret, default=json_handler)) if not context.sites: @@ -500,6 +504,8 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), frappe.flags.skip_test_records = skip_test_records if coverage: + from coverage import Coverage + # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe') cov = Coverage(source=[source_path], omit=[ From baa9afc9454bb432d715ca50e3ad3e283ad21d79 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 7 Sep 2020 16:20:28 +0530 Subject: [PATCH 091/154] fix(Email): replace newline characters in In-Reply-To in email header (cherry picked from commit 3f2827bf03ecf57c20e9583bd40c43faccde16b8) --- frappe/email/email_body.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 6d0c6f8f33..001ba59e95 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -207,11 +207,7 @@ class EMail: def set_in_reply_to(self, in_reply_to): """Used to send the Message-Id of a received email back as In-Reply-To""" - try: - self.msg_root["In-Reply-To"] = in_reply_to - except ValueError: - # in_reply_to may contain line feed characters, so ignore in that case - pass + self.msg_root["In-Reply-To"] = in_reply_to.replace("\r", "").replace("\n", "") def make(self): """build into msg_root""" From f975da1cbfcc99827ea80787febd8469095a36a9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 16:55:43 +0530 Subject: [PATCH 092/154] fix: URL Fixes and code restructure --- frappe/build.py | 88 ++++++++++++++++++++++++---------------- frappe/commands/utils.py | 2 +- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 7aa3b430a7..242c83b15d 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -36,19 +36,18 @@ def download_file(url, prefix): return local_filename -def exists(url): - from requests import head - return head(url, allow_redirects=True) - - def build_missing_files(): # check which files dont exist yet from the build.json and tell build.js to build only those! missing_assets = [] - current_asset_files = [ - "js/{0}".format(x) for x in os.listdir(os.path.join(sites_path, "assets", "js")) - ] + [ - "css/{0}".format(x) for x in os.listdir(os.path.join(sites_path, "assets", "css")) - ] + current_asset_files = [] + + for type in ["css", "js"]: + current_asset_files.extend( + [ + "{0}/{0}".format(type, name) + for name in os.listdir(os.path.join(sites_path, "assets", type)) + ] + ) with open(os.path.join(sites_path, "assets", "frappe", "build.json")) as f: all_asset_files = json.load(f).keys() @@ -68,37 +67,59 @@ def build_missing_files(): check_call(command, cwd=os.path.join("..", "apps", "frappe")) -def download_frappe_assets(): - """Downloads and sets up Frappe assets if they exist based on the current - commit HEAD. - Returns True if correctly setup else returns False. - """ +def get_assets_link(frappe_head): from subprocess import getoutput + from requests import head - frappe_head = getoutput("cd ../apps/frappe && git rev-parse HEAD") - exc = False - - if frappe_head: - from tempfile import mkdtemp - - tag = getoutput( + tag = getoutput( "cd ../apps/frappe && git show-ref --tags -d | grep %s | sed -e 's,.*" " refs/tags/,,' -e 's/\^{}//'" % frappe_head ) - if tag: - # if tag exists, download assets from github release - url = "https://github.com/frappe/frappe/releases/{0}/assets.tar.gz".format(tag) - else: - url = "http://assets.frappeframework.com/{0}.tar.gz".format(frappe_head) + if tag: + # if tag exists, download assets from github release + url = "https://github.com/frappe/frappe/releases/download/{0}/assets.tar.gz".format(tag) + else: + url = "http://assets.frappeframework.com/{0}.tar.gz".format(frappe_head) + if not head(url): + raise ValueError("URL {0} doesn't exist".format(url)) + + return url + + +def handle_verbosity(): + import sys + import wrapt + + @wrapt.decorator + def verbosity(wrapped, instance, args, kwargs): + verbose = kwargs.get("verbose") or False + if not verbose: + sys.stdout = open(os.devnull, "wb") + ret = wrapped(*args, **kwargs) + sys.stdout = sys.__stdout__ + return ret + + return verbosity + +@handle_verbosity() +def download_frappe_assets(verbose=True): + """Downloads and sets up Frappe assets if they exist based on the current + commit HEAD. + Returns True if correctly setup else returns False. + """ + from subprocess import getoutput + from tempfile import mkdtemp + + assets_setup = False + frappe_head = getoutput("cd ../apps/frappe && git rev-parse HEAD") + + if frappe_head: try: + url = get_assets_link(frappe_head) click.secho("Retreiving Assets...", fg="yellow") - - if not exists(url): - return False - prefix = mkdtemp(prefix="frappe-assets-", suffix=frappe_head) assets_archive = download_file(url, prefix) @@ -114,8 +135,7 @@ def download_frappe_assets(): else: raise except Exception: - exc = True - click.secho("No Assets Found...Building...", fg="yellow") + assets_setup = False print(frappe.get_traceback()) finally: try: @@ -123,7 +143,7 @@ def download_frappe_assets(): except Exception: pass - return not exc + return assets_setup def symlink(target, link_name, overwrite=False): diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 1affeb5a5e..9f781d7475 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -29,7 +29,7 @@ def build(app=None, make_copy=False, restore=False, verbose=False, force=False): if not (force or app): # skip building frappe if assets exist remotely - skip_frappe = frappe.build.download_frappe_assets() + skip_frappe = frappe.build.download_frappe_assets(verbose=verbose) else: skip_frappe = False From 426a9464080bdcfda7001a2f4f6fa3ec11cb6a85 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 7 Sep 2020 16:58:20 +0530 Subject: [PATCH 093/154] fix(minor): file name cannot contain separator (#11426) --- frappe/core/doctype/file/file.py | 3 +++ frappe/model/document.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 316915e43a..a54df756dd 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -359,6 +359,9 @@ class File(Document): """write file to disk with a random name (to compare)""" file_path = get_files_path(is_private=self.is_private) + if os.path.sep in self.file_name: + frappe.throw(_('File name cannot have {0}').format(os.path.sep)) + # create directory (if not exists) frappe.create_folder(file_path) # write the file diff --git a/frappe/model/document.py b/frappe/model/document.py index 2b171547d1..53fcd99f78 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -905,9 +905,9 @@ class Document(BaseDocument): """Cancel the document. Sets `docstatus` = 2, then saves.""" self._cancel() - def delete(self): + def delete(self, ignore_permissions=False): """Delete document.""" - frappe.delete_doc(self.doctype, self.name, flags=self.flags) + frappe.delete_doc(self.doctype, self.name, ignore_permissions = ignore_permissions, flags=self.flags) def run_before_save_methods(self): """Run standard methods before `INSERT` or `UPDATE`. Standard Methods are: From 1c3bc937d98aa10434d65d63a55acbf4f938bed3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 7 Sep 2020 17:05:28 +0530 Subject: [PATCH 094/154] fix: Sider --- frappe/build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 242c83b15d..82075d3ee4 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -44,7 +44,7 @@ def build_missing_files(): for type in ["css", "js"]: current_asset_files.extend( [ - "{0}/{0}".format(type, name) + "{0}/{1}".format(type, name) for name in os.listdir(os.path.join(sites_path, "assets", type)) ] ) @@ -136,7 +136,6 @@ def download_frappe_assets(verbose=True): raise except Exception: assets_setup = False - print(frappe.get_traceback()) finally: try: shutil.rmtree(os.path.dirname(assets_archive)) From cb23da1ed96fff4b262881f7f3a676540643e34b Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Mon, 7 Sep 2020 18:34:42 +0530 Subject: [PATCH 095/154] chore(query-builder): Remove unused code --- frappe/desk/query_builder.py | 263 ----------------------------------- 1 file changed, 263 deletions(-) delete mode 100644 frappe/desk/query_builder.py diff --git a/frappe/desk/query_builder.py b/frappe/desk/query_builder.py deleted file mode 100644 index 81eba35e05..0000000000 --- a/frappe/desk/query_builder.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe - -out = frappe.response - -from frappe.utils import cint -import frappe.defaults -from six import text_type - -def get_sql_tables(q): - if q.find('WHERE') != -1: - tl = q.split('FROM')[1].split('WHERE')[0].split(',') - elif q.find('GROUP BY') != -1: - tl = q.split('FROM')[1].split('GROUP BY')[0].split(',') - else: - tl = q.split('FROM')[1].split('ORDER BY')[0].split(',') - return [t.strip().strip('`')[3:] for t in tl] - -def get_parent_dt(dt): - pdt = '' - if frappe.db.sql('select name from `tabDocType` where istable=1 and name=%s', dt): - import frappe.model.meta - return frappe.model.meta.get_parent_dt(dt) - return pdt - -def get_sql_meta(tl): - std_columns = { - 'owner':('Owner', '', '', '100'), - 'creation':('Created on', 'Date', '', '100'), - 'modified':('Last modified on', 'Date', '', '100'), - 'modified_by':('Modified By', '', '', '100') - } - - meta = {} - - for dt in tl: - meta[dt] = std_columns.copy() - - # for table doctype, the ID is the parent id - pdt = get_parent_dt(dt) - if pdt: - meta[dt]['parent'] = ('ID', 'Link', pdt, '200') - - # get the field properties from DocField - res = frappe.db.sql("select fieldname, label, fieldtype, options, width \ - from tabDocField where parent=%s", dt) - for r in res: - if r[0]: - meta[dt][r[0]] = (r[1], r[2], r[3], r[4]); - - # name - meta[dt]['name'] = ('ID', 'Link', dt, '200') - - return meta - -def add_match_conditions(q, tl): - from frappe.desk.reportview import build_match_conditions - sl = [] - for dt in tl: - s = build_match_conditions(dt) - if s: - sl.append(s) - - # insert the conditions - if sl: - condition_st = q.find('WHERE')!=-1 and ' AND ' or ' WHERE ' - condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT' - condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end - - if q.find('ORDER BY')!=-1 or q.find('LIMIT')!=-1 or q.find('GROUP BY')!=-1: # if query continues beyond conditions - q = q.split(condition_end) - q = q[0] + condition_st + '(' + ' OR '.join(sl) + ') ' + condition_end + q[1] - else: - q = q + condition_st + '(' + ' OR '.join(sl) + ')' - - return q - -def guess_type(m): - """ - Returns fieldtype depending on the MySQLdb Description - """ - if frappe.db.is_type_number(m): - return 'Currency' - elif m in frappe.is_type_datetime(m): - return 'Date' - else: - return 'Data' - -def build_description_simple(): - colnames, coltypes, coloptions, colwidths = [], [], [], [] - - for m in frappe.db.get_description(): - colnames.append(m[0]) - coltypes.append(guess_type[m[1]]) - coloptions.append('') - colwidths.append('100') - - return colnames, coltypes, coloptions, colwidths - -def build_description_standard(meta, tl): - - desc = frappe.db.get_description() - - colnames, coltypes, coloptions, colwidths = [], [], [], [] - - # merged metadata - used if we are unable to - # get both the table name and field name from - # the description - in case of joins - merged_meta = {} - for d in meta: - merged_meta.update(meta[d]) - - for f in desc: - fn, dt = f[0], '' - if '.' in fn: - dt, fn = fn.split('.') - - if (not dt) and merged_meta.get(fn): - # no "AS" given, find type from merged description - - desc = merged_meta[fn] - colnames.append(desc[0] or fn) - coltypes.append(desc[1] or '') - coloptions.append(desc[2] or '') - colwidths.append(desc[3] or '100') - - elif fn in meta.get(dt,{}): - # type specified for a multi-table join - # usually from Report Builder - - desc = meta[dt][fn] - colnames.append(desc[0] or fn) - coltypes.append(desc[1] or '') - coloptions.append(desc[2] or '') - colwidths.append(desc[3] or '100') - - else: - # nothing found - # guess - colnames.append(fn) - coltypes.append(guess_type(f[1])) - coloptions.append('') - colwidths.append('100') - - return colnames, coltypes, coloptions, colwidths - -@frappe.whitelist() -def runquery(q='', ret=0, from_export=0): - import frappe.utils - - formatted = cint(frappe.form_dict.get('formatted')) - - # CASE A: Simple Query - # -------------------- - if frappe.form_dict.get('simple_query') or frappe.form_dict.get('is_simple'): - if not q: q = frappe.form_dict.get('simple_query') or frappe.form_dict.get('query') - if q.split()[0].lower() != 'select': - raise Exception('Query must be a SELECT') - - as_dict = cint(frappe.form_dict.get('as_dict')) - res = frappe.db.sql(q, as_dict = as_dict, as_list = not as_dict, formatted=formatted) - - # build colnames etc from metadata - colnames, coltypes, coloptions, colwidths = [], [], [], [] - - # CASE B: Standard Query - # ----------------------- - else: - if not q: q = frappe.form_dict.get('query') - - tl = get_sql_tables(q) - meta = get_sql_meta(tl) - - q = add_match_conditions(q, tl) - - # replace special variables - q = q.replace('__user', frappe.session.user) - q = q.replace('__today', frappe.utils.nowdate()) - - res = frappe.db.sql(q, as_list=1, formatted=formatted) - - colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl) - - # run server script - # ----------------- - style, header_html, footer_html, page_template = '', '', '', '' - - out['colnames'] = colnames - out['coltypes'] = coltypes - out['coloptions'] = coloptions - out['colwidths'] = colwidths - out['header_html'] = header_html - out['footer_html'] = footer_html - out['page_template'] = page_template - - if style: - out['style'] = style - - # just the data - return - if ret==1: - return res - - out['values'] = res - - # return num of entries - qm = frappe.form_dict.get('query_max') or '' - if qm and qm.strip(): - if qm.split()[0].lower() != 'select': - raise Exception('Query (Max) must be a SELECT') - if not frappe.form_dict.get('simple_query'): - qm = add_match_conditions(qm, tl) - - out['n_values'] = frappe.utils.cint(frappe.db.sql(qm)[0][0]) - - -@frappe.whitelist() -def runquery_csv(): - global out - - q = frappe.form_dict.get('query') - - rep_name = frappe.form_dict.get('report_name') - if not frappe.form_dict.get('simple_query'): - - # Report Name - if not rep_name: - rep_name = get_sql_tables(q)[0] - - if not rep_name: rep_name = 'DataExport' - - rows = [[rep_name], out['colnames']] + out['values'] - - from six import StringIO - import csv - - f = StringIO() - writer = csv.writer(f) - for r in rows: - # encode only unicode type strings and not int, floats etc. - writer.writerow(map(lambda v: isinstance(v, text_type) and v.encode('utf-8') or v, r)) - - f.seek(0) - out['result'] = text_type(f.read(), 'utf-8') - out['type'] = 'csv' - out['doctype'] = rep_name - -def add_limit_to_query(query, args): - """ - Add limit condition to query - can be used by methods called in listing to add limit condition - """ - if args.get('limit_page_length'): - query += """ - limit %(limit_start)s, %(limit_page_length)s""" - - import frappe.utils - args['limit_start'] = frappe.utils.cint(args.get('limit_start')) - args['limit_page_length'] = frappe.utils.cint(args.get('limit_page_length')) - - return query, args From 555c52fc70e3f320603d782a95f4c840b474dc3f Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Mon, 7 Sep 2020 18:56:24 +0530 Subject: [PATCH 096/154] fix(db_query): Reject `user` argument in whitelisted methods --- frappe/desk/reportview.py | 1 + frappe/model/db_query.py | 1 + 2 files changed, 2 insertions(+) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6102be61ce..d4fc8833ae 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -36,6 +36,7 @@ def get_form_params(): data.pop('data', None) data.pop('ignore_permissions', None) data.pop('view', None) + data.pop('user', None) if "csrf_token" in data: del data["csrf_token"] diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index ac87b1d907..bea37f1ef1 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -769,6 +769,7 @@ def get_list(doctype, *args, **kwargs): kwargs.pop('ignore_permissions', None) kwargs.pop('data', None) kwargs.pop('strict', None) + kwargs.pop('user', None) # If doctype is child table if frappe.is_table(doctype): From 6ad3156233f9bc585995daa55eda4d5f0a4b1bef Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Mon, 7 Sep 2020 19:30:15 +0530 Subject: [PATCH 097/154] 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 2050c2b9b31f2684bb4fa89189075801de9c90ac Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Mon, 7 Sep 2020 19:53:41 +0530 Subject: [PATCH 098/154] fix(auto-repeat): Add permission check for rendering message preview --- frappe/automation/doctype/auto_repeat/auto_repeat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index c09e347e71..a9a358ce5f 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -403,6 +403,7 @@ def update_reference(docname, reference): @frappe.whitelist() def generate_message_preview(reference_dt, reference_doc, message=None, subject=None): + frappe.has_permission("Auto Repeat", "write", throw=True) doc = frappe.get_doc(reference_dt, reference_doc) subject_preview = _("Please add a subject to your email") msg_preview = frappe.render_template(message, {'doc': doc}) From 8261eded1966002e44c513834871ca14821a9f39 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 7 Sep 2020 18:51:24 +0530 Subject: [PATCH 099/154] 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 100/154] 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 101/154] 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 102/154] 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 103/154] 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 104/154] 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 1fa5ca0ef708e9beb816dbd23a35ae183011701b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 8 Sep 2020 15:29:09 +0530 Subject: [PATCH 105/154] fix: Use tarfile lib instead of tar UNIX tool style: Fix UX to show more outputs --- frappe/build.py | 17 ++++++++++++----- requirements.txt | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 82075d3ee4..4a8d88baf3 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -104,12 +104,13 @@ def handle_verbosity(): return verbosity -@handle_verbosity() + def download_frappe_assets(verbose=True): """Downloads and sets up Frappe assets if they exist based on the current commit HEAD. Returns True if correctly setup else returns False. """ + from simple_chalk import green from subprocess import getoutput from tempfile import mkdtemp @@ -122,14 +123,20 @@ def download_frappe_assets(verbose=True): click.secho("Retreiving Assets...", fg="yellow") prefix = mkdtemp(prefix="frappe-assets-", suffix=frappe_head) assets_archive = download_file(url, prefix) + print("{0} Downloaded assets archive from {1}".format(green('✔'), url)) if assets_archive: - import subprocess + import tarfile click.secho("Extracting Assets...", fg="yellow") - subprocess.check_output( - ["tar", "xf", assets_archive, "--strip", "3"], cwd=sites_path - ) + with tarfile.open(assets_archive) as tar: + for file in tar: + if not file.isdir(): + dest = "." + file.name.replace("./frappe-bench/sites", "") + show = dest.replace("./assets/", "") + tar.makefile(file, dest) + print("{0} Restored {1}".format(green('✔'), show)) + build_missing_files() return True else: diff --git a/requirements.txt b/requirements.txt index af3104cce7..77c80d5911 100644 --- a/requirements.txt +++ b/requirements.txt @@ -57,6 +57,7 @@ RestrictedPython==5.0 rq>=1.1.0 schedule==0.6.0 semantic-version==2.8.4 +simple-chalk==0.1.0 six==1.14.0 sqlparse==0.2.4 stripe==2.40.0 From 5cf3b2416c6e10766b3e39c38bc015947823d287 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 8 Sep 2020 15:30:09 +0530 Subject: [PATCH 106/154] chore: Drop dead code --- frappe/build.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 4a8d88baf3..2646d67a6c 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -89,22 +89,6 @@ def get_assets_link(frappe_head): return url -def handle_verbosity(): - import sys - import wrapt - - @wrapt.decorator - def verbosity(wrapped, instance, args, kwargs): - verbose = kwargs.get("verbose") or False - if not verbose: - sys.stdout = open(os.devnull, "wb") - ret = wrapped(*args, **kwargs) - sys.stdout = sys.__stdout__ - return ret - - return verbosity - - def download_frappe_assets(verbose=True): """Downloads and sets up Frappe assets if they exist based on the current commit HEAD. From 7f61fa68fceaedfc06658d8c4dfc2f94b16615cd Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 9 Sep 2020 11:27:19 +0530 Subject: [PATCH 107/154] fix: Consistent spacing and cases for messages --- frappe/build.py | 8 ++++---- rollup/build.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 2646d67a6c..b58b3ed3a2 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -60,7 +60,7 @@ def build_missing_files(): from subprocess import check_call from shlex import split - click.secho("Building Missing Assets...", fg="yellow") + click.secho("\nBuilding missing assets...\n", fg="yellow") command = split( "node rollup/build.js --files {0} --no-concat".format(",".join(missing_assets)) ) @@ -104,15 +104,15 @@ def download_frappe_assets(verbose=True): if frappe_head: try: url = get_assets_link(frappe_head) - click.secho("Retreiving Assets...", fg="yellow") + click.secho("Retreiving assets...", fg="yellow") prefix = mkdtemp(prefix="frappe-assets-", suffix=frappe_head) assets_archive = download_file(url, prefix) - print("{0} Downloaded assets archive from {1}".format(green('✔'), url)) + print("\n{0} Downloaded Frappe assets from {1}".format(green('✔'), url)) if assets_archive: import tarfile - click.secho("Extracting Assets...", fg="yellow") + click.secho("\nExtracting assets...\n", fg="yellow") with tarfile.open(assets_archive) as tar: for file in tar: if not file.isdir(): diff --git a/rollup/build.js b/rollup/build.js index 5fdfe80790..db9a032b9f 100644 --- a/rollup/build.js +++ b/rollup/build.js @@ -35,7 +35,7 @@ const files = exists("--files") ? value("--files").split(",") : false; const build_for_app = exists("--app") ? value("--app") : null; const concat = !exists("--no-concat"); -show_production_message(); +if (!files) show_production_message(); ensure_js_css_dirs(); if (concat) concatenate_files(); create_build_file(); From b6fc42724d8156e7d8c07a50964dd46e7aa74f79 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 9 Sep 2020 08:27:19 +0530 Subject: [PATCH 108/154] fix: Re-use build from options --- rollup/build.js | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/rollup/build.js b/rollup/build.js index db9a032b9f..3375b2d1cd 100644 --- a/rollup/build.js +++ b/rollup/build.js @@ -66,11 +66,7 @@ function build_assets_for_app(app) { return build_assets(app) } -function build_assets(app) { - const options = get_options_for(app); - if (!options.length) return Promise.resolve(); - log(chalk.yellow(`\nBuilding ${app} assets...\n`)); - +function build_from_(options) { const promises = options.map(({ inputOptions, outputOptions, output_file}) => { return build(inputOptions, outputOptions) .then(() => { @@ -86,26 +82,21 @@ function build_assets(app) { }); } +function build_assets(app) { + const options = get_options_for(app); + if (!options.length) return Promise.resolve(); + log(chalk.yellow(`\nBuilding ${app} assets...\n`)); + return build_from_(options); +} + function build_files(files, app="frappe") { + let ret; for (let file of files) { let options = get_options(file, app); if (!options.length) return Promise.resolve(); - log(chalk.yellow(`\nBuilding ${app} assets...\n`)); - - let promises = options.map(({ inputOptions, outputOptions, output_file}) => { - return build(inputOptions, outputOptions) - .then(() => { - log(`${chalk.green('✔')} Built ${output_file}`); - }); - }); - - let start = Date.now(); - return Promise.all(promises) - .then(() => { - let time = Date.now() - start; - log(chalk.green(`✨ Done in ${time / 1000}s`)); - }); + ret += build_from_(options); } + return ret; } function build(inputOptions, outputOptions) { From 770730489036e13033a76add4edf60b709197363 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 8 Sep 2020 17:08:57 +0530 Subject: [PATCH 109/154] 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 110/154] 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 111/154] 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 043e6804f46c419480538f81a30abaa19d054d03 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 8 Sep 2020 19:45:26 +0530 Subject: [PATCH 112/154] chore: Add error message if something goes wrong --- frappe/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/build.py b/frappe/build.py index b58b3ed3a2..767217a9b9 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -126,6 +126,8 @@ def download_frappe_assets(verbose=True): else: raise except Exception: + # TODO: log traceback in bench.log + click.secho("An Error occurred while downloading assets...", fg="red") assets_setup = False finally: try: From 662ccbb6468be5a1a07e32fcaf98a807bb142b97 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 8 Sep 2020 20:54:23 +0530 Subject: [PATCH 113/154] fix: total row in report print template --- .../js/frappe/views/reports/print_grid.html | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/print_grid.html b/frappe/public/js/frappe/views/reports/print_grid.html index 852c2925e8..e607f12b52 100644 --- a/frappe/public/js/frappe/views/reports/print_grid.html +++ b/frappe/public/js/frappe/views/reports/print_grid.html @@ -37,16 +37,20 @@ - {% format_data = row.is_total_row ? data[0] : row %} - {{ - col.formatter - ? col.formatter(row._index, col._index, value, col, format_data, true) - : col.format - ? col.format(value, row, col, format_data) - : col.docfield - ? frappe.format(value, col.docfield) - : value - }} + {% format_data = row.is_total_row && ["Currency", "Float"].includes(col.fieldtype) ? data[0] : row %} + {% if (row.is_total_row && col._index == 0) { %} + {{ __("Total") }} + {% } else { %} + {{ + col.formatter + ? col.formatter(row._index, col._index, value, col, format_data, true) + : col.format + ? col.format(value, row, col, format_data) + : col.docfield + ? frappe.format(value, col.docfield) + : value + }} + {% } %} {% endif %} @@ -55,4 +59,3 @@ {% endfor %} - From 3b5c4043781f6bd8fbfd9f5a281dc7a009af6310 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Sep 2020 20:55:53 +0530 Subject: [PATCH 114/154] 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 115/154] 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 116/154] 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 117/154] 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 118/154] 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 119/154] 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 120/154] 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 121/154] 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 122/154] 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 123/154] 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 124/154] 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 8feba5fa9b084d93158bc23ff14d2f52cfa571d1 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 9 Sep 2020 20:52:45 +0530 Subject: [PATCH 125/154] fix(pdf): Cleanup temporary files after pdf generation This was removed in https://github.com/frappe/frappe/pull/7358 --- frappe/utils/pdf.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 51febb5f72..7699cbc522 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -52,6 +52,8 @@ def get_pdf(html, options=None, output=None): output.appendPagesFromReader(reader) else: raise + finally: + cleanup(options) if "password" in options: password = options["password"] @@ -184,10 +186,7 @@ def prepare_header_footer(soup): return options -def cleanup(fname, options): - if os.path.exists(fname): - os.remove(fname) - +def cleanup(options): for key in ("header-html", "footer-html"): if options.get(key) and os.path.exists(options[key]): os.remove(options[key]) From 3c9f2650910124dc87883c17511fe86d9b3ea6cb Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 9 Sep 2020 20:58:42 +0530 Subject: [PATCH 126/154] fix(pdf): Restrict cookies to the host domain Use wkhtmltopdf's cookie-jar options to set domain Reference: https://github.com/wkhtmltopdf/wkhtmltopdf/blob/7952b0f3e754588a3fcdaf5000a4cd99448292de/src/lib/multipageloader.cc#L532-L536 Cookie format Reference: https://doc.qt.io/qt-5/qnetworkcookie.html#parseCookies Also, werkzeug 0.16.x shows port in the host attribute. https://werkzeug.palletsprojects.com/en/0.16.x/wrappers/#werkzeug.wrappers.BaseRequest.host --- frappe/utils/pdf.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 7699cbc522..ae5fc2b334 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -112,8 +112,7 @@ def prepare_options(html, options): options.update(html_options or {}) # cookies - if frappe.session and frappe.session.sid: - options['cookie'] = [('sid', '{0}'.format(frappe.session.sid))] + options.update(get_cookie_options()) # page size if not options.get("page-size"): @@ -122,6 +121,22 @@ def prepare_options(html, options): return html, options +def get_cookie_options(): + options = {} + if frappe.session and frappe.session.sid: + # Use wkhtmltopdf's cookie-jar feature to set cookies and restrict them to host domain + cookiejar = "/tmp/{}.jar".format(frappe.generate_hash()) + + # Remove port from request.host + # https://werkzeug.palletsprojects.com/en/0.16.x/wrappers/#werkzeug.wrappers.BaseRequest.host + domain = frappe.local.request.host.split(":", 1)[0] + with open(cookiejar, "w") as f: + f.write("sid={}; Domain={};\n".format(frappe.session.sid, domain)) + + options['cookie-jar'] = cookiejar + + return options + def read_options_from_html(html): options = {} soup = BeautifulSoup(html, "html5lib") @@ -187,7 +202,7 @@ def prepare_header_footer(soup): def cleanup(options): - for key in ("header-html", "footer-html"): + for key in ("header-html", "footer-html", "cookie-jar"): if options.get(key) and os.path.exists(options[key]): os.remove(options[key]) From 40748d7fa88c18f64600081b986d95f3b5cf0721 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 9 Sep 2020 23:42:46 +0530 Subject: [PATCH 127/154] 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 d8377dccc22b0ba83b6cbfec1a4e94dfdaa8ad77 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 10 Sep 2020 11:10:45 +0530 Subject: [PATCH 128/154] chore(tests): Cleanup property setter added for Blog Post --- frappe/tests/test_form_load.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py index 459a3f52bd..78562e1055 100644 --- a/frappe/tests/test_form_load.py +++ b/frappe/tests/test_form_load.py @@ -40,7 +40,7 @@ class TestFormLoad(unittest.TestCase): user.remove_roles(*user_roles) user.add_roles('Blogger') - make_property_setter('Blog Post', 'published', 'permlevel', 1, 'Int') + blog_post_property_setter = make_property_setter('Blog Post', 'published', 'permlevel', 1, 'Int') reset('Blog Post') add('Blog Post', 'Website Manager', 1) update('Blog Post', 'Website Manager', 1, 'write', 1) @@ -80,6 +80,7 @@ class TestFormLoad(unittest.TestCase): user.add_roles(*user_roles) blog_doc.delete() + frappe.delete_doc(blog_post_property_setter.doctype, blog_post_property_setter.name) def test_fieldlevel_permissions_in_load_for_child_table(self): contact = frappe.new_doc('Contact') From 2b413319c1244d814fdd7d606dc3a1b3cec6f5f3 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 10 Sep 2020 12:36:59 +0530 Subject: [PATCH 129/154] 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 From a429729e928c57503c7c00b66d01c9a44acc53cf Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 10 Sep 2020 12:57:34 +0530 Subject: [PATCH 130/154] fix: handle undefined tree filter value --- frappe/model/db_query.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index ac87b1d907..3db3ffd936 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -391,7 +391,10 @@ class DatabaseQuery(object): ref_doctype = frappe.get_meta(f.doctype).get_field(f.fieldname).options result=[] - lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) + + lft, rgt = '', '' + if f.value: + lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) # Get descendants elements of a DocType with a tree structure if f.operator.lower() in ('descendants of', 'not descendants of') : From f4a22683d5c9703fe8d2767abd2c53301c0074c9 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 10 Sep 2020 12:58:52 +0530 Subject: [PATCH 131/154] test(blog): Add tests for blog post pagination --- .../doctype/blog_post/test_blog_post.py | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 7c6e64dc5a..cdf53122b2 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -9,6 +9,8 @@ import re from frappe.utils import set_request from frappe.website.render import render from frappe.utils import random_string +from frappe.website.doctype.blog_post.blog_post import get_blog_list +from frappe.website.website_generator import WebsiteGenerator class TestBlogPost(unittest.TestCase): def test_generator_view(self): @@ -60,12 +62,36 @@ class TestBlogPost(unittest.TestCase): frappe.delete_doc("Blog Post", blog.name) frappe.delete_doc("Blog Category", blog.blog_category) -def make_test_blog(): - if not frappe.db.exists('Blog Category', 'test-blog-category'): - # Set different title and name for the category + def test_blog_pagination(self): + # Create some Blog Posts for a Blog Category + category_title, blogs, BLOG_COUNT = "List Category", [], 4 + + for index in range(BLOG_COUNT): + blog = make_test_blog(category_title) + blogs.append(blog) + + filters = frappe._dict({"blog_category": scrub(category_title)}) + # Assert that get_blog_list returns results as expected + + self.assertEqual(len(get_blog_list(None, None, filters, 0, 3)), 3) + self.assertEqual(len(get_blog_list(None, None, filters, 0, BLOG_COUNT)), BLOG_COUNT) + self.assertEqual(len(get_blog_list(None, None, filters, 0, 2)), 2) + self.assertEqual(len(get_blog_list(None, None, filters, 2, BLOG_COUNT)), 2) + + # Cleanup Blog Post and linked Blog Category + for blog in blogs: + frappe.delete_doc(blog.doctype, blog.name) + frappe.delete_doc("Blog Category", blogs[0].blog_category) + +def scrub(text): + return WebsiteGenerator.scrub(None, text) + +def make_test_blog(category_title="Test Blog Category"): + category_name = scrub(category_title) + if not frappe.db.exists('Blog Category', category_name): frappe.get_doc(dict( doctype = 'Blog Category', - title='Test Blog Category')).insert() + title=category_title)).insert() if not frappe.db.exists('Blogger', 'test-blogger'): frappe.get_doc(dict( doctype = 'Blogger', @@ -73,7 +99,7 @@ def make_test_blog(): full_name='Test Blogger')).insert() test_blog = frappe.get_doc(dict( doctype = 'Blog Post', - blog_category = 'test-blog-category', + blog_category = category_name, blogger = 'test-blogger', title = random_string(20), route = random_string(20), From 40c29158e5a9bbd76d50010e7fb2cbdf9d281f46 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 10 Sep 2020 13:03:06 +0530 Subject: [PATCH 132/154] fix(blog): Use `LIMIT row_count OFFSET offset` syntax instead of `LIMIT offset, row_count` Reference: https://www.postgresql.org/docs/9.5/queries-limit.html --- frappe/website/doctype/blog_post/blog_post.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index beffcdca25..79d0754af4 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -242,7 +242,7 @@ def get_blog_list(doctype, txt=None, filters=None, limit_start=0, limit_page_len and t1.blogger = t2.name %(condition)s order by featured desc, published_on desc, name asc - limit %(start)s, %(page_len)s""" % { + limit %(page_len)s OFFSET %(start)s""" % { "start": limit_start, "page_len": limit_page_length, "condition": (" and " + " and ".join(conditions)) if conditions else "" } From 19309936759a534eda73cf8f7415bc23603b0a85 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 10 Sep 2020 14:49:31 +0200 Subject: [PATCH 133/154] test: login-css is no longer in login.html --- frappe/tests/test_website.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index c708f670c1..c5da2bdfb7 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -9,6 +9,7 @@ from frappe.utils import set_request class TestWebsite(unittest.TestCase): + def test_home_page_for_role(self): frappe.delete_doc_if_exists('User', 'test-user-for-home-page@example.com') frappe.delete_doc_if_exists('Role', 'home-page-test') @@ -42,8 +43,6 @@ class TestWebsite(unittest.TestCase): frappe.cache().hdel('home_page', frappe.session.user) self.assertEqual(get_home_page(), 'test-portal-home') - - def test_page_load(self): frappe.set_user('Guest') set_request(method='POST', path='login') @@ -53,7 +52,6 @@ class TestWebsite(unittest.TestCase): html = frappe.safe_decode(response.get_data()) - self.assertTrue('/* login-css */' in html) self.assertTrue('// login.js' in html) self.assertTrue('' in html) frappe.set_user('Administrator') From be36c532b742244da8a622c4531eacd45bb0e801 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 8 Sep 2020 21:03:25 +0530 Subject: [PATCH 134/154] fix: replace cr and lf characters with empty string in case of exception (cherry picked from commit 6310494aa9afb67058b5d0b8d0f4cc8e382357e2) --- frappe/email/email_body.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 001ba59e95..5bb654abf3 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -207,7 +207,7 @@ class EMail: def set_in_reply_to(self, in_reply_to): """Used to send the Message-Id of a received email back as In-Reply-To""" - self.msg_root["In-Reply-To"] = in_reply_to.replace("\r", "").replace("\n", "") + self.set_header('In-Reply-To', in_reply_to) def make(self): """build into msg_root""" @@ -234,7 +234,10 @@ class EMail: if key in self.msg_root: del self.msg_root[key] - self.msg_root[key] = value + try: + self.msg_root[key] = value + except ValueError: + self.msg_root[key] = sanitize_email_header(value) def as_string(self): """validate, build message and convert to string""" @@ -458,3 +461,6 @@ def get_header(header=None): }) return email_header + +def sanitize_email_header(str): + return str.replace('\r', '').replace('\n', '') From ae31f08f429b432f881327c4781b4e812ea29261 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 14 Sep 2020 12:19:06 +0530 Subject: [PATCH 135/154] feat(user): Add hourly limit for password reset requests (#11371) Co-authored-by: Chinmay D. Pai Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../system_settings/system_settings.json | 8 ++++++ frappe/core/doctype/user/test_user.py | 14 ++++++++++ frappe/core/doctype/user/user.py | 26 ++++++++++++++++--- frappe/hooks.py | 3 ++- frappe/patches.txt | 1 + .../v12_0/set_default_password_reset_limit.py | 9 +++++++ frappe/utils/password.py | 18 +++++++++++++ 7 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 frappe/patches/v12_0/set_default_password_reset_limit.py diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index 819bb89e72..17f97b3e1a 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -40,6 +40,7 @@ "password_settings", "logout_on_password_reset", "force_user_to_reset_password", + "password_reset_limit", "column_break_31", "enable_password_policy", "minimum_password_score", @@ -415,6 +416,13 @@ "fieldtype": "Int", "label": "Run Jobs only Daily if Inactive For (Days)" }, + { + "default": "3", + "description": "Hourly rate limit for generating password reset links", + "fieldname": "password_reset_limit", + "fieldtype": "Int", + "label": "Password Reset Link Generation Limit" + }, { "default": "1", "fieldname": "logout_on_password_reset", diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index d4c0fa98ed..fb1fa4aff9 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -19,6 +19,7 @@ class TestUser(unittest.TestCase): # disable password strength test frappe.db.set_value("System Settings", "System Settings", "enable_password_policy", 0) frappe.db.set_value("System Settings", "System Settings", "minimum_password_score", "") + frappe.db.set_value("System Settings", "System Settings", "password_reset_limit", 3) def test_user_type(self): new_user = frappe.get_doc(dict(doctype='User', email='test-for-type@example.com', @@ -222,6 +223,19 @@ class TestUser(unittest.TestCase): self.assertEqual(extract_mentions(comment)[0], "test_user@example.com") self.assertEqual(extract_mentions(comment)[1], "test.again@example1.com") + def test_rate_limiting_for_reset_password(self): + from frappe.utils.password import delete_password_reset_cache + delete_password_reset_cache() + + frappe.db.set_value("System Settings", "System Settings", "password_reset_limit", 1) + + user = frappe.get_doc("User", "testperm@example.com") + link = user.reset_password() + self.assertRegex(link, "\/update-password\?key=[A-Za-z0-9]*") + + self.assertRaises(frappe.ValidationError, user.reset_password, False) + + def delete_contact(user): frappe.db.sql("DELETE FROM `tabContact` WHERE `email_id`= %s", user) frappe.db.sql("DELETE FROM `tabContact Email` WHERE `email_id`= %s", user) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index ef2de679ae..2c5865fb69 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -13,15 +13,16 @@ from frappe.utils.user import get_system_managers from bs4 import BeautifulSoup import frappe.permissions import frappe.share -import re -import json from frappe.website.utils import is_signup_enabled from frappe.utils.background_jobs import enqueue STANDARD_USERS = ("Guest", "Administrator") -class MaxUsersReachedError(frappe.ValidationError): pass + +class MaxUsersReachedError(frappe.ValidationError): + pass + class User(Document): __new_password = None @@ -225,6 +226,11 @@ class User(Document): def reset_password(self, send_email=False, password_expired=False): from frappe.utils import random_string, get_url + rate_limit = frappe.db.get_single_value("System Settings", "password_reset_limit") + + if rate_limit: + check_password_reset_limit(self.name, rate_limit) + key = random_string(32) self.db_set("reset_password_key", key) @@ -236,6 +242,7 @@ class User(Document): if send_email: self.password_reset_mail(link) + update_password_reset_limit(self.name) return link def get_other_system_managers(self): @@ -1110,3 +1117,16 @@ def generate_keys(user): return {"api_secret": api_secret} frappe.throw(frappe._("Not Permitted"), frappe.PermissionError) + +def update_password_reset_limit(user): + generated_link_count = get_generated_link_count(user) + generated_link_count += 1 + frappe.cache().hset("password_reset_link_count", user, generated_link_count) + +def check_password_reset_limit(user, rate_limit): + generated_link_count = get_generated_link_count(user) + if generated_link_count >= rate_limit: + frappe.throw(_("You have reached the hourly limit for generating password reset links. Please try again later.")) + +def get_generated_link_count(user): + return cint(frappe.cache().hget("password_reset_link_count", user)) or 0 \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index 7ecc199814..9e2009ef6c 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -196,7 +196,8 @@ scheduler_events = { "frappe.deferred_insert.save_to_db", "frappe.desk.form.document_follow.send_hourly_updates", "frappe.integrations.doctype.google_calendar.google_calendar.sync", - "frappe.email.doctype.newsletter.newsletter.send_scheduled_email" + "frappe.email.doctype.newsletter.newsletter.send_scheduled_email", + "frappe.utils.password.delete_password_reset_cache" ], "daily": [ "frappe.email.queue.clear_outbox", diff --git a/frappe/patches.txt b/frappe/patches.txt index 0c871664a3..b2dc3fa391 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -305,6 +305,7 @@ frappe.patches.v12_0.fix_email_id_formatting frappe.patches.v13_0.add_toggle_width_in_navbar_settings frappe.patches.v13_0.rename_notification_fields frappe.patches.v13_0.remove_duplicate_navbar_items +frappe.patches.v12_0.set_default_password_reset_limit frappe.patches.v13_0.set_route_for_blog_category frappe.patches.v13_0.enable_custom_script frappe.patches.v13_0.update_newsletter_content_type diff --git a/frappe/patches/v12_0/set_default_password_reset_limit.py b/frappe/patches/v12_0/set_default_password_reset_limit.py new file mode 100644 index 0000000000..188f2383e7 --- /dev/null +++ b/frappe/patches/v12_0/set_default_password_reset_limit.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe + + +def execute(): + frappe.reload_doc("core", "doctype", "system_settings", force=1) + frappe.db.set_value('System Settings', None, "password_reset_limit", 3) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index b1461ce7fe..177a3118fb 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -50,6 +50,7 @@ def get_decrypted_password(doctype, name, fieldname='password', raise_exception= elif raise_exception: frappe.throw(_('Password not found'), frappe.AuthenticationError) + def set_encrypted_password(doctype, name, pwd, fieldname='password'): try: frappe.db.sql("""insert into `__Auth` (doctype, name, fieldname, `password`, encrypted) @@ -63,6 +64,7 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): frappe.throw("Most probably your password is too long.", exc=e) raise e + def check_password(user, pwd, doctype='User', fieldname='password'): '''Checks if user and password are correct, else raises frappe.AuthenticationError''' @@ -82,11 +84,20 @@ def check_password(user, pwd, doctype='User', fieldname='password'): return user + def delete_login_failed_cache(user): frappe.cache().hdel('last_login_tried', user) frappe.cache().hdel('login_failed_count', user) frappe.cache().hdel('locked_account_time', user) + +def delete_password_reset_cache(user=None): + if user: + frappe.cache().hdel('password_reset_link_count', user) + else: + frappe.cache().delete_key('password_reset_link_count') + + def update_password(user, pwd, doctype='User', fieldname='password', logout_all_sessions=False): ''' Update the password for the User @@ -115,6 +126,7 @@ def update_password(user, pwd, doctype='User', fieldname='password', logout_all_ from frappe.sessions import clear_sessions clear_sessions(user=user, keep_current=True, force=True) + def delete_all_passwords_for(doctype, name): try: frappe.db.sql("""delete from `__Auth` where `doctype`=%(doctype)s and `name`=%(name)s""", @@ -123,26 +135,31 @@ def delete_all_passwords_for(doctype, name): if not frappe.db.is_missing_column(e): raise + def rename_password(doctype, old_name, new_name): # NOTE: fieldname is not considered, since the document is renamed frappe.db.sql("""update `__Auth` set name=%(new_name)s where doctype=%(doctype)s and name=%(old_name)s""", { 'doctype': doctype, 'new_name': new_name, 'old_name': old_name }) + def rename_password_field(doctype, old_fieldname, new_fieldname): frappe.db.sql('''update `__Auth` set fieldname=%(new_fieldname)s where doctype=%(doctype)s and fieldname=%(old_fieldname)s''', { 'doctype': doctype, 'old_fieldname': old_fieldname, 'new_fieldname': new_fieldname }) + def create_auth_table(): # same as Framework.sql frappe.db.create_auth_table() + def encrypt(pwd): cipher_suite = Fernet(encode(get_encryption_key())) cipher_text = cstr(cipher_suite.encrypt(encode(pwd))) return cipher_text + def decrypt(pwd): try: cipher_suite = Fernet(encode(get_encryption_key())) @@ -152,6 +169,7 @@ def decrypt(pwd): # encryption_key in site_config is changed and not valid frappe.throw(_('Encryption key is invalid, Please check site_config.json')) + def get_encryption_key(): from frappe.installer import update_site_config From a0086db9b6054c7a5f511b194f80668de2332644 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 14 Sep 2020 12:30:30 +0530 Subject: [PATCH 136/154] fix(frappe client): validate http method for frappe.client api (#11228) --- frappe/__init__.py | 13 +++++++++++-- frappe/client.py | 24 ++++++++++++------------ frappe/handler.py | 12 ++++++++---- frappe/tests/test_client.py | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 4b60181bd1..e1fe790b45 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -514,12 +514,15 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message whitelisted = [] guest_methods = [] xss_safe_methods = [] -def whitelist(allow_guest=False, xss_safe=False): +allowed_http_methods_for_whitelisted_func = {} + +def whitelist(allow_guest=False, xss_safe=False, methods=None): """ Decorator for whitelisting a function and making it accessible via HTTP. Standard request will be `/api/method/[path.to.method]` :param allow_guest: Allow non logged-in user to access this method. + :param methods: Allowed http method to access the method. Use as: @@ -527,10 +530,16 @@ def whitelist(allow_guest=False, xss_safe=False): def myfunc(param1, param2): pass """ + + if not methods: + methods = ['GET', 'POST', 'PUT', 'DELETE'] + def innerfn(fn): - global whitelisted, guest_methods, xss_safe_methods + global whitelisted, guest_methods, xss_safe_methods, allowed_http_methods_for_whitelisted_func whitelisted.append(fn) + allowed_http_methods_for_whitelisted_func[fn] = methods + if allow_guest: guest_methods.append(fn) diff --git a/frappe/client.py b/frappe/client.py index 0db18421ef..2217b53673 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -107,7 +107,7 @@ def get_single_value(doctype, field): value = frappe.db.get_single_value(doctype, field) return value -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def set_value(doctype, name, fieldname, value=None): '''Set a value using get_doc, group of values @@ -142,7 +142,7 @@ def set_value(doctype, name, fieldname, value=None): return doc.as_dict() -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def insert(doc=None): '''Insert a document @@ -160,7 +160,7 @@ def insert(doc=None): doc = frappe.get_doc(doc).insert() return doc.as_dict() -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def insert_many(docs=None): '''Insert multiple documents @@ -186,7 +186,7 @@ def insert_many(docs=None): return out -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def save(doc): '''Update (save) an existing document @@ -199,7 +199,7 @@ def save(doc): return doc.as_dict() -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def rename_doc(doctype, old_name, new_name, merge=False): '''Rename document @@ -209,7 +209,7 @@ def rename_doc(doctype, old_name, new_name, merge=False): new_name = frappe.rename_doc(doctype, old_name, new_name, merge=merge) return new_name -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def submit(doc): '''Submit a document @@ -222,7 +222,7 @@ def submit(doc): return doc.as_dict() -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def cancel(doctype, name): '''Cancel a document @@ -233,7 +233,7 @@ def cancel(doctype, name): return wrapper.as_dict() -@frappe.whitelist() +@frappe.whitelist(methods=['DELETE', 'POST']) def delete(doctype, name): '''Delete a remote document @@ -241,13 +241,13 @@ def delete(doctype, name): :param name: name of the document to be deleted''' frappe.delete_doc(doctype, name, ignore_missing=False) -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def set_default(key, value, parent=None): """set a user default value""" frappe.db.set_default(key, value, parent or frappe.session.user) frappe.clear_cache(user=frappe.session.user) -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def make_width_property_setter(doc): '''Set width Property Setter @@ -257,7 +257,7 @@ def make_width_property_setter(doc): if doc["doctype"]=="Property Setter" and doc["property"]=="width": frappe.get_doc(doc).insert(ignore_permissions = True) -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def bulk_update(docs): '''Bulk update documents @@ -333,7 +333,7 @@ def get_time_zone(): '''Returns default time zone''' return {"time_zone": frappe.defaults.get_defaults().get("time_zone")} -@frappe.whitelist() +@frappe.whitelist(methods=['POST', 'PUT']) def attach_file(filename=None, filedata=None, doctype=None, docname=None, folder=None, decode_base64=False, is_private=None, docfield=None): '''Attach a file to Document (POST) diff --git a/frappe/handler.py b/frappe/handler.py index e5a7f742ae..cac9c3a460 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -65,16 +65,21 @@ def execute_cmd(cmd, from_async=False): method = method.queue is_whitelisted(method) + is_valid_http_method(method) return frappe.call(method, **frappe.form_dict) +def is_valid_http_method(method): + http_method = frappe.local.request.method + + if http_method not in frappe.allowed_http_methods_for_whitelisted_func[method]: + frappe.throw(_("Not permitted"), frappe.PermissionError) def is_whitelisted(method): # check if whitelisted if frappe.session['user'] == 'Guest': if (method not in frappe.guest_methods): - frappe.msgprint(_("Not permitted")) - raise frappe.PermissionError('Not Allowed, {0}'.format(method)) + frappe.throw(_("Not permitted"), frappe.PermissionError) if method not in frappe.xss_safe_methods: # strictly sanitize form_dict @@ -85,8 +90,7 @@ def is_whitelisted(method): else: if not method in frappe.whitelisted: - frappe.msgprint(_("Not permitted")) - raise frappe.PermissionError('Not Allowed, {0}'.format(method)) + frappe.throw(_("Not permitted"), frappe.PermissionError) @frappe.whitelist(allow_guest=True) def version(): diff --git a/frappe/tests/test_client.py b/frappe/tests/test_client.py index 8371849935..89460203f6 100644 --- a/frappe/tests/test_client.py +++ b/frappe/tests/test_client.py @@ -21,3 +21,37 @@ class TestClient(unittest.TestCase): self.assertFalse(frappe.db.exists("ToDo", todo.name)) self.assertRaises(frappe.DoesNotExistError, delete, "ToDo", todo.name) + + def test_http_valid_method_access(self): + from frappe.client import delete + from frappe.handler import execute_cmd + + frappe.set_user("Administrator") + + frappe.local.request = frappe._dict() + frappe.local.request.method = 'POST' + + frappe.local.form_dict = frappe._dict({ + 'doc': dict(doctype='ToDo', description='Valid http method'), + 'cmd': 'frappe.client.save' + }) + todo = execute_cmd('frappe.client.save') + + self.assertEqual(todo.get('description'), 'Valid http method') + + delete("ToDo", todo.name) + + def test_http_invalid_method_access(self): + from frappe.handler import execute_cmd + + frappe.set_user("Administrator") + + frappe.local.request = frappe._dict() + frappe.local.request.method = 'GET' + + frappe.local.form_dict = frappe._dict({ + 'doc': dict(doctype='ToDo', description='Invalid http method'), + 'cmd': 'frappe.client.save' + }) + + self.assertRaises(frappe.PermissionError, execute_cmd, 'frappe.client.save') From c615f637319fe7da7ebf5903d4cc91368c514505 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 14 Sep 2020 14:59:29 +0530 Subject: [PATCH 137/154] fix: Remove print statement --- frappe/email/doctype/notification/notification.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index aa0fa01d25..9d8fb49ee9 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -73,7 +73,6 @@ def get_context(context): def validate_twilio_settings(self): if self.enabled and self.channel == "WhatsApp" \ and not frappe.db.get_single_value("Twilio Settings", "enabled"): - print(frappe.db.get_single_value("Twilio Settings", "enabled")) frappe.throw(_("Please enable Twilio settings to send WhatsApp messages")) def validate_condition(self): @@ -432,4 +431,4 @@ def get_assignees(doc): recipients = [d.owner for d in assignees] - return recipients \ No newline at end of file + return recipients From 6c5d4cbb22bc2d8eb9d0b7d3541affd7f9effa18 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 14 Sep 2020 15:18:59 +0530 Subject: [PATCH 138/154] fix: Better implementation for refresh on toggle sidebar --- frappe/public/js/frappe/views/reports/report_view.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index aec24d9d13..05e8e75eeb 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -48,10 +48,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { setup_view() { this.setup_columns(); super.setup_new_doc_event(); + } + + toggle_side_bar() { + super.toggle_side_bar(); // refresh datatable when sidebar is toggled to accomodate extra space - $(document.body) - .off('toggleListSidebar.report_view') - .on('toggleListSidebar.report_view', () => this.render(true)); + this.render(true); } setup_result_area() { From 984b46d1028b87f4db9ce970d14b1137fa92bba8 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 14 Sep 2020 16:39:03 +0530 Subject: [PATCH 139/154] fix: TypeError occuring due to modification of modified function --- frappe/public/js/frappe/form/grid_row.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 733c1bea5f..827fbfdee6 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -393,11 +393,16 @@ export default class GridRow { // sync get_query field.get_query = this.grid.get_field(df.fieldname).get_query; - var field_on_change_function = field.df.onchange; - field.df.onchange = function(e) { - field_on_change_function && field_on_change_function(e); - me.grid.grid_rows[this.doc.idx - 1].refresh_field(field.df.fieldname); - }; + if (!field.df.onchange_modified) { + var field_on_change_function = field.df.onchange; + field.df.onchange = function(e) { + field_on_change_function && field_on_change_function(e); + me.grid.grid_rows[this.doc.idx - 1].refresh_field(this.df.fieldname); + }; + + field.df.onchange_modified = true; + } + field.refresh(); if(field.$input) { field.$input From 55733b9b3876ece13dd180fd922f2a3860f7ce4f Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 14 Sep 2020 21:00:10 +0530 Subject: [PATCH 140/154] fix: Early skip fetching Frappe Assets if running in CI --- frappe/commands/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 9f781d7475..b7c85268ae 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -27,7 +27,8 @@ def build(app=None, make_copy=False, restore=False, verbose=False, force=False): # don't minify in developer_mode for faster builds no_compress = frappe.local.conf.developer_mode or False - if not (force or app): + # dont try downloading assets if force used, app specified or running via CI + if not (force or app or os.environ.get('CI')): # skip building frappe if assets exist remotely skip_frappe = frappe.build.download_frappe_assets(verbose=verbose) else: From 5a92195b753e606ca4b78cb096e81990f286f116 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 15 Sep 2020 12:28:25 +0530 Subject: [PATCH 141/154] fix: Clear sitemap cache along with website cache --- frappe/website/render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/render.py b/frappe/website/render.py index a5842f0694..04876786e1 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -309,7 +309,7 @@ def clear_cache(path=None): :param path: (optional) for the given path''' for key in ('website_generator_routes', 'website_pages', - 'website_full_index'): + 'website_full_index', 'sitemap_routes'): frappe.cache().delete_value(key) frappe.cache().delete_value("website_404") From 26e132b5b6818962f0bf10221b23299883c5de7f Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 15 Sep 2020 13:16:50 +0530 Subject: [PATCH 142/154] Revert "fix: Change assets endpoint" This reverts commit 8a9545afdb457f177740cc825f393369d4660977. --- .github/workflows/publish-assets-develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index 522cfc815c..8546011ebe 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -38,6 +38,6 @@ jobs: AWS_S3_BUCKET: 'frappe-assets' AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} - AWS_S3_ENDPOINT: 'http://assets.frappeframework.com' + AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' AWS_REGION: 'fr-par' SOURCE_DIR: '$GITHUB_WORKSPACE/build' From 0ac124f0d6c5607a576f1d9fba758741ec4db0d9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 15 Sep 2020 13:17:53 +0530 Subject: [PATCH 143/154] fix: Updated bucket name --- .github/workflows/publish-assets-develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index 8546011ebe..ee633ef039 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -35,7 +35,7 @@ jobs: with: args: --acl public-read env: - AWS_S3_BUCKET: 'frappe-assets' + AWS_S3_BUCKET: 'assets.frappeframework.com' AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' From 772153da116f48a96f6112edc1cc7b3bfd34d54f Mon Sep 17 00:00:00 2001 From: Afshan Date: Tue, 15 Sep 2020 16:20:58 +0530 Subject: [PATCH 144/154] fix: setting "self.setting site_config_backup_path" --- frappe/utils/backups.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index b8184886f9..4f5916d617 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -80,6 +80,11 @@ class BackupGenerator: last_db, last_file, last_private_file, site_config_backup_path = self.get_recent_backup(older_than) else: last_db, last_file, last_private_file, site_config_backup_path = False, False, False, False + print(last_db) + print(last_file) + print(last_private_file) + print(site_config_backup_path) + self.todays_date = now_datetime().strftime('%Y%m%d_%H%M%S') @@ -93,6 +98,7 @@ class BackupGenerator: self.zip_files() else: + print('inside') self.backup_path_files = last_file self.backup_path_db = last_db self.backup_path_private_files = last_private_file @@ -176,6 +182,8 @@ class BackupGenerator: with open(site_config_backup_path, "w") as n, open(site_config_path) as c: n.write(c.read()) + self.site_config_backup_path = site_config_backup_path + def take_dump(self): import frappe.utils From 5377d65cad9e2b778cd6238a38165e1ac880f655 Mon Sep 17 00:00:00 2001 From: Afshan Date: Tue, 15 Sep 2020 16:40:24 +0530 Subject: [PATCH 145/154] fix: removed print statements --- frappe/utils/backups.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 4f5916d617..3277ef4c47 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -80,11 +80,6 @@ class BackupGenerator: last_db, last_file, last_private_file, site_config_backup_path = self.get_recent_backup(older_than) else: last_db, last_file, last_private_file, site_config_backup_path = False, False, False, False - print(last_db) - print(last_file) - print(last_private_file) - print(site_config_backup_path) - self.todays_date = now_datetime().strftime('%Y%m%d_%H%M%S') @@ -98,7 +93,6 @@ class BackupGenerator: self.zip_files() else: - print('inside') self.backup_path_files = last_file self.backup_path_db = last_db self.backup_path_private_files = last_private_file From bbdb05378b326d3112d3e7e6fddea33333df7ed7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 15 Sep 2020 17:30:26 +0530 Subject: [PATCH 146/154] fix: Deprecate site_config_backup_path in favour of backup_path_conf --- frappe/utils/backups.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 3277ef4c47..7f06a26ee0 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -68,6 +68,12 @@ class BackupGenerator: dir = os.path.dirname(file_path) os.makedirs(dir, exist_ok=True) + @property + def site_config_backup_path(self): + # For backwards compatibility + import click + click.secho("BackupGenerator.site_config_backup_path has been deprecated in favour of BackupGenerator.backup_path_conf", fg="yellow") + return getattr(self, "backup_path_conf", None) def get_backup(self, older_than=24, ignore_files=False, force=False): """ @@ -96,7 +102,7 @@ class BackupGenerator: self.backup_path_files = last_file self.backup_path_db = last_db self.backup_path_private_files = last_private_file - self.site_config_backup_path = site_config_backup_path + self.backup_path_conf = site_config_backup_path def set_backup_file_name(self): #Generate a random name using today's date and a 8 digit random number @@ -176,8 +182,6 @@ class BackupGenerator: with open(site_config_backup_path, "w") as n, open(site_config_path) as c: n.write(c.read()) - self.site_config_backup_path = site_config_backup_path - def take_dump(self): import frappe.utils From 843718f87bb85abbd52b0076666c3f7a5f001eea Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 15 Sep 2020 17:31:02 +0530 Subject: [PATCH 147/154] fix: Update integrations with new API --- .../integrations/doctype/dropbox_settings/dropbox_settings.py | 2 +- frappe/integrations/doctype/google_drive/google_drive.py | 2 +- .../doctype/s3_backup_settings/s3_backup_settings.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py index 864720174f..6b95a3f5bf 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py @@ -97,7 +97,7 @@ def backup_to_dropbox(upload_db_backup=True): if frappe.flags.create_new_backup: backup = new_backup(ignore_files=True) filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) - site_config = os.path.join(get_backups_path(), os.path.basename(backup.site_config_backup_path)) + site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf)) else: filename, site_config = get_latest_backup_file() diff --git a/frappe/integrations/doctype/google_drive/google_drive.py b/frappe/integrations/doctype/google_drive/google_drive.py index 58f28f882a..c1c73d7726 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.py +++ b/frappe/integrations/doctype/google_drive/google_drive.py @@ -191,7 +191,7 @@ def upload_system_backup_to_google_drive(): backup = new_backup() file_urls = [] file_urls.append(backup.backup_path_db) - file_urls.append(backup.site_config_backup_path) + file_urls.append(backup.backup_path_conf) if account.file_backup: file_urls.append(backup.backup_path_files) diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py index c59b0ddd5b..7c90d37f82 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -118,7 +118,7 @@ def backup_to_s3(): backup = new_backup(ignore_files=False, backup_path_db=None, backup_path_files=None, backup_path_private_files=None, force=True) db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) - site_config = os.path.join(get_backups_path(), os.path.basename(backup.site_config_backup_path)) + site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf)) if backup_files: files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files)) private_files = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_private_files)) From 8e9b1805e8a4ec3f8796df41a0925adc20db1fbf Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Wed, 16 Sep 2020 06:15:54 +0300 Subject: [PATCH 148/154] fix(security):[Snyk] 1 vulnerabilities (#11507) The following vulnerabilities are fixed with a Snyk patch: - https://snyk.io/vuln/SNYK-JS-LODASH-567746 --- .snyk | 6 + package.json | 2 +- yarn.lock | 1136 +++++++++++--------------------------------------- 3 files changed, 251 insertions(+), 893 deletions(-) diff --git a/.snyk b/.snyk index 6e7bb44986..f2ff29617a 100644 --- a/.snyk +++ b/.snyk @@ -65,3 +65,9 @@ patch: patched: '2020-04-30T23:02:32.330Z' - quill-image-resize > lodash: patched: '2020-08-24T23:06:37.710Z' + - node-sass > lodash: + patched: '2020-09-15T23:06:41.931Z' + - node-sass > sass-graph > lodash: + patched: '2020-09-15T23:06:41.931Z' + - node-sass > gaze > globule > lodash: + patched: '2020-09-15T23:06:41.931Z' diff --git a/package.json b/package.json index 3f319ffde9..9829356f8e 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "qz-tray": "^2.0.8", "redis": "^2.8.0", "showdown": "^1.9.1", - "snyk": "^1.382.0", + "snyk": "^1.398.0", "socket.io": "^2.3.0", "superagent": "^3.8.2", "touch": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 23bfe25255..f845904153 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@arcanis/slice-ansi@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@arcanis/slice-ansi/-/slice-ansi-1.0.2.tgz#35331e41a1062e3c53c01ad2ec1555c5c1959d8f" - integrity sha512-lDL63z0W/L/WTgqrwVOuNyMAsTv+pvjybd21z9SWdStmQoXT59E/iVWwat3gYjcdTNBf6oHAMoyFm8dtjpXEYw== - dependencies: - grapheme-splitter "^1.0.4" - "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -50,27 +43,6 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@nodelib/fs.scandir@2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" - integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== - dependencies: - "@nodelib/fs.stat" "2.0.3" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" - integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" - integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== - dependencies: - "@nodelib/fs.scandir" "2.1.3" - fastq "^1.6.0" - "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -81,81 +53,58 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1" integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg== -"@sindresorhus/is@^3.0.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-3.1.2.tgz#548650de521b344e3781fbdb0ece4aa6f729afb8" - integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ== +"@snyk/cli-interface@2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.9.1.tgz#e0466d183e7d4a13112ba098b1702a0d628dd380" + integrity sha512-2zHRvEt4S0DO+hPRX3hp5ssELouJqgb/JUTmPDMr/32r//qooSTxojwSvAK2A6VYgYOHuo1S3VTpsSP/ywkPXA== + dependencies: + "@snyk/dep-graph" "1.19.4" + "@types/graphlib" "^2.1.7" + tslib "^1.9.3" -"@snyk/cli-interface@2.3.2", "@snyk/cli-interface@^2.0.3": +"@snyk/cli-interface@2.9.2", "@snyk/cli-interface@^2.9.1", "@snyk/cli-interface@^2.9.2": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.9.2.tgz#defbeafd5fa7fa5ab1c39d57f1d379b2fbfc9860" + integrity sha512-C64bGtcQbh7941l7qgXFJ+FJIZdQtBHkPhKfGtUlCCMbC0FK0oaUmp6d7YPQxT4dEnkQdtlBT/eA2F6qIKbEng== + dependencies: + "@snyk/dep-graph" "1.19.4" + "@types/graphlib" "^2.1.7" + tslib "^1.9.3" + +"@snyk/cli-interface@^2.0.3": version "2.3.2" resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.3.2.tgz#e93afa82de15b912e657f1ba86f9d7963983e594" integrity sha512-jmZyxVHqzYU1GfdnWCGdd68WY/lAzpPVyqalHazPj4tFJehrSfEFc82RMTYAMgXEJuvFRFIwhsvXh3sWUhIQmg== dependencies: tslib "^1.9.3" -"@snyk/cli-interface@2.6.1": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.6.1.tgz#1e4e7acf5f9aca1267ddf3c1bb98a469011c907a" - integrity sha512-3X+OwwwT9j0r2ObqxYIiAgdaHsTW71b92PN3wawGAxl4YgPRrRVw8Fouhe41I4WJsn7OlKUNedylZguvpYg9qw== +"@snyk/cocoapods-lockfile-parser@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.5.2.tgz#7f441ecf2fb9c0b488db7e392447d2c520b60cef" + integrity sha512-fIiUNCmhDp7lVKTs/nHCnLK1roMkG15HhuQhtZXxiFW3EZ5H9IqMdtrxqjXuzVWt7X2h7lbF5OMBzD07NODtug== dependencies: - "@snyk/graphlib" "2.1.9-patch" - tslib "^1.9.3" - -"@snyk/cli-interface@2.8.0": - version "2.8.0" - resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.8.0.tgz#ee5b45b7c75942163875b29e712c44f9d7f36bb3" - integrity sha512-St/G39iJG1zQK15L24kcVYM2gmFc/ylBCcBqU2DMZKJKwOPccKLUO6s+dWIUXMccQ+DFS6TuHPvuAKQNi9C4Yg== - dependencies: - "@snyk/dep-graph" "1.19.0" - "@snyk/graphlib" "2.1.9-patch" - tslib "^1.9.3" - -"@snyk/cli-interface@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.8.1.tgz#67a0c34e8473dd433d6c206c41fa8b0e12426621" - integrity sha512-pALcfgoY0hAavy/pBlDIqEu+FFC5m+D4bMnCwlQ26mObL/zzxp2+Ohx+HykCIom62u2J94SzAtRLFdm/2TgoOw== - dependencies: - "@snyk/dep-graph" "1.19.0" - "@snyk/graphlib" "2.1.9-patch" - tslib "^1.9.3" - -"@snyk/cocoapods-lockfile-parser@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.5.1.tgz#572907ae12abf77c1ea3dd497f7d1ced31104aa7" - integrity sha512-0bzajH/HdP3k5cOZKUmT/xqmHZFuWN124c/lrqh+U6Q1Z9Bt7TLOB2ifLKL+1I4rq+IgOesGWJYG1KhxBy3RLw== - dependencies: - "@snyk/dep-graph" "1.19.3" - "@snyk/ruby-semver" "^3.0.0" + "@snyk/dep-graph" "1.19.4" "@types/js-yaml" "^3.12.1" js-yaml "^3.13.1" source-map-support "^0.5.7" tslib "^1.10.0" -"@snyk/composer-lockfile-parser@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.0.tgz#a16fff515288496a27292b32b5cc14d7d84f9026" - integrity sha512-ga4YTRjJUuP0Ufr+t1IucwVjEFAv66JSBB/zVHP2zy/jmfA3l3ZjlGQSjsRC6Me9P2Z0esQ83AYNZvmIf9pq2w== +"@snyk/composer-lockfile-parser@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.1.tgz#2f7c93ad367520322b16d9490a208fec08445e0e" + integrity sha512-wNANv235j95NFsQuODIXCiQZ9kcyg9fz92Kg1zoGvaP3kN/ma7fgCnvQL/dyml6iouQJR5aZovjhrrfEFoKtiQ== dependencies: - "@snyk/lodash" "^4.17.15-patch" + lodash.findkey "^4.6.0" + lodash.get "^4.4.2" + lodash.invert "^4.3.0" + lodash.isempty "^4.4.0" -"@snyk/dep-graph@1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.0.tgz#dfb2699520225e715083f6dd590bb91b55e99ba1" - integrity sha512-/0phOICMk4hkX2KtZgi+4KNd5G9oYDIlxQDQk+ui2xl4gonPvK6Q5MFzHP7Xet1YY/XoU33ox41i+IO48qZ+zQ== +"@snyk/dep-graph@1.19.4", "@snyk/dep-graph@^1.19.3", "@snyk/dep-graph@^1.19.4": + version "1.19.4" + resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.4.tgz#d156c482bfdfba7505f1cf3e8a80b86dd9f45383" + integrity sha512-h3MMhjVm3BuIruwpDBqnMowKOG9viwr3TJHdIxTHulWKWSsPTTW1AAP3/RaK+UBp1y/Ua9yzeHncKIrzBdT5Nw== dependencies: - "@snyk/graphlib" "2.1.9-patch" - lodash.isequal "^4.5.0" - object-hash "^2.0.3" - semver "^6.0.0" - source-map-support "^0.5.19" - tslib "^2.0.0" - -"@snyk/dep-graph@1.19.3", "@snyk/dep-graph@^1.17.0", "@snyk/dep-graph@^1.19.0": - version "1.19.3" - resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.3.tgz#f20521baf060f83f052fd6b55fad8b377833418d" - integrity sha512-WJLUFKBokoFK5imi0t8Dkyj+uqtS/5Ziuf4oE/OOFX30UqP1ffMDkv9/3sqBJQVQ9FjdgsX3Cm8JZMtMlYRc6w== - dependencies: - "@snyk/graphlib" "2.1.9-patch.2" + graphlib "^2.1.8" lodash.isequal "^4.5.0" object-hash "^2.0.3" semver "^6.0.0" @@ -176,63 +125,15 @@ resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.2.0.tgz#919857944973cce74c650e5428aaf11bcd5c0457" integrity sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA== -"@snyk/graphlib@2.1.9-patch": - version "2.1.9-patch" - resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.tgz#2cf8b39fc879681569c8070776feaec8efe71442" - integrity sha512-uFO/pNMm3pN15QB+hVMU7uaQXhsBNwEA8lOET/VDcdOzLptODhXzkJqSHqt0tZlpdAz6/6Uaj8jY00UvPFgFMA== +"@snyk/java-call-graph-builder@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.13.2.tgz#6e4a9495d5c47bbab9bc69e066d4646473781b67" + integrity sha512-YN3a93ttscqFQRUeThrxa7i2SJkFPfYn0VpFqdPB6mIJz2fRVLxUkMtlCbG0aSEUvWiLnGVHN0IYxwWEzhq11w== dependencies: - "@snyk/lodash" "4.17.15-patch" - -"@snyk/graphlib@2.1.9-patch.2": - version "2.1.9-patch.2" - resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.2.tgz#571255808f5bf291f42d51f1e1f0adc8f9145be9" - integrity sha512-BjJzOXDNzoEMBOjSks7vadu5f0c39SeorJMi9vUvvWM5dcE22CZqcN9VMRW5DYTifUJiCWszkm5TOyfYfB0bfg== - dependencies: - lodash.clone "^4.5.0" - lodash.constant "^3.0.0" - lodash.filter "^4.6.0" - lodash.foreach "^4.5.0" - lodash.has "^4.5.2" - lodash.isarray "^4.0.0" - lodash.isempty "^4.4.0" - lodash.isfunction "^3.0.9" - lodash.isundefined "^3.0.1" - lodash.keys "^4.2.0" - lodash.map "^4.6.0" - lodash.reduce "^4.6.0" - lodash.size "^4.2.0" - lodash.transform "^4.6.0" - lodash.union "^4.6.0" - lodash.values "^4.3.0" - -"@snyk/inquirer@6.2.2-patch": - version "6.2.2-patch" - resolved "https://registry.yarnpkg.com/@snyk/inquirer/-/inquirer-6.2.2-patch.tgz#14bfd111493eebdb8858f7ac4a98f8d274d10a91" - integrity sha512-IUq5bHRL0vtVKtfvd4GOccAIaLYHbcertug2UVZzk5+yY6R/CxfYsnFUTho1h4BdkfNdin2tPjE/5jRF4SKSrw== - dependencies: - "@snyk/lodash" "4.17.15-patch" - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.0.0" - through "^2.3.6" - -"@snyk/java-call-graph-builder@1.13.1": - version "1.13.1" - resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.13.1.tgz#641d0f929baa95cd30f8e16f938b4d21499a1e9b" - integrity sha512-oOCSIyOMplV73a1agcXKXlFYQftK5esUUaFRTf90GOxQwKy8R9tZtKdP+CdutlgvjRP286DQ+7GlvKYsGGZbWg== - dependencies: - "@snyk/graphlib" "2.1.9-patch" ci-info "^2.0.0" debug "^4.1.1" glob "^7.1.6" + graphlib "^2.1.8" jszip "^3.2.2" needle "^2.3.3" progress "^2.0.3" @@ -241,11 +142,6 @@ temp-dir "^2.0.0" tslib "^1.9.3" -"@snyk/lodash@4.17.15-patch", "@snyk/lodash@^4.17.15-patch": - version "4.17.15-patch" - resolved "https://registry.yarnpkg.com/@snyk/lodash/-/lodash-4.17.15-patch.tgz#fb61af14b75d10a20015b40af5d0423944af89dc" - integrity sha512-e4+t34bGyjjRnwXwI14hqye9J/nRbG9iwaqTgXWHskm5qC+iK0UrjgYdWXiHJCf3Plbpr+1rpW+4LPzZnCGMhQ== - "@snyk/rpm-parser@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@snyk/rpm-parser/-/rpm-parser-2.0.0.tgz#4ded7fa4b0a8efca7699359e4ca7a79bfbe38bc1" @@ -253,31 +149,14 @@ dependencies: event-loop-spinner "^2.0.0" -"@snyk/ruby-semver@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@snyk/ruby-semver/-/ruby-semver-2.2.0.tgz#dfb2f11c52e52f8273ec8750e9327db7c84e679c" - integrity sha512-FqUayoVjcyCsQFYPm3DcaCKdFR4xmapUkCGY+bcNBs3jqCUw687PoP9CPQ1Jvtaw5YpfBNl/62jyntsWCeciuA== +"@snyk/snyk-cocoapods-plugin@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.1.tgz#af4e749ee1420c1596345fefae3c5eb48b84ecb0" + integrity sha512-A+1xHD+SpmXQa0p+dWmiApFZtz/y37qAW9aWmFx2B1j7fwRBf9Qr89/6RbJOznf1a4nEitjzE3fa98yNZk/MNg== dependencies: - "@snyk/lodash" "4.17.15-patch" - -"@snyk/ruby-semver@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@snyk/ruby-semver/-/ruby-semver-3.0.0.tgz#592e648343a88eccb7d787a97d3ad6c9df1dd61f" - integrity sha512-GoSRcwNuJ/mK3Q+tqelRJlylPh8K3RZRWh3ZpkOKm1gQPdG+z0wt+LipSIHxGR8yBDl5bQjwTrPLkL49/N1V6Q== - dependencies: - lodash.escaperegexp "^4.1.0" - lodash.flatten "^4.4.0" - lodash.uniq "^4.5.0" - tslib "^1.13.0" - -"@snyk/snyk-cocoapods-plugin@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.0.tgz#8dedb9d3165e5c8cd72ab13fdb49397083a15866" - integrity sha512-arK4VHzNh/D9vCFQFeAiSP+rMRXwLbzaRoIKucodf8Q/3KftIo/byeDmoc2Cc7awR1HPo5E391bwBNH5ra8UqA== - dependencies: - "@snyk/cli-interface" "2.6.1" - "@snyk/cocoapods-lockfile-parser" "3.5.1" - "@snyk/dep-graph" "^1.19.0" + "@snyk/cli-interface" "^2.9.2" + "@snyk/cocoapods-lockfile-parser" "3.5.2" + "@snyk/dep-graph" "^1.19.4" source-map-support "^0.5.7" tslib "^2.0.0" @@ -325,23 +204,15 @@ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== -"@types/emscripten@^1.38.0": - version "1.39.4" - resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8" - integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ== - "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== -"@types/glob@^7.1.1": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" +"@types/graphlib@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@types/graphlib/-/graphlib-2.1.7.tgz#e6a47a4f43511f5bad30058a669ce5ce93bfd823" + integrity sha512-K7T1n6U2HbTYu+SFHlBjz/RH74OA2D/zF1qlzn8uXbvB4uRg7knOM85ugS2bbXI1TXMh7rLqk4OVRwIwEBaixg== "@types/hosted-git-info@^2.7.0": version "2.7.0" @@ -365,11 +236,6 @@ dependencies: "@types/node" "*" -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - "@types/node@*": version "13.7.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.4.tgz#76c3cb3a12909510f52e5dc04a6298cdf9504ffd" @@ -380,11 +246,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.10.4.tgz#3f5fc4f0f322805f009e00ab35a2ff3d6b778e42" integrity sha512-wa09itaLE8L705aXd8F80jnFpxz3Y1/KRHfKsYL2bPc0XF+wEWu8sR9n5bmeu8Ba1N9z2GRNzm/YdHcghLkLKg== -"@types/node@^13.7.0": - version "13.13.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766" - integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw== - "@types/node@^6.14.4": version "6.14.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.14.9.tgz#733583e21ef0eab85a9737dfafbaa66345a92ef0" @@ -417,13 +278,6 @@ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== -"@types/xml2js@0.4.5": - version "0.4.5" - resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa" - integrity sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w== - dependencies: - "@types/node" "*" - "@vue/component-compiler-utils@^1.2.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.3.1.tgz#686f0b913d59590ae327b2a1cb4b6d9b931bbe0e" @@ -465,98 +319,11 @@ postcss-modules-sync "^1.0.0" source-map "0.6.*" -"@yarnpkg/core@^2.0.0-rc.29": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@yarnpkg/core/-/core-2.1.1.tgz#637786eb2acd09b48db7af59f2287934a0a5afd7" - integrity sha512-qeBxz8nHjKAbGTP2ZcXBnXGfM7+cN0A73mIai/24uru1ayvCIgfjWL1uIj/MM+m+K5lJX0Dcn94ZBHWits9JWQ== - dependencies: - "@arcanis/slice-ansi" "^1.0.2" - "@yarnpkg/fslib" "^2.1.0" - "@yarnpkg/json-proxy" "^2.1.0" - "@yarnpkg/libzip" "^2.1.0" - "@yarnpkg/parsers" "^2.1.0" - "@yarnpkg/pnp" "^2.1.0" - "@yarnpkg/shell" "^2.1.0" - camelcase "^5.3.1" - chalk "^3.0.0" - ci-info "^2.0.0" - clipanion "^2.4.2" - cross-spawn "7.0.3" - diff "^4.0.1" - globby "^10.0.1" - got "^11.1.3" - json-file-plus "^3.3.1" - logic-solver "^2.0.1" - micromatch "^4.0.2" - mkdirp "^0.5.1" - p-limit "^2.2.0" - pluralize "^7.0.0" - pretty-bytes "^5.1.0" - semver "^7.1.2" - stream-to-promise "^2.2.0" - tar "^4.4.6" - tslib "^1.13.0" - tunnel "^0.0.6" - -"@yarnpkg/fslib@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/fslib/-/fslib-2.1.0.tgz#c7039ec369a8fde06932cbddb3d3e473d789c458" - integrity sha512-E+f8w5yQZnTf1soyTWy7qdf+GmHsY+A0yEN4Di44/Txk6XRIMruyc1ShDi93mOI6ilnXxD87rNms18zJ8WnspA== - dependencies: - "@yarnpkg/libzip" "^2.1.0" - tslib "^1.13.0" - -"@yarnpkg/json-proxy@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/json-proxy/-/json-proxy-2.1.0.tgz#362a161678cd7dda74b47b4fc848a2f1730d16cd" - integrity sha512-rOgCg2DkyviLgr80mUMTt9vzdf5RGOujQB26yPiXjlz4WNePLBshKlTNG9rKSoKQSOYEQcw6cUmosfOKDatrCw== - dependencies: - "@yarnpkg/fslib" "^2.1.0" - tslib "^1.13.0" - -"@yarnpkg/libzip@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/libzip/-/libzip-2.1.0.tgz#f45d861a853555a063f90a85142199e7cf181b71" - integrity sha512-39c7KuSWcYUqVxlBLZwfqdD/D6lS+jplNVWd6uAnk8EpnacaYGJRegvkqWyfw5c8KHukNMeEGF5JHrXPZYBM0w== - dependencies: - "@types/emscripten" "^1.38.0" - tslib "^1.13.0" - -"@yarnpkg/lockfile@^1.0.2", "@yarnpkg/lockfile@^1.1.0": +"@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@yarnpkg/parsers@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-2.1.0.tgz#52615acdb54803d6404d6fb8f15fa124936bff1f" - integrity sha512-75OYQ6PMs1C3zm+W+T1xhLyVDX78zXQGEVHpWd4o/QwpAbhneB3/5FXVGRzI3gjPPWWSb/pKOPB1S6p0xmQD2Q== - dependencies: - js-yaml "^3.10.0" - tslib "^1.13.0" - -"@yarnpkg/pnp@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/pnp/-/pnp-2.1.0.tgz#0eba881ccd4675594198e7f1e327fe11a845291b" - integrity sha512-b8NlB71EFifv1jDX47nFaRXrykROxHcS7YuGb2dQ+Gp9gqJ0thIaZ3yB9+qWF8acdWtNcMpjCug4xkfAAR5Odw== - dependencies: - "@types/node" "^13.7.0" - "@yarnpkg/fslib" "^2.1.0" - tslib "^1.13.0" - -"@yarnpkg/shell@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/shell/-/shell-2.1.0.tgz#e0855e59d4df663d8fe9044950a7fd390110cd4b" - integrity sha512-9i9ZWqeKHGV0DOfdxTVq5zl73Li8Fg947v57uLBEaytNF+HywkDfouNkg/6HfgBrpI0WH8OJ9Pz/uDaE5cpctw== - dependencies: - "@yarnpkg/fslib" "^2.1.0" - "@yarnpkg/parsers" "^2.1.0" - clipanion "^2.4.2" - cross-spawn "7.0.3" - fast-glob "^3.2.2" - stream-buffers "^3.0.2" - tslib "^1.13.0" - abbrev@1, abbrev@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -637,7 +404,7 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-escapes@3.2.0, ansi-escapes@^3.2.0: +ansi-escapes@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -647,6 +414,13 @@ ansi-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -692,11 +466,6 @@ ansicolors@^0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= -any-promise@^1.1.0, any-promise@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -757,11 +526,6 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -1038,13 +802,6 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" @@ -1264,6 +1021,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1279,11 +1044,6 @@ child-process@^1.0.2: resolved "https://registry.yarnpkg.com/child-process/-/child-process-1.0.2.tgz#98974dc7ed1ee4c6229f8e305fa7313a6885a7f2" integrity sha1-mJdNx+0e5MYin44wX6cxOmiFp/I= -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - ci-info@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" @@ -1323,12 +1083,12 @@ cli-cursor@^1.0.2: dependencies: restore-cursor "^1.0.1" -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: - restore-cursor "^2.0.0" + restore-cursor "^3.1.0" cli-spinner@0.2.10: version "0.2.10" @@ -1348,15 +1108,10 @@ cli-truncate@^0.2.1: slice-ansi "0.0.4" string-width "^1.0.1" -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -clipanion@^2.4.2: - version "2.4.4" - resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-2.4.4.tgz#d70244c6f60feb3f4cbd509d2fcbe829fc619061" - integrity sha512-KjyCBz8xplftHjIK/nOqq/9b3hPlXbAAo/AxoITrO4yySpQ6a9QSJDAfOx9PfcRUHteeqbdNxZKSPfeFqQ7plg== +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^3.0.3, cliui@^3.2.0: version "3.2.0" @@ -1611,15 +1366,6 @@ cosmiconfig@^5.0.0: lodash.get "^4.4.2" parse-json "^4.0.0" -cross-spawn@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -2054,13 +1800,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - docker-modem@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-2.1.3.tgz#15432225f63db02eb5de4bb9a621b7293e5f264d" @@ -2071,12 +1810,12 @@ docker-modem@2.1.3: split-ca "^1.0.1" ssh2 "^0.8.7" -dockerfile-ast@0.0.19: - version "0.0.19" - resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.0.19.tgz#b1e21138eba995d7bf5576dc30ba1130c15995c3" - integrity sha512-iDRNFeAB2j4rh/Ecc2gh3fjciVifCMsszfCfHlYF5Wv8yybjZLiRDZUBt/pS3xrAz8uWT8fCHLq4pOQMmwCDwA== +dockerfile-ast@0.0.30: + version "0.0.30" + resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.0.30.tgz#74cbcd65e389852d752c5687117255ea622583ad" + integrity sha512-QOeP5NjbjoSLtnMz6jzBLsrKtywLEVPoCOAwA54cQpulyKb1gBnZ63tr6Amq8oVDvu5PXa3aifBVw+wcoCGHKg== dependencies: - vscode-languageserver-types "^3.5.0" + vscode-languageserver-types "^3.15.1" dom-serializer@0: version "0.1.1" @@ -2113,13 +1852,14 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotnet-deps-parser@4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/dotnet-deps-parser/-/dotnet-deps-parser-4.10.0.tgz#f51c1e36700c9bf992263adb478e0e6f0095c4f0" - integrity sha512-dEO1oTvreaDCtcvhRdOmmAMubyC+MWqVr1c/1Wvasi+NW4NZeB67qGh1taqowUFh+aCXtPw3SP2eExn6aNkhwA== +dotnet-deps-parser@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/dotnet-deps-parser/-/dotnet-deps-parser-5.0.0.tgz#5115c442cbefea59e4fb9f9ed8fa4863a0f3186d" + integrity sha512-1l9K4UnQQHSfKgeHeLrxnB53AidCZqPyf9dkRL4/fZl8//NPiiDD43zHtgylw8DHlO7gvM8+O5a0UPHesNYZKw== dependencies: - "@snyk/lodash" "4.17.15-patch" - "@types/xml2js" "0.4.5" + lodash.isempty "^4.4.0" + lodash.set "^4.3.2" + lodash.uniq "^4.5.0" source-map-support "^0.5.7" tslib "^1.10.0" xml2js "0.4.23" @@ -2209,13 +1949,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -end-of-stream@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" - integrity sha1-6TUyWLqpEIll78QcsO+K3i88+wc= - dependencies: - once "~1.3.0" - engine.io-client@~3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700" @@ -2414,13 +2147,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-loop-spinner@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-1.1.0.tgz#96de9c70e6e2b0b3e257b0901e25e792e3c9c8d0" - integrity sha512-YVFs6dPpZIgH665kKckDktEVvSBccSYJmoZUfhNUdv5d3Xv+Q+SKF4Xis1jolq9aBzuW1ZZhQh/m/zU/TPdDhw== - dependencies: - tslib "^1.10.0" - event-loop-spinner@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-2.0.0.tgz#3aa949264a7d80be45d6cb782ab5e97ffc87b9e1" @@ -2620,18 +2346,6 @@ fast-diff@1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.0.3, fast-glob@^3.2.2: - version "3.2.4" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" - integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" - merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" - fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -2647,13 +2361,6 @@ fastparse@^1.1.1: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== -fastq@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" - integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== - dependencies: - reusify "^1.0.4" - fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" @@ -2676,10 +2383,10 @@ figures@^1.7.0: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -2698,13 +2405,6 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -2812,13 +2512,6 @@ fs-extra@5.0.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2977,13 +2670,6 @@ gl-vec3@^1.0.3: resolved "https://registry.yarnpkg.com/gl-vec3/-/gl-vec3-1.1.3.tgz#a47c62f918774a06cbed1b65bcd0288ecbb03826" integrity sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw== -glob-parent@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== - dependencies: - is-glob "^4.0.1" - glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -3030,20 +2716,6 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" -globby@^10.0.1: - version "10.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -3070,23 +2742,6 @@ got@11.4.0: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^11.1.3: - version "11.5.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.5.2.tgz#772e3f3a06d9c7589c7c94dc3c83cdb31ddbf742" - integrity sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww== - dependencies: - "@sindresorhus/is" "^3.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.0" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -3109,12 +2764,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -graphlib@^2.1.8: +graphlib@2.1.8, graphlib@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== @@ -3348,7 +2998,7 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -http2-wrapper@^1.0.0-beta.4.5, http2-wrapper@^1.0.0-beta.5.0: +http2-wrapper@^1.0.0-beta.4.5: version "1.0.0-beta.5.2" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== @@ -3386,11 +3036,6 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ignore@^5.1.1: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -3483,6 +3128,25 @@ ini@^1.3.0, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -3657,7 +3321,7 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= @@ -3691,13 +3355,6 @@ is-glob@^3.0.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - is-gzip@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" @@ -3746,11 +3403,6 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" @@ -3885,11 +3537,6 @@ is-yarn-global@^0.3.0: resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -is@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79" - integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3965,7 +3612,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0: +js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -3998,17 +3645,6 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-file-plus@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/json-file-plus/-/json-file-plus-3.3.1.tgz#f4363806b82819ff8803d83d539d6a9edd2a5258" - integrity sha512-wo0q1UuiV5NsDPQDup1Km8IwEeqe+olr8tkWxeJq9Bjtcp7DZ0l+yrg28fSC3DEtrE311mhTZ54QGS6oiqnZEA== - dependencies: - is "^3.2.1" - node.extend "^2.0.0" - object.assign "^4.1.0" - promiseback "^2.0.2" - safer-buffer "^2.0.2" - json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -4051,17 +3687,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jszip@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271" - integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw== - dependencies: - lie "~3.3.0" - pako "~1.0.2" - readable-stream "~2.3.6" - set-immediate-shim "~1.0.1" - -jszip@^3.2.2: +jszip@3.4.0, jszip@^3.2.2: version "3.4.0" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350" integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg== @@ -4265,20 +3891,10 @@ lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.constant@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash.constant/-/lodash.constant-3.0.0.tgz#bfe05cce7e515b3128925d6362138420bd624910" - integrity sha1-v+Bczn5RWzEokl1jYhOEIL1iSRA= - -lodash.escaperegexp@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" - integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= - -lodash.filter@^4.6.0: +lodash.findkey@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= + resolved "https://registry.yarnpkg.com/lodash.findkey/-/lodash.findkey-4.6.0.tgz#83058e903b51cbb759d09ccf546dea3ea39c4718" + integrity sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg= lodash.flatmap@^4.5.0: version "4.5.0" @@ -4290,25 +3906,15 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= -lodash.foreach@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.has@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" - integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI= - -lodash.isarray@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403" - integrity sha1-KspJayjEym1yZxUxNZDALm6jRAM= +lodash.invert@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.invert/-/lodash.invert-4.3.0.tgz#8ffe20d4b616f56bea8f1aa0c6ebd80dcf742aee" + integrity sha1-j/4g1LYW9WvqjxqgxuvYDc90Ku4= lodash.isempty@^4.4.0: version "4.4.0" @@ -4320,26 +3926,6 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfunction@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" - integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== - -lodash.isundefined@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" - integrity sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g= - -lodash.keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" - integrity sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU= - -lodash.map@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4355,51 +3941,31 @@ lodash.once@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.reduce@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= -lodash.size@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.size/-/lodash.size-4.2.0.tgz#71fe75ed3eabdb2bcb73a1b0b4f51c392ee27b86" - integrity sha1-cf517T6r2yvLc6GwtPUcOS7ie4Y= - lodash.topairs@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64" integrity sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ= -lodash.transform@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0" - integrity sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A= - -lodash.union@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash.values@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" - integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c= - lodash@4.17.15, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19, lodash@^4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log-symbols@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -4422,13 +3988,6 @@ log-update@^1.0.2: ansi-escapes "^1.0.0" cli-cursor "^1.0.2" -logic-solver@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/logic-solver/-/logic-solver-2.0.1.tgz#e9fa47002eb5d8cda7616d41639b97552eb674be" - integrity sha1-6fpHAC612M2nYW1BY5uXVS62dL4= - dependencies: - underscore "^1.7.0" - loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -4569,11 +4128,6 @@ merge-stream@^1.0.1: dependencies: readable-stream "^2.0.1" -merge2@^1.2.3, merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -4598,14 +4152,6 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - mime-db@1.40.0: version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" @@ -4635,10 +4181,10 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" @@ -4667,21 +4213,6 @@ minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -4729,10 +4260,10 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.13.2: version "2.14.0" @@ -4787,19 +4318,19 @@ ndarray@^1.0.13, ndarray@^1.0.18: iota-array "^1.0.0" is-buffer "^1.0.2" -needle@^2.3.3: - version "2.4.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== +needle@2.5.0, needle@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" + integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== dependencies: debug "^3.2.6" iconv-lite "^0.4.4" sax "^1.2.4" -needle@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" - integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== +needle@^2.3.3: + version "2.4.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" + integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== dependencies: debug "^3.2.6" iconv-lite "^0.4.4" @@ -4878,14 +4409,6 @@ node-sass@^4.13.1: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" -node.extend@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-2.0.2.tgz#b4404525494acc99740f3703c496b7d5182cc6cc" - integrity sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ== - dependencies: - has "^1.0.3" - is "^3.2.1" - "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -5072,24 +4595,17 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -once@~1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" - integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA= - dependencies: - wrappy "1" - onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: - mimic-fn "^1.0.0" + mimic-fn "^2.1.0" open@^7.0.3: version "7.0.3" @@ -5176,13 +4692,6 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -5350,11 +4859,6 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -5374,11 +4878,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - peek-stream@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" @@ -5398,11 +4897,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - pify@^2.0.0, pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5425,11 +4919,6 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== - pngjs@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" @@ -5844,11 +5333,6 @@ prettier@^1.13.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== -pretty-bytes@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" - integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -5859,33 +5343,18 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-deferred@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/promise-deferred/-/promise-deferred-2.0.3.tgz#b99c9588820798501862a593d49cece51d06fd7f" - integrity sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ== - dependencies: - promise "^7.3.1" - promise.series@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= -"promise@>=3.2 <8", promise@^7.1.1, promise@^7.3.1: +"promise@>=3.2 <8", promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" -promiseback@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/promiseback/-/promiseback-2.0.3.tgz#bd468d86930e8cd44bfc3292de9a6fbafb6378e6" - integrity sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w== - dependencies: - is-callable "^1.1.5" - promise-deferred "^2.0.3" - proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -6414,12 +5883,12 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: - onetime "^2.0.0" + onetime "^5.1.0" signal-exit "^3.0.2" ret@~0.1.10: @@ -6427,11 +5896,6 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - rgb-regex@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" @@ -6552,17 +6016,10 @@ rollup@^1.2.2: "@types/node" "^11.9.5" acorn "^6.1.1" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -run-parallel@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" - integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== rxjs@^5.0.0-beta.11: version "5.5.12" @@ -6571,10 +6028,10 @@ rxjs@^5.0.0-beta.11: dependencies: symbol-observable "1.0.1" -rxjs@^6.4.0: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== dependencies: tslib "^1.9.0" @@ -6650,11 +6107,6 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -6736,23 +6188,11 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - showdown@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz#134e148e75cd4623e09c21b0511977d79b5ad0ef" @@ -6785,11 +6225,6 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" @@ -6830,16 +6265,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -snyk-config@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-3.1.0.tgz#61dec9296bff8d7459c46bd3f1e6c0ffcf7eea5f" - integrity sha512-3UlyogA67/9WOssJ7s4d7gqWQRWyO/LbgdBBNMhhmFDKa7eTUSW+A782CVHgyDSJZ2kNANcMWwMiOL+h3p6zQg== - dependencies: - "@snyk/lodash" "4.17.15-patch" - debug "^4.1.1" - nconf "^0.10.0" - -snyk-config@^3.0.0: +snyk-config@3.1.1, snyk-config@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-3.1.1.tgz#a511ef8bf769545f0564e09d382b5ea3aacb9c6a" integrity sha512-wwrMIEDozfLJ8LmakCsCC1FQ0siIX5icCQPCbUKKgRbeVsZ27NjPJs37BpTXX4rcHkaWpe8TbH3yOtp23qmszg== @@ -6848,28 +6274,31 @@ snyk-config@^3.0.0: lodash.merge "^4.6.2" nconf "^0.10.0" -snyk-cpp-plugin@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/snyk-cpp-plugin/-/snyk-cpp-plugin-1.2.0.tgz#1e554eece61d40288bbc745f040c2ca54ec7e08a" - integrity sha512-zc2h0IAAjcxJwxWp4rauliwEUXZahmC3WoCgjTmRi36UhzwlxWJf8QpIeSOKJnc3t0NhlKzUc1YFkaQGJ2dXVQ== +snyk-cpp-plugin@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/snyk-cpp-plugin/-/snyk-cpp-plugin-1.5.0.tgz#2ec2068fdcf5e579eb7d9b9eed8bb984fd00a925" + integrity sha512-nBZ0cBmpT4RVJUFzYydQJOxwjcdXk7NtRJE1UIIOafQa2FcvIl3GBezfrCJ6pu61svOAf5r8Qi/likx6F15K1A== dependencies: + "@snyk/dep-graph" "^1.19.3" + chalk "^4.1.0" + debug "^4.1.1" tslib "^2.0.0" -snyk-docker-plugin@3.17.0: - version "3.17.0" - resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-3.17.0.tgz#9f48f9d4455b15dc15fdfc37137c567e9c9e24e2" - integrity sha512-Pq0xr/NQQUss3bLb0rTfVtsot/9ID6TzZCWlF9nMrj9JFrJRrJPQxi/KO/uqfX0Nbwz4VysXDzwdWNtrxpaabg== +snyk-docker-plugin@3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-3.21.0.tgz#a92074c0411578c1a7b86852a06f1421770e985d" + integrity sha512-A7oJS3QGR7bwm1qeeczCb8PDfi8go1KM6VWph/drJHBQ7JxVKKLb3j4AzrMmIM96mGZFbmyNOL4pznwumaOM8g== dependencies: "@snyk/rpm-parser" "^2.0.0" "@snyk/snyk-docker-pull" "^3.2.0" debug "^4.1.1" docker-modem "2.1.3" - dockerfile-ast "0.0.19" + dockerfile-ast "0.0.30" event-loop-spinner "^2.0.0" gunzip-maybe "^1.4.2" mkdirp "^1.0.4" semver "^6.1.0" - snyk-nodejs-lockfile-parser "1.22.0" + snyk-nodejs-lockfile-parser "1.28.1" tar-stream "^2.1.0" tmp "^0.2.1" tslib "^1" @@ -6883,25 +6312,25 @@ snyk-go-parser@1.4.1: toml "^3.0.0" tslib "^1.10.0" -snyk-go-plugin@1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/snyk-go-plugin/-/snyk-go-plugin-1.16.0.tgz#4688e45e59d1673256f645fccca2fa6a7ab0f55a" - integrity sha512-XNGHEFyP+pCzcqmXnj5T/1Oy6AZzm2WkTSuUpohWQ/09ecMRCCv2yrr/kwMQemrKN4+7CoJS/9xfm3GnNlzVHA== +snyk-go-plugin@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/snyk-go-plugin/-/snyk-go-plugin-1.16.2.tgz#aa3d57fee79d4f2c6bb2282ec94609123fd2ed1d" + integrity sha512-FAM56z3bl1iuxeqkCEA/jyZ2hpwkQK8xQxQbhR+QppEK5lole7w1PQyWYgZAJ9oRY/BU32zdRAJwGuZbhk7G2Q== dependencies: - "@snyk/dep-graph" "1.19.3" - "@snyk/graphlib" "2.1.9-patch" + "@snyk/dep-graph" "1.19.4" debug "^4.1.1" + graphlib "2.1.8" snyk-go-parser "1.4.1" - tmp "0.2.0" + tmp "0.2.1" tslib "^1.10.0" -snyk-gradle-plugin@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.5.1.tgz#a424a7b8136471296e971e7b25f4776d1a08b9ff" - integrity sha512-8tZwQCqRbjp1azvc+bBRXSbn2AjbUpFAM6qoSpM/IZpfGl1RaOtz4/JTkGFxj+iBhTFoAkGxEunT66eO0pHZZw== +snyk-gradle-plugin@3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.6.3.tgz#484059bcb98469b6a674bbcbdc995eafb5581041" + integrity sha512-j/eQSLSsK3DHmvVX2fNig4+ugYrKlCOV8Xvo6OYFkNzhMpdyNFiGWTS1uyP1HH75Gyc78MaLANMgjlSYePukzQ== dependencies: - "@snyk/cli-interface" "2.8.0" - "@snyk/dep-graph" "^1.17.0" + "@snyk/cli-interface" "2.9.1" + "@snyk/dep-graph" "^1.19.4" "@types/debug" "^4.1.4" chalk "^3.0.0" debug "^4.1.1" @@ -6934,43 +6363,27 @@ snyk-module@^2.0.2: debug "^3.1.0" hosted-git-info "^2.7.1" -snyk-mvn-plugin@2.19.1: - version "2.19.1" - resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.19.1.tgz#00b68a459a4da9e39b0d8c416ae05496b3b03ee3" - integrity sha512-VXYJSdhUmOQAyxdsv5frAKbi3UOcHPabWEQxQ9wxhVBEEmx2lP5ajv1a+ntxwWwL7u3jdc+rnCIKHpLlQJ5nyw== +snyk-mvn-plugin@2.19.4: + version "2.19.4" + resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.19.4.tgz#4e29fa82b9ca409789d441939c766797d6a2360f" + integrity sha512-kYPUKOugnNd31PFqx1YHJTo90pospELYHME4AzBx8dkMDgs5ZPjAmQXSxegQ3AMUqfqcETMSTzlKHe6uHujI8A== dependencies: - "@snyk/cli-interface" "2.8.1" - "@snyk/java-call-graph-builder" "1.13.1" + "@snyk/cli-interface" "2.9.1" + "@snyk/java-call-graph-builder" "1.13.2" debug "^4.1.1" needle "^2.5.0" tmp "^0.1.0" tslib "1.11.1" -snyk-nodejs-lockfile-parser@1.22.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.22.0.tgz#213e4a9bfda45b6af490d71a511b63e46b64a07f" - integrity sha512-l6jLoJxqcIIkQopSdQuAstXdMw5AIgLu+uGc5CYpHyw8fYqOwna8rawwofNeGuwJAAv4nEiNiexeYaR88OCq6Q== +snyk-nodejs-lockfile-parser@1.28.1: + version "1.28.1" + resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.28.1.tgz#9eda1354bbca1fc881a4e63a1e1042f80c37bff2" + integrity sha512-0zbmtidYLI2ia/DQD4rZm2YKrhfHLvHlVBdF2cMAGPwhOoKW5ovG9eBO4wNQdvjxNi7b4VeUyAj8SfuhjDraDQ== dependencies: - "@snyk/graphlib" "2.1.9-patch" - "@snyk/lodash" "^4.17.15-patch" - "@yarnpkg/lockfile" "^1.0.2" - event-loop-spinner "^1.1.0" - p-map "2.1.0" - snyk-config "^3.0.0" - source-map-support "^0.5.7" - tslib "^1.9.3" - uuid "^3.3.2" - -snyk-nodejs-lockfile-parser@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.27.0.tgz#cde4d052ac429c5fa2844c981578f5ff5dea3022" - integrity sha512-s1yEqEC8uxELzEXqXmnWKpoOIn0rM8StMjuy0X5U+FmxGvO3jRtFDU5hcVw9cAULLfrQYBHRowrO/5FaR79fxA== - dependencies: - "@snyk/graphlib" "2.1.9-patch" - "@yarnpkg/core" "^2.0.0-rc.29" "@yarnpkg/lockfile" "^1.1.0" event-loop-spinner "^2.0.0" got "11.4.0" + graphlib "2.1.8" lodash.clonedeep "^4.5.0" lodash.flatmap "^4.5.0" lodash.isempty "^4.4.0" @@ -6983,15 +6396,15 @@ snyk-nodejs-lockfile-parser@1.27.0: uuid "^3.3.2" yaml "^1.9.2" -snyk-nuget-plugin@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/snyk-nuget-plugin/-/snyk-nuget-plugin-1.18.1.tgz#5296ee7b3ee96fc8186fa157f71697b804173a60" - integrity sha512-Bq+IzbyewxIrUhgdFaDKS5wCNixERC7QBitKsZGM3uCOr9fJM8rr5qg5SS9UIU7eyeKvzuVO/V1yDzjo1cKvUw== +snyk-nuget-plugin@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/snyk-nuget-plugin/-/snyk-nuget-plugin-1.19.3.tgz#5b4d9a5a61a543810c98bd4e67b9f6b1d95e3c3a" + integrity sha512-KwKoMumwcXVz/DQH80ifXfX7CTnm29bmHJ2fczjCGohxLGb4EKBGQtA3t7K98O7lTISQGgXDxnWIaM9ZXkxPdw== dependencies: - "@snyk/lodash" "4.17.15-patch" debug "^4.1.1" - dotnet-deps-parser "4.10.0" - jszip "3.3.0" + dotnet-deps-parser "5.0.0" + jszip "3.4.0" + lodash "^4.17.20" snyk-paket-parser "1.6.0" tslib "^1.11.2" xml2js "^0.4.17" @@ -7003,13 +6416,13 @@ snyk-paket-parser@1.6.0: dependencies: tslib "^1.9.3" -snyk-php-plugin@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/snyk-php-plugin/-/snyk-php-plugin-1.9.0.tgz#9cbb0439f251325aff7b4cda5f22797e8541df9a" - integrity sha512-uORrEoC47dw0ITZYu5vKqQtmXnbbQs+ZkWeo5bRHGdf10W8e4rNr1S1R4bReiLrSbSisYhVHeFMkdOAiLIPJVQ== +snyk-php-plugin@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/snyk-php-plugin/-/snyk-php-plugin-1.9.2.tgz#282ef733060aab49da23e1fb2d2dd1af8f71f7cd" + integrity sha512-IQcdsQBqqXVRY5DatlI7ASy4flbhtU2V7cr4P2rK9rkFnVHO6LHcitwKXVZa9ocdOmpZDzk7U6iwHJkVFcR6OA== dependencies: - "@snyk/cli-interface" "2.3.2" - "@snyk/composer-lockfile-parser" "1.4.0" + "@snyk/cli-interface" "^2.9.1" + "@snyk/composer-lockfile-parser" "^1.4.1" tslib "1.11.1" snyk-policy@1.14.1: @@ -7094,19 +6507,15 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1: lru-cache "^4.0.0" then-fs "^2.0.0" -snyk@^1.382.0: - version "1.382.0" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.382.0.tgz#5625931dd066fca406f2d35663eb7280d57565bd" - integrity sha512-lZsuJ6HUVNO7ZYDQm6B2gPdcXiRtd8bCQUre8xYMZu1VakKwrCCNsJbxo91z3gK5aYqempjmTe7+qH2CksXZ+Q== +snyk@1.398.0, snyk@^1.398.0: + version "1.398.0" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.398.0.tgz#ae58884704d35fc494697bfa920fb70331ea5c41" + integrity sha512-o3xMTBo7cQSCO87+2smzuV7MXlUwf7tAdfYVZstjr/eKYnLGD4K/AOYkabHWauaTPdiK0LV61rLHYIy6+WybqA== dependencies: - "@snyk/cli-interface" "2.8.1" - "@snyk/dep-graph" "1.19.3" + "@snyk/cli-interface" "2.9.2" + "@snyk/dep-graph" "1.19.4" "@snyk/gemfile" "1.2.0" - "@snyk/graphlib" "2.1.9-patch" - "@snyk/inquirer" "6.2.2-patch" - "@snyk/lodash" "^4.17.15-patch" - "@snyk/ruby-semver" "2.2.0" - "@snyk/snyk-cocoapods-plugin" "2.5.0" + "@snyk/snyk-cocoapods-plugin" "2.5.1" abbrev "^1.1.1" ansi-escapes "3.2.0" chalk "^2.4.2" @@ -7115,22 +6524,25 @@ snyk@^1.382.0: debug "^4.1.1" diff "^4.0.1" glob "^7.1.3" - needle "^2.5.0" + graphlib "^2.1.8" + inquirer "^7.3.3" + lodash "^4.17.20" + needle "2.5.0" open "^7.0.3" os-name "^3.0.0" proxy-agent "^3.1.1" proxy-from-env "^1.0.0" semver "^6.0.0" - snyk-config "3.1.0" - snyk-cpp-plugin "1.2.0" - snyk-docker-plugin "3.17.0" - snyk-go-plugin "1.16.0" - snyk-gradle-plugin "3.5.1" + snyk-config "3.1.1" + snyk-cpp-plugin "1.5.0" + snyk-docker-plugin "3.21.0" + snyk-go-plugin "1.16.2" + snyk-gradle-plugin "3.6.3" snyk-module "3.1.0" - snyk-mvn-plugin "2.19.1" - snyk-nodejs-lockfile-parser "1.27.0" - snyk-nuget-plugin "1.18.1" - snyk-php-plugin "1.9.0" + snyk-mvn-plugin "2.19.4" + snyk-nodejs-lockfile-parser "1.28.1" + snyk-nuget-plugin "1.19.3" + snyk-php-plugin "1.9.2" snyk-policy "1.14.1" snyk-python-plugin "1.17.1" snyk-resolve "1.0.1" @@ -7374,37 +6786,16 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stream-buffers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-array@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" - integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M= - dependencies: - any-promise "^1.1.0" - stream-to-observable@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= -stream-to-promise@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/stream-to-promise/-/stream-to-promise-2.2.0.tgz#b1edb2e1c8cb11289d1b503c08d3f2aef51e650f" - integrity sha1-se2y4cjLESidG1A8CNPyrvUeZQ8= - dependencies: - any-promise "~1.3.0" - end-of-stream "~1.1.0" - stream-to-array "~2.3.0" - streamsearch@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -7424,7 +6815,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0: +"string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -7674,19 +7065,6 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4.4.6: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -7768,13 +7146,6 @@ tmp@0.1.0, tmp@^0.1.0: dependencies: rimraf "^2.6.3" -tmp@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.0.tgz#fdc6a78d2a77bf7e426ca476d8a4f82eefcf648c" - integrity sha512-spsb5g6EiPmteS5TcOAECU3rltCMDMp4VMU2Sb0+WttN4qGobEkMAd+dkr1cubscN08JGNDX765dPbGImbG7MQ== - dependencies: - rimraf "^3.0.0" - tmp@0.2.1, tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -7807,13 +7178,6 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -7893,11 +7257,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -7910,6 +7269,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -7935,11 +7299,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -underscore@^1.7.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" - integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -8134,7 +7493,7 @@ vlq@^1.0.0: resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.0.tgz#8101be90843422954c2b13eb27f2f3122bdcc806" integrity sha512-o3WmXySo+oI5thgqr7Qy8uBkT/v9Zr+sRyrh1lr8aWPUkgDWdWt4Nae2WKBrLsocgE8BuWWD0jLc+VW8LeU+2g== -vscode-languageserver-types@^3.5.0: +vscode-languageserver-types@^3.15.1: version "3.15.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== @@ -8212,13 +7571,6 @@ which@1, which@^1.2.14, which@^1.2.9: dependencies: isexe "^2.0.0" -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -8342,7 +7694,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From fc2fdbe42613a6931fc300f7dc8407b6dfd7b39c Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 16 Sep 2020 15:05:28 +0530 Subject: [PATCH 149/154] fix: handle None value in recipients while sending system notification --- frappe/email/doctype/notification/notification.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 5355d56b35..18ec760c77 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -167,7 +167,10 @@ def get_context(context): subject = frappe.render_template(self.subject, context) attachments = self.get_attachment(doc) + recipients, cc, bcc = self.get_list_of_recipients(doc, context) + if not (recipients or cc or bcc): + return users = recipients + cc + bcc notification_doc = { From 1d2053d915814ad30e2eddc34143e62f0da30fce Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 16 Sep 2020 15:45:02 +0530 Subject: [PATCH 150/154] fix(desk): Hide badge if count is zero --- frappe/public/js/frappe/widgets/shortcut_widget.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/widgets/shortcut_widget.js b/frappe/public/js/frappe/widgets/shortcut_widget.js index a8eead8a3d..a1560f9d00 100644 --- a/frappe/public/js/frappe/widgets/shortcut_widget.js +++ b/frappe/public/js/frappe/widgets/shortcut_widget.js @@ -78,6 +78,7 @@ export default class ShortcutWidget extends Widget { } set_count(count) { + if (!count) return; const get_label = () => { if (this.format) { return this.format.replace(/{}/g, count); From 5f75db43937b37ff2b8e1446a83a559c6cf963f1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 16 Sep 2020 18:22:41 +0530 Subject: [PATCH 151/154] fix: Mute badges if count is zero --- frappe/public/js/frappe/widgets/shortcut_widget.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/frappe/widgets/shortcut_widget.js b/frappe/public/js/frappe/widgets/shortcut_widget.js index a1560f9d00..7174ba0d2a 100644 --- a/frappe/public/js/frappe/widgets/shortcut_widget.js +++ b/frappe/public/js/frappe/widgets/shortcut_widget.js @@ -78,7 +78,6 @@ export default class ShortcutWidget extends Widget { } set_count(count) { - if (!count) return; const get_label = () => { if (this.format) { return this.format.replace(/{}/g, count); @@ -90,11 +89,10 @@ export default class ShortcutWidget extends Widget { const label = get_label(); const buttons = $(`
${label}
`); if (this.color) { - buttons.css("background-color", this.color); - buttons.css( - "color", - frappe.ui.color.get_contrast_color(this.color) - ); + let bg_color = count ? this.color: '#EEEEEE'; + let text_color = count ? frappe.ui.color.get_contrast_color(bg_color): '#8D99A6'; + buttons.css("background-color", bg_color); + buttons.css("color", text_color); } buttons.appendTo(this.action_area); From 97049504af0c506c83ea24dd66ad763626eda706 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 16 Sep 2020 20:09:14 +0530 Subject: [PATCH 152/154] fix: Validation for naming_series field --- frappe/core/doctype/doctype/doctype.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 5c558f3bd6..78d59e8ae4 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -234,6 +234,8 @@ class DocType(Document): if not autoname and self.get("fields", {"fieldname":"naming_series"}): self.autoname = "naming_series:" + elif self.autoname == "naming_series:" and not self.get("fields", {"fieldname":"naming_series"}): + frappe.throw(_("Invalid fieldname '{0}' in autoname").format(self.autoname)) # validate field name if autoname field:fieldname is used # Create unique index on autoname field automatically. From d08fc9e2c152245bd4e0e9822ee8f7b481191666 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Thu, 17 Sep 2020 07:30:48 +0300 Subject: [PATCH 153/154] [Snyk] Fix for 1 vulnerabilities (#11523) The following vulnerabilities are fixed with a Snyk patch: - https://snyk.io/vuln/SNYK-JS-LODASH-567746 --- .snyk | 28 ++++++++++++++++++++++++++++ package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.snyk b/.snyk index f2ff29617a..6c6555a819 100644 --- a/.snyk +++ b/.snyk @@ -71,3 +71,31 @@ patch: patched: '2020-09-15T23:06:41.931Z' - node-sass > gaze > globule > lodash: patched: '2020-09-15T23:06:41.931Z' + - snyk > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-cpp-plugin > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-go-plugin > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-gradle-plugin > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-docker-plugin > snyk-nodejs-lockfile-parser > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-mvn-plugin > @snyk/java-call-graph-builder > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-php-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-gradle-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-mvn-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > @snyk/dep-graph > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' + - snyk > snyk-go-plugin > graphlib > lodash: + patched: '2020-09-16T23:06:38.881Z' diff --git a/package.json b/package.json index 9829356f8e..0ef96ad642 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "qz-tray": "^2.0.8", "redis": "^2.8.0", "showdown": "^1.9.1", - "snyk": "^1.398.0", + "snyk": "^1.398.1", "socket.io": "^2.3.0", "superagent": "^3.8.2", "touch": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index f845904153..dd28ad1a95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6507,10 +6507,10 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1: lru-cache "^4.0.0" then-fs "^2.0.0" -snyk@1.398.0, snyk@^1.398.0: - version "1.398.0" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.398.0.tgz#ae58884704d35fc494697bfa920fb70331ea5c41" - integrity sha512-o3xMTBo7cQSCO87+2smzuV7MXlUwf7tAdfYVZstjr/eKYnLGD4K/AOYkabHWauaTPdiK0LV61rLHYIy6+WybqA== +snyk@1.398.1, snyk@^1.398.1: + version "1.398.1" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.398.1.tgz#19aec8dfffa60e7412e6309117e96b2cfa960355" + integrity sha512-jH24ztdJY8DQlqkd1z8n/JutdOqHtTPccCynM2hfOedW20yAp9c108LFjXvqBEk/EH3YyNmWzyLkkHOySeDkwQ== dependencies: "@snyk/cli-interface" "2.9.2" "@snyk/dep-graph" "1.19.4" From f3132fa301480ea615b70d8b644fe0366b52e243 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Fri, 18 Sep 2020 11:59:34 +0530 Subject: [PATCH 154/154] fix: Always return list from get_list_of_recipients --- frappe/email/doctype/notification/notification.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 18ec760c77..df4b7ad44c 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -169,9 +169,11 @@ def get_context(context): attachments = self.get_attachment(doc) recipients, cc, bcc = self.get_list_of_recipients(doc, context) - if not (recipients or cc or bcc): - return + users = recipients + cc + bcc + + if not users: + return notification_doc = { 'type': 'Alert', @@ -277,8 +279,6 @@ def get_context(context): if self.send_to_all_assignees: recipients = recipients + get_assignees(doc) - if not recipients and not cc and not bcc: - return None, None, None return list(set(recipients)), list(set(cc)), list(set(bcc)) def get_receiver_list(self, doc, context): @@ -428,4 +428,4 @@ def get_assignees(doc): recipients = [d.owner for d in assignees] - return recipients \ No newline at end of file + return recipients