From d9daefc54ddb1ea3ed5d109964acc1a9eed8219c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 27 Oct 2021 13:44:06 +0530 Subject: [PATCH 01/61] fix: Calculate dynamic properties for as_dict If you have a @property in the controller class, this data used to not be accessible anywhere other than directly through the object via `frappe.get_doc`. This fix changes that. --- frappe/model/base_document.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 26a4658c36..232f108615 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -264,6 +264,11 @@ class BaseDocument(object): if isinstance(d[fieldname], list) and df.fieldtype not in table_fields: frappe.throw(_('Value for {0} cannot be a list').format(_(df.label))) + if d[fieldname] == None: + _val = getattr(self, fieldname, None) + if not callable(_val): + d[fieldname] = _val + if convert_dates_to_str and isinstance(d[fieldname], ( datetime.datetime, datetime.date, @@ -307,6 +312,7 @@ class BaseDocument(object): def as_dict(self, no_nulls=False, no_default_fields=False, convert_dates_to_str=False): doc = self.get_valid_dict(convert_dates_to_str=convert_dates_to_str) doc["doctype"] = self.doctype + for df in self.meta.get_table_fields(): children = self.get(df.fieldname) or [] doc[df.fieldname] = [d.as_dict(convert_dates_to_str=convert_dates_to_str, no_nulls=no_nulls, no_default_fields=no_default_fields) for d in children] From 36a0ecde771cda844d0dd95391ac0455d691b303 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 27 Oct 2021 14:10:34 +0530 Subject: [PATCH 02/61] refactor: check_fieldname_conflicts - Don't raise if docfield is read only. This allows for dynamic data fields whose values don't need to be stored in the database - After 1599f422d320122391acacf91a2206ba42165517, it's possible to pass dynamic properties' data to Desk/APIs This makes it possible to show Person.age on Desk/Api if you have their DOB saved in the table --- frappe/core/doctype/doctype/doctype.py | 10 +++++----- frappe/custom/doctype/custom_field/custom_field.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3754288145..e5dc185aef 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1295,10 +1295,9 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): else: raise -def check_fieldname_conflicts(doctype, fieldname): +def check_fieldname_conflicts(docfield): """Checks if fieldname conflicts with methods or properties""" - - doc = frappe.get_doc({"doctype": doctype}) + doc = frappe.get_doc({"doctype": docfield.dt}) available_objects = [x for x in dir(doc) if isinstance(x, str)] property_list = [ x for x in available_objects if isinstance(getattr(type(doc), x, None), property) @@ -1306,9 +1305,10 @@ def check_fieldname_conflicts(doctype, fieldname): method_list = [ x for x in available_objects if x not in property_list and callable(getattr(doc, x)) ] + msg = _("Fieldname {0} conflicting with meta object").format(docfield.fieldname) - if fieldname in method_list + property_list: - frappe.throw(_("Fieldname {0} conflicting with meta object").format(fieldname)) + if docfield.fieldname in method_list + property_list: + frappe.msgprint(msg, raise_exception=not docfield.read_only) def clear_linked_doctype_cache(): frappe.cache().delete_value('linked_doctypes_without_ignore_user_permissions_enabled') diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 8f7b21dd24..2d03bfc0c8 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -65,7 +65,7 @@ class CustomField(Document): if not self.flags.ignore_validate: from frappe.core.doctype.doctype.doctype import check_fieldname_conflicts - check_fieldname_conflicts(self.dt, self.fieldname) + check_fieldname_conflicts(self) def on_update(self): if not frappe.flags.in_setup_wizard: From 6502c4b340fb02e82c00a07a662d1ed9f55519a9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 12 Jan 2022 14:50:27 +0530 Subject: [PATCH 03/61] fix: Don't add property value if NoneType or Falsy --- frappe/model/base_document.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 232f108615..a48a41c303 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -264,9 +264,9 @@ class BaseDocument(object): if isinstance(d[fieldname], list) and df.fieldtype not in table_fields: frappe.throw(_('Value for {0} cannot be a list').format(_(df.label))) - if d[fieldname] == None: + if d[fieldname] is None: _val = getattr(self, fieldname, None) - if not callable(_val): + if _val and not callable(_val): d[fieldname] = _val if convert_dates_to_str and isinstance(d[fieldname], ( From 57f89c8b050873ceeabb5db4260382399c45984e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 13 Jan 2022 12:23:50 +0530 Subject: [PATCH 04/61] test: Add test for dynamic docfield --- frappe/tests/test_document.py | 56 +++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 34a1dd070c..8027bde504 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -1,11 +1,20 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -import os import unittest +from contextlib import contextmanager +from datetime import timedelta +from unittest.mock import patch import frappe -from frappe.utils import cint -from frappe.model.naming import revert_series_if_last, make_autoname, parse_naming_series +from frappe.desk.doctype.note.note import Note +from frappe.model.naming import make_autoname, parse_naming_series, revert_series_if_last +from frappe.utils import cint, now_datetime + + +class CustomTestNote(Note): + @property + def age(self): + return now_datetime() - self.creation class TestDocument(unittest.TestCase): @@ -256,4 +265,41 @@ class TestDocument(unittest.TestCase): def test_limit_for_get(self): doc = frappe.get_doc("DocType", "DocType") # assuming DocType has more that 3 Data fields - self.assertEquals(len(doc.get("fields", filters={"fieldtype": "Data"}, limit=3)), 3) \ No newline at end of file + self.assertEquals(len(doc.get("fields", filters={"fieldtype": "Data"}, limit=3)), 3) + + def test_dynamic_fields(self): + """Read Only Dynamic fields are accessible via API and Form views, whenever .as_dict is invoked + """ + frappe.db.delete("Custom Field", {"dt": "Note", "fieldname":"age"}) + + def patch_note(): + return patch("frappe.controllers", new={frappe.local.site: {'Note': CustomTestNote}}) + + @contextmanager + def customize_note(): + custom_field = frappe.get_doc({ + "doctype": "Custom Field", + "dt": "Note", + "fieldname": "age", + "fieldtype": "Data", + "read_only": True, + }) + + try: + yield custom_field.insert(ignore_if_duplicate=True) + finally: + custom_field.delete() + + with patch_note(): + doc = frappe.get_last_doc("Note") + self.assertIsInstance(doc, CustomTestNote) + self.assertIsInstance(doc.age, timedelta) + self.assertIsNone(doc.as_dict().get("age")) + self.assertIsNone(doc.get_valid_dict().get("age")) + + with customize_note(), patch_note(): + doc = frappe.get_last_doc("Note") + self.assertIsInstance(doc, CustomTestNote) + self.assertIsInstance(doc.age, timedelta) + self.assertIsInstance(doc.as_dict().get("age"), timedelta) + self.assertIsInstance(doc.get_valid_dict().get("age"), timedelta) From 3eb08b0efc40fbb8fea8c394b921558b5ecdc286 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 12:18:33 +0530 Subject: [PATCH 05/61] fix: Added tabs for different sections --- frappe/desk/doctype/workspace/workspace.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 211029dfcf..960ae215e7 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -41,7 +41,7 @@ "collapsible": 1, "collapsible_depends_on": "charts", "fieldname": "section_break_2", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Dashboards" }, { @@ -79,14 +79,14 @@ "collapsible": 1, "collapsible_depends_on": "shortcuts", "fieldname": "section_break_15", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Shortcuts" }, { "collapsible": 1, "collapsible_depends_on": "links", "fieldname": "section_break_18", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Link Cards" }, { @@ -153,13 +153,13 @@ }, { "fieldname": "roles_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Roles" } ], "in_create": 1, "links": [], - "modified": "2021-12-15 19:33:00.805265", + "modified": "2022-01-27 12:06:13.111743", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", @@ -189,5 +189,6 @@ } ], "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file From daf9c7d4cb1422ff17f8e374a94f651e57ff4c3b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 12:19:16 +0530 Subject: [PATCH 06/61] fix: changed Save Customization to Save --- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index e28225904d..2a02cd33ce 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -376,7 +376,7 @@ frappe.views.Workspace = class Workspace { this.clear_page_actions(); page.is_editable && this.page.set_primary_action( - __("Save Customizations"), + __("Save"), () => { this.clear_page_actions(); this.save_page(page).then((saved) => { From 990b00a8084c9919ef0a9732a934e2681089e3d3 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 12:19:53 +0530 Subject: [PATCH 07/61] fix: added shadow on main card --- frappe/public/scss/desk/desktop.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 549ed6eee9..b471feb878 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -850,10 +850,16 @@ body { } } + .layout-main-section-wrapper { + margin-top: -5px; + padding-top: 5px; + } + .layout-main-section { background-color: var(--fg-color); - padding: var(--padding-sm); + box-shadow: var(--card-shadow); border-radius: var(--border-radius-lg); + padding: var(--padding-sm); } .block-menu-item-icon svg{ From de4997b2d381e91e4a08c087656a8c398066a4ef Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 14:42:38 +0530 Subject: [PATCH 08/61] fix: Close notification when any notification item is clicked --- .../public/js/frappe/ui/notifications/notifications.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 6fa8303574..35b1bed14e 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -283,12 +283,13 @@ class NotificationsView extends BaseNotificationsView { e.stopImmediatePropagation(); this.mark_as_read(field.name, item_html); }); - - item_html.on('click', () => { - this.mark_as_read(field.name, item_html); - }); } + item_html.on('click', () => { + !field.read && this.mark_as_read(field.name, item_html); + this.notifications_icon.trigger('click') + }); + return item_html; } From 8e21d18ee09bf74f2e2e48557ae5072632fb9349 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 14:59:24 +0530 Subject: [PATCH 09/61] test: fixed workspace ui test --- cypress/integration/workspace.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js index 9d6eeaff64..fbff451305 100644 --- a/cypress/integration/workspace.js +++ b/cypress/integration/workspace.js @@ -23,7 +23,7 @@ context('Workspace 2.0', () => { // check if sidebar item is added in pubic section cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0'); - cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click(); + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); cy.wait(300); cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0'); @@ -67,7 +67,7 @@ context('Workspace 2.0', () => { cy.get('.ce-block:last .dropdown-item').contains('Expand').click(); cy.get(".ce-block:last").should('have.class', 'col-xs-12'); - cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click(); + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); }); it('Delete Private Page', () => { @@ -80,7 +80,7 @@ context('Workspace 2.0', () => { .find('.dropdown-item[title="Delete Workspace"]').click({force: true}); cy.wait(300); cy.get('.modal-footer > .standard-actions > .btn-modal-primary:visible').first().click(); - cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click(); + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); cy.get('.codex-editor__redactor .ce-block'); cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('not.exist'); }); From 2500d2c5873b98f5ce9acdb25b80d37990801b11 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 15:00:57 +0530 Subject: [PATCH 10/61] fix: sider fix --- frappe/public/js/frappe/ui/notifications/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 35b1bed14e..bf1cee2cbf 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -287,7 +287,7 @@ class NotificationsView extends BaseNotificationsView { item_html.on('click', () => { !field.read && this.mark_as_read(field.name, item_html); - this.notifications_icon.trigger('click') + this.notifications_icon.trigger('click'); }); return item_html; From b5ebf062cb9ca52f0f6f21f7dd37cf0f8d562f4f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 27 Jan 2022 15:21:47 +0530 Subject: [PATCH 11/61] refactor: replace Link field with Select field --- frappe/desk/doctype/form_tour/form_tour.js | 111 ++++++++++-------- frappe/desk/doctype/form_tour/form_tour.py | 65 +++------- .../form_tour_step/form_tour_step.json | 49 ++------ frappe/public/js/frappe/form/form_tour.js | 2 +- 4 files changed, 90 insertions(+), 137 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js index 6a7c736fac..2e149c2c47 100644 --- a/frappe/desk/doctype/form_tour/form_tour.js +++ b/frappe/desk/doctype/form_tour/form_tour.js @@ -53,73 +53,84 @@ frappe.ui.form.on('Form Tour', { }; }); - frm.set_query("field", "steps", function() { - return { - query: "frappe.desk.doctype.form_tour.form_tour.get_docfield_list", - filters: { - doctype: frm.doc.reference_doctype, - hidden: 0 - } - }; - }); - - frm.set_query("parent_field", "steps", function() { - return { - query: "frappe.desk.doctype.form_tour.form_tour.get_docfield_list", - filters: { - doctype: frm.doc.reference_doctype, - fieldtype: "Table", - hidden: 0, - } - }; - }); - frm.trigger('reference_doctype'); }, reference_doctype(frm) { if (!frm.doc.reference_doctype) return; - frappe.db.get_list('DocField', { - filters: { - parent: frm.doc.reference_doctype, - parenttype: 'DocType', - fieldtype: 'Table' - }, - fields: ['options'] - }).then(res => { - if (Array.isArray(res)) { - frm.child_doctypes = res.map(r => r.options); - } + frappe.model.with_doctype(frm.doc.reference_doctype, () => { + let fields = frappe.meta + .get_docfields(frm.doc.reference_doctype, null, { + hidden: 0 + }) + .map(df => ({ + label: `${df.label || 'No Label'} (${df.fieldtype})`, + value: df.fieldname + })); + + frm.fields_dict.steps.grid.update_docfield_property( + "fieldname", + "options", + [""].concat(fields) + ); + + let parent_fields = frappe.meta + .get_docfields(frm.doc.reference_doctype, null, { + fieldtype: "Table", + hidden: 0 + }) + .map(df => ({ + label: `${df.label || 'No Label'} (${df.fieldtype})`, + value: df.fieldname + })); + + frm.fields_dict.steps.grid.update_docfield_property( + "parent_fieldname", + "options", + [""].concat(parent_fields) + ); }); } }); frappe.ui.form.on('Form Tour Step', { - parent_field(frm, cdt, cdn) { + form_render(frm, cdt, cdn) { + if (locals[cdt][cdn].is_table_field) { + frm.trigger('parent_fieldname', cdt, cdn); + } + }, + parent_fieldname(frm, cdt, cdn) { const child_row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, 'field', ''); - const field_control = get_child_field("steps", cdn, "field"); - field_control.get_query = function() { - return { - query: "frappe.desk.doctype.form_tour.form_tour.get_docfield_list", - filters: { - doctype: child_row.child_doctype, + + const parent_fieldname_df = frappe + .get_meta(frm.doc.reference_doctype) + .fields.find(df => df.fieldname == child_row.parent_fieldname); + + frappe.model.with_doctype(parent_fieldname_df.options, () => { + let fields = frappe.meta + .get_docfields(parent_fieldname_df.options, null, { hidden: 0 - } - }; - }; + }) + .map(df => ({ + label: `${df.label || 'No Label'} (${df.fieldtype})`, + value: df.fieldname + })); + + frm.fields_dict.steps.grid.update_docfield_property( + "fieldname", + "options", + [""].concat(fields) + ); + + if (child_row.fieldname) { + frappe.model.set_value(cdt, cdn, 'fieldname', child_row.fieldname); + } + }); } }); -function get_child_field(child_table, child_name, fieldname) { - // gets the field from grid row form - const grid = cur_frm.fields_dict[child_table].grid; - const grid_row = grid.grid_rows_by_docname[child_name]; - return grid_row.grid_form.fields_dict[fieldname]; -} - async function check_if_single(doctype) { const { message } = await frappe.db.get_value('DocType', doctype, 'issingle'); return message.issingle || 0; diff --git a/frappe/desk/doctype/form_tour/form_tour.py b/frappe/desk/doctype/form_tour/form_tour.py index 82d47224dd..6248b43e62 100644 --- a/frappe/desk/doctype/form_tour/form_tour.py +++ b/frappe/desk/doctype/form_tour/form_tour.py @@ -5,58 +5,23 @@ import frappe from frappe.model.document import Document from frappe.modules.export_file import export_to_files + class FormTour(Document): - def before_insert(self): - if not self.is_standard: - return + def before_save(self): + meta = frappe.get_meta(self.reference_doctype) + for step in self.steps: + if step.is_table_field and step.parent_fieldname: + parent_field_df = meta.get_field(step.parent_fieldname) + step.child_doctype = parent_field_df.options - # while syncing, set proper docfield reference - for d in self.steps: - if not frappe.db.exists('DocField', d.field): - d.field = frappe.db.get_value('DocField', { - 'fieldname': d.fieldname, 'parent': self.reference_doctype, 'fieldtype': d.fieldtype - }, "name") - - if d.is_table_field and not frappe.db.exists('DocField', d.parent_field): - d.parent_field = frappe.db.get_value('DocField', { - 'fieldname': d.parent_fieldname, 'parent': self.reference_doctype, 'fieldtype': 'Table' - }, "name") + field_df = frappe.get_meta(step.child_doctype).get_field(step.fieldname) + step.label = field_df.label + step.fieldtype = field_df.fieldtype + else: + field_df = meta.get_field(step.fieldname) + step.label = field_df.label + step.fieldtype = field_df.fieldtype def on_update(self): if frappe.conf.developer_mode and self.is_standard: - export_to_files([['Form Tour', self.name]], self.module) - - def before_export(self, doc): - for d in doc.steps: - d.field = "" - d.parent_field = "" - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_docfield_list(doctype, txt, searchfield, start, page_len, filters): - or_filters = [ - ['fieldname', 'like', '%' + txt + '%'], - ['label', 'like', '%' + txt + '%'], - ['fieldtype', 'like', '%' + txt + '%'] - ] - - parent_doctype = filters.get('doctype') - fieldtype = filters.get('fieldtype') - if not fieldtype: - excluded_fieldtypes = ['Column Break'] - excluded_fieldtypes += filters.get('excluded_fieldtypes', []) - fieldtype_filter = ['not in', excluded_fieldtypes] - else: - fieldtype_filter = fieldtype - - docfields = frappe.get_all( - doctype, - fields=["name as value", "label", "fieldtype"], - filters={'parent': parent_doctype, 'fieldtype': fieldtype_filter}, - or_filters=or_filters, - limit_start=start, - limit_page_length=page_len, - order_by="idx", - as_list=1, - ) - return docfields + export_to_files([["Form Tour", self.name]], self.module) diff --git a/frappe/desk/doctype/form_tour_step/form_tour_step.json b/frappe/desk/doctype/form_tour_step/form_tour_step.json index 3b6c91a208..7eb6eab223 100644 --- a/frappe/desk/doctype/form_tour_step/form_tour_step.json +++ b/frappe/desk/doctype/form_tour_step/form_tour_step.json @@ -6,19 +6,17 @@ "field_order": [ "is_table_field", "section_break_2", - "parent_field", - "field", + "parent_fieldname", + "fieldname", "title", "description", "column_break_2", "position", "label", + "fieldtype", "has_next_condition", "next_step_condition", "section_break_13", - "fieldname", - "parent_fieldname", - "fieldtype", "child_doctype" ], "fields": [ @@ -38,23 +36,13 @@ "reqd": 1 }, { - "depends_on": "eval: (!doc.is_table_field || (doc.is_table_field && doc.parent_field))", - "fieldname": "field", - "fieldtype": "Link", - "label": "Field", - "options": "DocField", + "depends_on": "eval: (!doc.is_table_field || (doc.is_table_field && doc.parent_fieldname))", + "fieldname": "fieldname", + "fieldtype": "Select", + "label": "Fieldname", "reqd": 1 }, { - "fetch_from": "field.fieldname", - "fieldname": "fieldname", - "fieldtype": "Data", - "hidden": 1, - "label": "Fieldname", - "read_only": 1 - }, - { - "fetch_from": "field.label", "fieldname": "label", "fieldtype": "Data", "in_list_view": 1, @@ -88,10 +76,8 @@ }, { "default": "0", - "fetch_from": "field.fieldtype", "fieldname": "fieldtype", "fieldtype": "Data", - "hidden": 1, "label": "Fieldtype", "read_only": 1 }, @@ -105,14 +91,6 @@ "fieldname": "section_break_2", "fieldtype": "Section Break" }, - { - "depends_on": "is_table_field", - "fieldname": "parent_field", - "fieldtype": "Link", - "label": "Parent Field", - "mandatory_depends_on": "is_table_field", - "options": "DocField" - }, { "fieldname": "section_break_13", "fieldtype": "Section Break", @@ -120,7 +98,6 @@ "label": "Hidden Fields" }, { - "fetch_from": "parent_field.options", "fieldname": "child_doctype", "fieldtype": "Data", "hidden": 1, @@ -128,18 +105,17 @@ "read_only": 1 }, { - "fetch_from": "parent_field.fieldname", + "depends_on": "is_table_field", "fieldname": "parent_fieldname", - "fieldtype": "Data", - "hidden": 1, - "label": "Parent Fieldname", - "read_only": 1 + "fieldtype": "Select", + "label": "Parent Field", + "mandatory_depends_on": "is_table_field" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-06 20:52:21.076972", + "modified": "2022-01-27 15:18:36.481801", "modified_by": "Administrator", "module": "Desk", "name": "Form Tour Step", @@ -147,5 +123,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index 7fefb59ac6..2bb888e17c 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -151,7 +151,7 @@ frappe.ui.form.FormTour = class FormTour { const curr_step = step_info; const next_step = this.tour.steps[curr_step.idx]; - const is_next_field_in_curr_table = next_step.parent_field == curr_step.field; + const is_next_field_in_curr_table = next_step.parent_fieldname == curr_step.fieldname; if (!is_next_field_in_curr_table) return; From b3b1c3302c094ceaf0b1ff140a0d8ae3b45c3251 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 27 Jan 2022 19:18:39 +0530 Subject: [PATCH 12/61] chore: renamed tab break fields --- frappe/desk/doctype/workspace/workspace.json | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 960ae215e7..fa8b81f5fd 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -20,13 +20,13 @@ "hide_custom", "public", "content", - "section_break_2", + "tab_break_2", "charts", - "section_break_15", + "tab_break_15", "shortcuts", - "section_break_18", + "tab_break_18", "links", - "roles_section", + "roles_tab", "roles" ], "fields": [ @@ -40,7 +40,7 @@ { "collapsible": 1, "collapsible_depends_on": "charts", - "fieldname": "section_break_2", + "fieldname": "tab_break_2", "fieldtype": "Tab Break", "label": "Dashboards" }, @@ -78,14 +78,14 @@ { "collapsible": 1, "collapsible_depends_on": "shortcuts", - "fieldname": "section_break_15", + "fieldname": "tab_break_15", "fieldtype": "Tab Break", "label": "Shortcuts" }, { "collapsible": 1, "collapsible_depends_on": "links", - "fieldname": "section_break_18", + "fieldname": "tab_break_18", "fieldtype": "Tab Break", "label": "Link Cards" }, @@ -152,7 +152,7 @@ "options": "Has Role" }, { - "fieldname": "roles_section", + "fieldname": "roles_tab", "fieldtype": "Tab Break", "label": "Roles" } @@ -189,6 +189,5 @@ } ], "sort_field": "modified", - "sort_order": "DESC", - "states": [] + "sort_order": "DESC" } \ No newline at end of file From 9e3e3f804a20b047d8c9f3918cd0004fbc9ad92c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 Jan 2022 22:38:48 +0530 Subject: [PATCH 13/61] feat: Virtual DocField Add virtual docfield through which you can make callable properties for the controller --- frappe/core/doctype/docfield/docfield.json | 9 ++++++++- .../custom/doctype/customize_form/customize_form.py | 3 ++- .../customize_form_field/customize_form_field.json | 9 ++++++++- frappe/model/base_document.py | 13 +++++++------ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 26ddce7d35..6eb8cf347f 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -17,6 +17,7 @@ "hide_days", "hide_seconds", "reqd", + "is_virtual", "search_index", "column_break_18", "options", @@ -534,13 +535,19 @@ "fieldname": "show_dashboard", "fieldtype": "Check", "label": "Show Dashboard" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Virtual" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-01-03 11:56:19.812863", + "modified": "2022-01-27 21:22:20.529072", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 1593ed49a5..83fb529b8b 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -558,7 +558,8 @@ docfield_properties = { 'allow_in_quick_entry': 'Check', 'hide_border': 'Check', 'hide_days': 'Check', - 'hide_seconds': 'Check' + 'hide_seconds': 'Check', + 'is_virtual': 'Check', } doctype_link_properties = { diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index a545cd9fe1..4351e76609 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -14,6 +14,7 @@ "non_negative", "reqd", "unique", + "is_virtual", "in_list_view", "in_standard_filter", "in_global_search", @@ -115,6 +116,12 @@ "fieldtype": "Check", "label": "Unique" }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" + }, { "default": "0", "fieldname": "in_list_view", @@ -436,7 +443,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-01-03 14:50:32.035768", + "modified": "2022-01-27 21:45:22.349776", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 56c41f2001..408aa622ca 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -244,7 +244,13 @@ class BaseDocument(object): continue df = self.meta.get_field(fieldname) - if df: + + if df and df.get("is_virtual"): + if d[fieldname] is None: + _val = getattr(self, fieldname, None) + if _val and not callable(_val): + d[fieldname] = _val + elif df: if df.fieldtype=="Check": d[fieldname] = 1 if cint(d[fieldname]) else 0 @@ -264,11 +270,6 @@ class BaseDocument(object): if isinstance(d[fieldname], list) and df.fieldtype not in table_fields: frappe.throw(_('Value for {0} cannot be a list').format(_(df.label))) - if d[fieldname] is None: - _val = getattr(self, fieldname, None) - if _val and not callable(_val): - d[fieldname] = _val - if convert_dates_to_str and isinstance(d[fieldname], ( datetime.datetime, datetime.date, From 17e970d94789fd495c5cf7a5ad5c3b02b4bcc331 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 Jan 2022 22:40:41 +0530 Subject: [PATCH 14/61] fix: Add is_virtual to Custom Field The diff for this commit is too large because the format was changed. But essentially, only "Is Virtual" field was added. This commit was meant to be a part of the preceeding one. It was kept separate only because the diffs too large :') --- .../doctype/custom_field/custom_field.json | 922 +++++++++--------- 1 file changed, 466 insertions(+), 456 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json index 235f11aad8..e51dfda14b 100644 --- a/frappe/custom/doctype/custom_field/custom_field.json +++ b/frappe/custom/doctype/custom_field/custom_field.json @@ -1,458 +1,468 @@ { - "actions": [], - "allow_import": 1, - "creation": "2013-01-10 16:34:01", - "description": "Adds a custom field to a DocType", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "dt", - "module", - "label", - "label_help", - "fieldname", - "insert_after", - "length", - "column_break_6", - "fieldtype", - "precision", - "hide_seconds", - "hide_days", - "options", - "fetch_from", - "fetch_if_empty", - "options_help", - "section_break_11", - "collapsible", - "collapsible_depends_on", - "default", - "depends_on", - "mandatory_depends_on", - "read_only_depends_on", - "properties", - "non_negative", - "reqd", - "unique", - "read_only", - "ignore_user_permissions", - "hidden", - "print_hide", - "print_hide_if_no_value", - "print_width", - "no_copy", - "allow_on_submit", - "in_list_view", - "in_standard_filter", - "in_global_search", - "in_preview", - "bold", - "report_hide", - "search_index", - "allow_in_quick_entry", - "ignore_xss_filter", - "translatable", - "hide_border", - "description", - "permlevel", - "width", - "columns" - ], - "fields": [{ - "bold": 1, - "fieldname": "dt", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Document", - "oldfieldname": "dt", - "oldfieldtype": "Link", - "options": "DocType", - "reqd": 1, - "search_index": 1 - }, - { - "bold": 1, - "fieldname": "label", - "fieldtype": "Data", - "in_filter": 1, - "label": "Label", - "no_copy": 1, - "oldfieldname": "label", - "oldfieldtype": "Data" - }, - { - "fieldname": "label_help", - "fieldtype": "HTML", - "label": "Label Help", - "oldfieldtype": "HTML" - }, - { - "fieldname": "fieldname", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Fieldname", - "no_copy": 1, - "oldfieldname": "fieldname", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "description": "Select the label after which you want to insert new field.", - "fieldname": "insert_after", - "fieldtype": "Select", - "label": "Insert After", - "no_copy": 1, - "oldfieldname": "insert_after", - "oldfieldtype": "Select" - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "bold": 1, - "default": "Data", - "fieldname": "fieldtype", - "fieldtype": "Select", - "in_filter": 1, - "in_list_view": 1, - "label": "Field Type", - "oldfieldname": "fieldtype", - "oldfieldtype": "Select", - "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature\nTab Break", - "reqd": 1 - }, - { - "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", - "description": "Set non-standard precision for a Float or Currency field", - "fieldname": "precision", - "fieldtype": "Select", - "label": "Precision", - "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" - }, - { - "fieldname": "options", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Options", - "oldfieldname": "options", - "oldfieldtype": "Text" - }, - { - "fieldname": "fetch_from", - "fieldtype": "Small Text", - "label": "Fetch From" - }, - { - "default": "0", - "description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", - "fieldname": "fetch_if_empty", - "fieldtype": "Check", - "label": "Fetch If Empty" - }, - { - "fieldname": "options_help", - "fieldtype": "HTML", - "label": "Options Help", - "oldfieldtype": "HTML" - }, - { - "fieldname": "section_break_11", - "fieldtype": "Section Break" - }, - { - "default": "0", - "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fieldname": "collapsible", - "fieldtype": "Check", - "label": "Collapsible" - }, - { - "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fieldname": "collapsible_depends_on", - "fieldtype": "Code", - "label": "Collapsible Depends On" - }, - { - "fieldname": "default", - "fieldtype": "Text", - "label": "Default Value", - "oldfieldname": "default", - "oldfieldtype": "Text" - }, - { - "fieldname": "depends_on", - "fieldtype": "Code", - "label": "Depends On", - "length": 255 - }, - { - "fieldname": "description", - "fieldtype": "Text", - "label": "Field Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_width": "300px", - "width": "300px" - }, - { - "default": "0", - "fieldname": "permlevel", - "fieldtype": "Int", - "label": "Permission Level", - "oldfieldname": "permlevel", - "oldfieldtype": "Int" - }, - { - "fieldname": "width", - "fieldtype": "Data", - "label": "Width", - "oldfieldname": "width", - "oldfieldtype": "Data" - }, - { - "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", - "fieldname": "columns", - "fieldtype": "Int", - "label": "Columns" - }, - { - "fieldname": "properties", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "default": "0", - "fieldname": "reqd", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Is Mandatory Field", - "oldfieldname": "reqd", - "oldfieldtype": "Check" - }, - { - "default": "0", - "fieldname": "unique", - "fieldtype": "Check", - "label": "Unique" - }, - { - "default": "0", - "fieldname": "read_only", - "fieldtype": "Check", - "label": "Read Only" - }, - { - "default": "0", - "depends_on": "eval:doc.fieldtype===\"Link\"", - "fieldname": "ignore_user_permissions", - "fieldtype": "Check", - "label": "Ignore User Permissions" - }, - { - "default": "0", - "fieldname": "hidden", - "fieldtype": "Check", - "label": "Hidden" - }, - { - "default": "0", - "fieldname": "print_hide", - "fieldtype": "Check", - "label": "Print Hide", - "oldfieldname": "print_hide", - "oldfieldtype": "Check" - }, - { - "default": "0", - "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", - "fieldname": "print_hide_if_no_value", - "fieldtype": "Check", - "label": "Print Hide If No Value" - }, - { - "fieldname": "print_width", - "fieldtype": "Data", - "hidden": 1, - "label": "Print Width", - "no_copy": 1, - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "no_copy", - "fieldtype": "Check", - "label": "No Copy", - "oldfieldname": "no_copy", - "oldfieldtype": "Check" - }, - { - "default": "0", - "fieldname": "allow_on_submit", - "fieldtype": "Check", - "label": "Allow on Submit", - "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check" - }, - { - "default": "0", - "fieldname": "in_list_view", - "fieldtype": "Check", - "label": "In List View" - }, - { - "default": "0", - "fieldname": "in_standard_filter", - "fieldtype": "Check", - "label": "In Standard Filter" - }, - { - "default": "0", - "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", - "fieldname": "in_global_search", - "fieldtype": "Check", - "label": "In Global Search" - }, - { - "default": "0", - "fieldname": "bold", - "fieldtype": "Check", - "label": "Bold" - }, - { - "default": "0", - "fieldname": "report_hide", - "fieldtype": "Check", - "label": "Report Hide", - "oldfieldname": "report_hide", - "oldfieldtype": "Check" - }, - { - "default": "0", - "fieldname": "search_index", - "fieldtype": "Check", - "hidden": 1, - "label": "Index", - "no_copy": 1, - "print_hide": 1 - }, - { - "default": "0", - "description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", - "fieldname": "ignore_xss_filter", - "fieldtype": "Check", - "label": "Ignore XSS Filter" - }, - { - "default": "1", - "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", - "fieldname": "translatable", - "fieldtype": "Check", - "label": "Translatable" - }, - { - "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", - "fieldname": "length", - "fieldtype": "Int", - "label": "Length" - }, - { - "fieldname": "mandatory_depends_on", - "fieldtype": "Code", - "label": "Mandatory Depends On", - "length": 255 - }, - { - "fieldname": "read_only_depends_on", - "fieldtype": "Code", - "label": "Read Only Depends On", - "length": 255 - }, - { - "default": "0", - "fieldname": "allow_in_quick_entry", - "fieldtype": "Check", - "label": "Allow in Quick Entry" - }, - { - "default": "0", - "depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", - "fieldname": "in_preview", - "fieldtype": "Check", - "label": "In Preview" - }, - { - "default": "0", - "depends_on": "eval:doc.fieldtype=='Duration'", - "fieldname": "hide_seconds", - "fieldtype": "Check", - "label": "Hide Seconds" - }, - { - "default": "0", - "depends_on": "eval:doc.fieldtype=='Duration'", - "fieldname": "hide_days", - "fieldtype": "Check", - "label": "Hide Days" - }, - { - "default": "0", - "depends_on": "eval:doc.fieldtype=='Section Break'", - "fieldname": "hide_border", - "fieldtype": "Check", - "label": "Hide Border" - }, - { - "default": "0", - "depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)", - "fieldname": "non_negative", - "fieldtype": "Check", - "label": "Non Negative" - }, - { - "fieldname": "module", - "fieldtype": "Link", - "label": "Module (for export)", - "options": "Module Def" - } - ], - "icon": "fa fa-glass", - "idx": 1, - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-09-04 12:45:23.810120", - "modified_by": "Administrator", - "module": "Custom", - "name": "Custom Field", - "owner": "Administrator", - "permissions": [{ - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Administrator", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "search_fields": "dt,label,fieldtype,options", - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 + "actions": [], + "allow_import": 1, + "creation": "2013-01-10 16:34:01", + "description": "Adds a custom field to a DocType", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "dt", + "module", + "label", + "label_help", + "fieldname", + "insert_after", + "length", + "column_break_6", + "fieldtype", + "precision", + "hide_seconds", + "hide_days", + "options", + "fetch_from", + "fetch_if_empty", + "options_help", + "section_break_11", + "collapsible", + "collapsible_depends_on", + "default", + "depends_on", + "mandatory_depends_on", + "read_only_depends_on", + "properties", + "non_negative", + "reqd", + "unique", + "is_virtual", + "read_only", + "ignore_user_permissions", + "hidden", + "print_hide", + "print_hide_if_no_value", + "print_width", + "no_copy", + "allow_on_submit", + "in_list_view", + "in_standard_filter", + "in_global_search", + "in_preview", + "bold", + "report_hide", + "search_index", + "allow_in_quick_entry", + "ignore_xss_filter", + "translatable", + "hide_border", + "description", + "permlevel", + "width", + "columns" + ], + "fields": [ + { + "bold": 1, + "fieldname": "dt", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Document", + "oldfieldname": "dt", + "oldfieldtype": "Link", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "bold": 1, + "fieldname": "label", + "fieldtype": "Data", + "in_filter": 1, + "label": "Label", + "no_copy": 1, + "oldfieldname": "label", + "oldfieldtype": "Data" + }, + { + "fieldname": "label_help", + "fieldtype": "HTML", + "label": "Label Help", + "oldfieldtype": "HTML" + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fieldname", + "no_copy": 1, + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "description": "Select the label after which you want to insert new field.", + "fieldname": "insert_after", + "fieldtype": "Select", + "label": "Insert After", + "no_copy": 1, + "oldfieldname": "insert_after", + "oldfieldtype": "Select" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "default": "Data", + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_filter": 1, + "in_list_view": 1, + "label": "Field Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature\nTab Break", + "reqd": 1 + }, + { + "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", + "description": "Set non-standard precision for a Float or Currency field", + "fieldname": "precision", + "fieldtype": "Select", + "label": "Precision", + "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" + }, + { + "fieldname": "options", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text" + }, + { + "fieldname": "fetch_from", + "fieldtype": "Small Text", + "label": "Fetch From" + }, + { + "default": "0", + "description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", + "fieldname": "fetch_if_empty", + "fieldtype": "Check", + "label": "Fetch If Empty" + }, + { + "fieldname": "options_help", + "fieldtype": "HTML", + "label": "Options Help", + "oldfieldtype": "HTML" + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible", + "fieldtype": "Check", + "label": "Collapsible" + }, + { + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible_depends_on", + "fieldtype": "Code", + "label": "Collapsible Depends On" + }, + { + "fieldname": "default", + "fieldtype": "Text", + "label": "Default Value", + "oldfieldname": "default", + "oldfieldtype": "Text" + }, + { + "fieldname": "depends_on", + "fieldtype": "Code", + "label": "Depends On", + "length": 255 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Field Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "label": "Permission Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int" + }, + { + "fieldname": "width", + "fieldtype": "Data", + "label": "Width", + "oldfieldname": "width", + "oldfieldtype": "Data" + }, + { + "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", + "fieldname": "columns", + "fieldtype": "Int", + "label": "Columns" + }, + { + "fieldname": "properties", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "default": "0", + "fieldname": "reqd", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Mandatory Field", + "oldfieldname": "reqd", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "unique", + "fieldtype": "Check", + "label": "Unique" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype===\"Link\"", + "fieldname": "ignore_user_permissions", + "fieldtype": "Check", + "label": "Ignore User Permissions" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "default": "0", + "fieldname": "print_hide", + "fieldtype": "Check", + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", + "fieldname": "print_hide_if_no_value", + "fieldtype": "Check", + "label": "Print Hide If No Value" + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "hidden": 1, + "label": "Print Width", + "no_copy": 1, + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy", + "oldfieldname": "no_copy", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View" + }, + { + "default": "0", + "fieldname": "in_standard_filter", + "fieldtype": "Check", + "label": "In Standard Filter" + }, + { + "default": "0", + "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", + "fieldname": "in_global_search", + "fieldtype": "Check", + "label": "In Global Search" + }, + { + "default": "0", + "fieldname": "bold", + "fieldtype": "Check", + "label": "Bold" + }, + { + "default": "0", + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "search_index", + "fieldtype": "Check", + "hidden": 1, + "label": "Index", + "no_copy": 1, + "print_hide": 1 + }, + { + "default": "0", + "description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", + "fieldname": "ignore_xss_filter", + "fieldtype": "Check", + "label": "Ignore XSS Filter" + }, + { + "default": "1", + "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", + "fieldname": "translatable", + "fieldtype": "Check", + "label": "Translatable" + }, + { + "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", + "fieldname": "length", + "fieldtype": "Int", + "label": "Length" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "length": 255 + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "length": 255 + }, + { + "default": "0", + "fieldname": "allow_in_quick_entry", + "fieldtype": "Check", + "label": "Allow in Quick Entry" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", + "fieldname": "in_preview", + "fieldtype": "Check", + "label": "In Preview" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_seconds", + "fieldtype": "Check", + "label": "Hide Seconds" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_days", + "fieldtype": "Check", + "label": "Hide Days" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Section Break'", + "fieldname": "hide_border", + "fieldtype": "Check", + "label": "Hide Border" + }, + { + "default": "0", + "depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)", + "fieldname": "non_negative", + "fieldtype": "Check", + "label": "Non Negative" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module (for export)", + "options": "Module Def" + } + ], + "icon": "fa fa-glass", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-01-27 21:47:01.065556", + "modified_by": "Administrator", + "module": "Custom", + "name": "Custom Field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "dt,label,fieldtype,options", + "sort_field": "modified", + "sort_order": "ASC", + "states": [], + "track_changes": 1 } \ No newline at end of file From aba467307f8bc456960128360fe7acd75f5fa66d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 27 Jan 2022 20:14:51 +0100 Subject: [PATCH 15/61] fix: translate default msgprint title in backend --- frappe/__init__.py | 5 ++--- frappe/public/js/frappe/ui/messages.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index c6cbfead43..f1e9987f17 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -356,7 +356,7 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, as_list=False, response JSON and shown in a pop-up / modal. :param msg: Message. - :param title: [optional] Message title. + :param title: [optional] Message title. Default: "Message". :param raise_exception: [optional] Raise given exception and show message. :param as_table: [optional] If `msg` is a list of lists, render as HTML table. :param as_list: [optional] If `msg` is a list, render as un-ordered list. @@ -393,8 +393,7 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, as_list=False, if flags.print_messages and out.message: print(f"Message: {strip_html_tags(out.message)}") - if title: - out.title = title + out.title = title or _("Message", context="Default title of the message dialog") if not indicator and raise_exception: indicator = 'red' diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index ac0c01c406..f0d03f0743 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -233,7 +233,7 @@ frappe.msgprint = function(msg, title, is_minimizable) { if(data.title || !msg_exists) { // set title only if it is explicitly given // and no existing title exists - frappe.msg_dialog.set_title(data.title || __('Message')); + frappe.msg_dialog.set_title(data.title || __('Message', null, 'Default title of the message dialog')); } // show / hide indicator From 9ca768a32c3d69ff5855a72e3176806755346f67 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 Jan 2022 12:12:30 +0530 Subject: [PATCH 16/61] fix(test): Update test_virtual_fields --- frappe/tests/test_document.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 8027bde504..bc00a4677a 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -267,8 +267,8 @@ class TestDocument(unittest.TestCase): # assuming DocType has more that 3 Data fields self.assertEquals(len(doc.get("fields", filters={"fieldtype": "Data"}, limit=3)), 3) - def test_dynamic_fields(self): - """Read Only Dynamic fields are accessible via API and Form views, whenever .as_dict is invoked + def test_virtual_fields(self): + """Virtual fields are accessible via API and Form views, whenever .as_dict is invoked """ frappe.db.delete("Custom Field", {"dt": "Note", "fieldname":"age"}) @@ -283,6 +283,7 @@ class TestDocument(unittest.TestCase): "fieldname": "age", "fieldtype": "Data", "read_only": True, + "is_virtual": True, }) try: From fd1006e619116a6d107397d595e85d62c86f46b1 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 28 Jan 2022 12:16:32 +0100 Subject: [PATCH 17/61] test: compare message, ignore title --- frappe/core/doctype/file/test_file.py | 2 +- frappe/core/doctype/user/test_user.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index 2c1042e104..6841f2fbd0 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -419,7 +419,7 @@ class TestFile(unittest.TestCase): test_file.reload() test_file.file_url = frappe.utils.get_url('unknown.jpg') test_file.make_thumbnail(suffix="xs") - self.assertEqual(json.loads(frappe.message_log[0]), {"message": f"File '{frappe.utils.get_url('unknown.jpg')}' not found"}) + self.assertEqual(json.loads(frappe.message_log[0]).get("message"), f"File '{frappe.utils.get_url('unknown.jpg')}' not found") self.assertEquals(test_file.thumbnail_url, None) def test_file_unzip(self): diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index d1291acfc4..581b44816a 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -355,7 +355,7 @@ class TestUser(unittest.TestCase): test_user.reload() self.assertEqual(update_password(new_password, key=test_user.reset_password_key), "/") update_password(old_password, old_password=new_password) - self.assertEqual(json.loads(frappe.message_log[0]), {"message": "Password reset instructions have been sent to your email"}) + self.assertEqual(json.loads(frappe.message_log[0]).get("message"), "Password reset instructions have been sent to your email") sendmail.assert_called_once() self.assertEqual(sendmail.call_args[1]["recipients"], "test2@example.com") From a5613720ed991d6bbab391592095e4742dcea422 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 28 Jan 2022 17:32:50 +0530 Subject: [PATCH 18/61] refactor: use set_fields_as_options --- frappe/desk/doctype/form_tour/form_tour.js | 53 ++++++++-------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js index 2e149c2c47..19fe623f60 100644 --- a/frappe/desk/doctype/form_tour/form_tour.js +++ b/frappe/desk/doctype/form_tour/form_tour.js @@ -59,36 +59,27 @@ frappe.ui.form.on('Form Tour', { reference_doctype(frm) { if (!frm.doc.reference_doctype) return; - frappe.model.with_doctype(frm.doc.reference_doctype, () => { - let fields = frappe.meta - .get_docfields(frm.doc.reference_doctype, null, { - hidden: 0 - }) - .map(df => ({ - label: `${df.label || 'No Label'} (${df.fieldtype})`, - value: df.fieldname - })); - + frm.set_fields_as_options( + "fieldname", + frm.doc.reference_doctype, + df => !df.hidden + ).then(options => { frm.fields_dict.steps.grid.update_docfield_property( "fieldname", "options", - [""].concat(fields) + [""].concat(options) ); + }); - let parent_fields = frappe.meta - .get_docfields(frm.doc.reference_doctype, null, { - fieldtype: "Table", - hidden: 0 - }) - .map(df => ({ - label: `${df.label || 'No Label'} (${df.fieldtype})`, - value: df.fieldname - })); - + frm.set_fields_as_options( + 'parent_fieldname', + frm.doc.reference_doctype, + (df) => df.fieldtype == "Table" && !df.hidden, + ).then(options => { frm.fields_dict.steps.grid.update_docfield_property( "parent_fieldname", "options", - [""].concat(parent_fields) + [""].concat(options) ); }); @@ -108,22 +99,16 @@ frappe.ui.form.on('Form Tour Step', { .get_meta(frm.doc.reference_doctype) .fields.find(df => df.fieldname == child_row.parent_fieldname); - frappe.model.with_doctype(parent_fieldname_df.options, () => { - let fields = frappe.meta - .get_docfields(parent_fieldname_df.options, null, { - hidden: 0 - }) - .map(df => ({ - label: `${df.label || 'No Label'} (${df.fieldtype})`, - value: df.fieldname - })); - + frm.set_fields_as_options( + 'fieldname', + parent_fieldname_df.options, + (df) => !df.hidden, + ).then(options => { frm.fields_dict.steps.grid.update_docfield_property( "fieldname", "options", - [""].concat(fields) + [""].concat(options) ); - if (child_row.fieldname) { frappe.model.set_value(cdt, cdn, 'fieldname', child_row.fieldname); } From 0a77059a0d463f1799059290dfb86b4253327dc6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 Jan 2022 18:47:29 +0530 Subject: [PATCH 19/61] fix: Skip raising only if virtual docfield while checking for conflicts --- frappe/core/doctype/doctype/doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index e5dc185aef..804796abfb 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1308,7 +1308,7 @@ def check_fieldname_conflicts(docfield): msg = _("Fieldname {0} conflicting with meta object").format(docfield.fieldname) if docfield.fieldname in method_list + property_list: - frappe.msgprint(msg, raise_exception=not docfield.read_only) + frappe.msgprint(msg, raise_exception=not docfield.is_virtual) def clear_linked_doctype_cache(): frappe.cache().delete_value('linked_doctypes_without_ignore_user_permissions_enabled') From 62643e835568d7799165409afa620b6d2c6519b2 Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Sat, 29 Jan 2022 11:45:51 +0530 Subject: [PATCH 20/61] fix: filter html tags in comments --- frappe/templates/includes/comments/comments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index 99afb580d8..8d485423bf 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -6,6 +6,7 @@ from frappe.website.utils import clear_cache from frappe.rate_limiter import rate_limit from frappe.utils import add_to_date, now from frappe.website.doctype.blog_settings.blog_settings import get_comment_limit +from frappe.utils.html_utils import clean_html from frappe import _ @@ -29,7 +30,7 @@ def add_comment(comment, comment_email, comment_by, reference_doctype, reference return False comment = doc.add_comment( - text=comment, + text=clean_html(comment), comment_email=comment_email, comment_by=comment_by) From 105f8d8bc7868ae59922a895ad071d0927fadc9c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 31 Jan 2022 11:24:08 +0530 Subject: [PATCH 21/61] fix: form tour ui test --- frappe/tests/ui_test_helpers.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index b299df522c..67c58a1154 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -148,9 +148,6 @@ def create_form_tour(): if frappe.db.exists('Form Tour', {'name': 'Test Form Tour'}): return - def get_docfield_name(filters): - return frappe.db.get_value('DocField', filters, "name") - tour = frappe.get_doc({ 'doctype': 'Form Tour', 'title': 'Test Form Tour', @@ -161,7 +158,6 @@ def create_form_tour(): "description": "Test Description 1", "has_next_condition": 1, "next_step_condition": "eval: doc.first_name", - "field": get_docfield_name({'parent': 'Contact', 'fieldname': 'first_name'}), "fieldname": "first_name", "fieldtype": "Data" },{ @@ -169,21 +165,18 @@ def create_form_tour(): "description": "Test Description 2", "has_next_condition": 1, "next_step_condition": "eval: doc.last_name", - "field": get_docfield_name({'parent': 'Contact', 'fieldname': 'last_name'}), "fieldname": "last_name", "fieldtype": "Data" },{ "title": "Test Title 3", "description": "Test Description 3", - "field": get_docfield_name({'parent': 'Contact', 'fieldname': 'phone_nos'}), "fieldname": "phone_nos", "fieldtype": "Table" },{ "title": "Test Title 4", "description": "Test Description 4", "is_table_field": 1, - "parent_field": get_docfield_name({'parent': 'Contact', 'fieldname': 'phone_nos'}), - "field": get_docfield_name({'parent': 'Contact Phone', 'fieldname': 'phone'}), + "parent_fieldname": "phone_nos", "next_step_condition": "eval: doc.phone", "has_next_condition": 1, "fieldname": "phone", From 1a1d0849d0857e21777a032aabdb1ddbeedd6dd0 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 31 Jan 2022 15:03:07 +0530 Subject: [PATCH 22/61] fix: check if any changes made before saving --- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 2a02cd33ce..5e1a659cbd 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -1158,7 +1158,7 @@ frappe.views.Workspace = class Workspace { item.data.card_name !== 'Custom Reports') ); - if (page.content == JSON.stringify(blocks)) { + if (page.content == JSON.stringify(blocks) && Object.keys(new_widgets).length === 0) { this.setup_customization_buttons(page); frappe.show_alert({ message: __("No changes made on the page"), indicator: "warning" }); return false; From 20f35b8b6c4897c7201605c62f761b34b1634168 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 31 Jan 2022 15:04:17 +0530 Subject: [PATCH 23/61] fix: Make card links mandatory --- .../public/js/frappe/widgets/widget_dialog.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js index 01d41a0cf9..1486f94945 100644 --- a/frappe/public/js/frappe/widgets/widget_dialog.js +++ b/frappe/public/js/frappe/widgets/widget_dialog.js @@ -154,7 +154,7 @@ class CardDialog extends WidgetDialog { { fieldtype: "Data", fieldname: "label", - label: "Label", + label: "Label" }, { fieldname: 'links', @@ -174,7 +174,7 @@ class CardDialog extends WidgetDialog { }, { fieldname: "icon", - fieldtype: "Data", + fieldtype: "Icon", label: "Icon" }, { @@ -182,6 +182,7 @@ class CardDialog extends WidgetDialog { fieldtype: "Select", in_list_view: 1, label: "Link Type", + reqd: 1, options: ["DocType", "Page", "Report"] }, { @@ -189,9 +190,9 @@ class CardDialog extends WidgetDialog { fieldtype: "Dynamic Link", in_list_view: 1, label: "Link To", + reqd: 1, get_options: (df) => { return df.doc.link_type; - } }, { @@ -227,6 +228,31 @@ class CardDialog extends WidgetDialog { } process_data(data) { + data.links.map((item, idx) => { + let message = ''; + let row = idx+1; + + if (!item.link_type) { + message = "Following fields have missing values:

    " + message += `
  • Link Type in Row ${row}
  • `; + } + + if (!item.link_to) { + message += `
  • Link To in Row ${row}
  • `; + } + + if (message) { + message += "
"; + frappe.throw({ + message:__(message), + title: __("Missing Values Required"), + indicator: 'orange' + }); + } + + item.label = item.label ? item.label : item.link_to; + }); + data.label = data.label ? data.label : data.chart_name; return data; } From c30e59eb8d96a4ab70ed453d236b679b30a2bdbc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 31 Jan 2022 15:09:05 +0530 Subject: [PATCH 24/61] fix: sider fix --- frappe/public/js/frappe/widgets/widget_dialog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js index 1486f94945..d1ba75227b 100644 --- a/frappe/public/js/frappe/widgets/widget_dialog.js +++ b/frappe/public/js/frappe/widgets/widget_dialog.js @@ -233,7 +233,7 @@ class CardDialog extends WidgetDialog { let row = idx+1; if (!item.link_type) { - message = "Following fields have missing values:

    " + message = "Following fields have missing values:

      "; message += `
    • Link Type in Row ${row}
    • `; } @@ -244,7 +244,7 @@ class CardDialog extends WidgetDialog { if (message) { message += "
    "; frappe.throw({ - message:__(message), + message: __(message), title: __("Missing Values Required"), indicator: 'orange' }); From 988771a8c3dc9ed847d1bca192b99f64518be208 Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Tue, 1 Feb 2022 10:32:27 +0530 Subject: [PATCH 25/61] test(blog comment): added filter for blog comments --- frappe/core/doctype/comment/test_comment.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index cd9af498aa..33672a7dea 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -70,6 +70,19 @@ class TestComment(unittest.TestCase): reference_name = test_blog.name ))), 0) + # test for filtering html and css injection elements + frappe.db.delete("Comment", {"reference_doctype": "Blog Post"}) + + frappe.form_dict.comment = 'Comment' + frappe.form_dict.comment_by = 'hacker' + + add_comment() + + self.assertEqual(frappe.get_all('Comment', fields = ['content'], filters = dict( + reference_doctype = test_blog.doctype, + reference_name = test_blog.name + ))[0]['content'], 'Comment') + test_blog.delete() From 111abfc1f4f7117c0c4f63f2d237d8091a753c37 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 26 Jan 2021 06:16:19 +0530 Subject: [PATCH 26/61] refactor!: Remove dead methods from PropertySetter controller --- .../property_setter/property_setter.py | 55 ++++--------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/frappe/custom/doctype/property_setter/property_setter.py b/frappe/custom/doctype/property_setter/property_setter.py index 0a65aa6f5d..a86cf5efd6 100644 --- a/frappe/custom/doctype/property_setter/property_setter.py +++ b/frappe/custom/doctype/property_setter/property_setter.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import frappe @@ -18,53 +18,19 @@ class PropertySetter(Document): def validate(self): self.validate_fieldtype_change() + if self.is_new(): delete_property_setter(self.doc_type, self.property, self.field_name, self.row_name) - - # clear cache frappe.clear_cache(doctype = self.doc_type) def validate_fieldtype_change(self): - if self.field_name in not_allowed_fieldtype_change and \ - self.property == 'fieldtype': - frappe.throw(_("Field type cannot be changed for {0}").format(self.field_name)) - - def get_property_list(self, dt): - return frappe.db.get_all('DocField', - fields=['fieldname', 'label', 'fieldtype'], - filters={ - 'parent': dt, - 'fieldtype': ['not in', ('Section Break', 'Column Break', 'Tab Break', 'HTML', 'Read Only', 'Fold') + frappe.model.table_fields], - 'fieldname': ['!=', ''] - }, - order_by='label asc', - as_dict=1 - ) - - def get_setup_data(self): - return { - 'doctypes': frappe.get_all("DocType", pluck="name"), - 'dt_properties': self.get_property_list('DocType'), - 'df_properties': self.get_property_list('DocField') - } - - def get_field_ids(self): - return frappe.db.get_values( - "DocField", - filters={"parent": self.doc_type}, - fieldname=["name", "fieldtype", "label", "fieldname"], - as_dict=True, - ) - - def get_defaults(self): - if not self.field_name: - return frappe.get_all("DocType", filters={"name": self.doc_type}, fields="*")[0] - else: - return frappe.db.get_values( - "DocField", - filters={"fieldname": self.field_name, "parent": self.doc_type}, - fieldname="*", - )[0] + if ( + self.property == 'fieldtype' + and self.field_name in not_allowed_fieldtype_change + ): + frappe.throw( + _("Field type cannot be changed for {0}").format(self.field_name) + ) def on_update(self): if frappe.flags.in_patch: @@ -74,6 +40,7 @@ class PropertySetter(Document): from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype validate_fields_for_doctype(self.doc_type) + def make_property_setter(doctype, fieldname, property, value, property_type, for_doctype = False, validate_fields_for_doctype=True): # WARNING: Ignores Permissions @@ -91,6 +58,7 @@ def make_property_setter(doctype, fieldname, property, value, property_type, for property_setter.insert() return property_setter + def delete_property_setter(doc_type, property, field_name=None, row_name=None): """delete other property setters on this, if this is new""" filters = dict(doc_type=doc_type, property=property) @@ -100,4 +68,3 @@ def delete_property_setter(doc_type, property, field_name=None, row_name=None): filters["row_name"] = row_name frappe.db.delete('Property Setter', filters) - From 66b09afda6575b6d7a81aec35adba2d4629aa71b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 1 Feb 2022 17:48:02 +0530 Subject: [PATCH 27/61] fix: Don't create DB column for Virtual DocFields --- frappe/database/schema.py | 5 ++++- frappe/model/meta.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 9a6dd502dc..96e5c6be47 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -67,7 +67,7 @@ class DBTable: """ get columns from docfields and custom fields """ - fields = self.meta.get_fieldnames_with_value(True) + fields = self.meta.get_fieldnames_with_value(with_field_meta=True) # optional fields like _comments if not self.meta.get('istable'): @@ -85,6 +85,9 @@ class DBTable: }) for field in fields: + if field.get("is_virtual"): + continue + self.columns[field.get('fieldname')] = DbColumn( self, field.get('fieldname'), diff --git a/frappe/model/meta.py b/frappe/model/meta.py index a483f3f2d6..b2f7841abe 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -442,9 +442,16 @@ class Meta(Document): self.permissions = [Document(d) for d in custom_perms] def get_fieldnames_with_value(self, with_field_meta=False): - return [df if with_field_meta else df.fieldname \ - for df in self.fields if df.fieldtype not in no_value_fields] + def is_value_field(docfield): + return not ( + docfield.is_virtual + or docfield.fieldtype in no_value_fields + ) + if with_field_meta: + return [df for df in self.fields if is_value_field(df)] + + return [df.fieldname for df in self.fields if is_value_field(df)] def get_fields_to_check_permissions(self, user_permission_doctypes): fields = self.get("fields", { From b9395a4d714b179030d5dfc623f02717f39ce4d4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 28 Jan 2021 10:16:19 +0530 Subject: [PATCH 28/61] fix: Handle Virtual DocFields in Desk * Show as "Read Only" in forms/controls/webforms etc * Exclude from filtering options in views * Exclude from quick entry --- .../js/frappe/form/controls/base_control.js | 5 ++++- frappe/public/js/frappe/form/quick_entry.js | 2 +- frappe/public/js/frappe/form/script_manager.js | 15 ++++++++++++--- frappe/public/js/frappe/list/list_view.js | 3 ++- .../public/js/frappe/views/reports/report_view.js | 3 ++- frappe/public/js/frappe/views/treeview.js | 2 +- frappe/website/doctype/web_form/web_form.js | 2 +- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index ce871c50cb..4ee52d16b8 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -39,6 +39,9 @@ frappe.ui.form.Control = class BaseControl { if (this.df.get_status) { return this.df.get_status(this); } + if (this.df.is_virtual) { + return "Read"; + } if ((!this.doctype && !this.docname) || this.df.parenttype === 'Web Form' || this.df.is_web_form) { // like in case of a dialog box @@ -52,7 +55,7 @@ frappe.ui.form.Control = class BaseControl { if(explain) console.log("By Hidden Dependency: None"); // eslint-disable-line no-console return "None"; - } else if (cint(this.df.read_only)) { + } else if (cint(this.df.read_only || this.df.is_virtual)) { // eslint-disable-next-line if (explain) console.log("By Read Only: Read"); // eslint-disable-line no-console return "Read"; diff --git a/frappe/public/js/frappe/form/quick_entry.js b/frappe/public/js/frappe/form/quick_entry.js index e412b1dec8..86523d7088 100644 --- a/frappe/public/js/frappe/form/quick_entry.js +++ b/frappe/public/js/frappe/form/quick_entry.js @@ -55,7 +55,7 @@ frappe.ui.form.QuickEntryForm = class QuickEntryForm { // prepare a list of mandatory, bold and allow in quick entry fields this.mandatory = fields.filter(df => { - return ((df.reqd || df.bold || df.allow_in_quick_entry) && !df.read_only); + return ((df.reqd || df.bold || df.allow_in_quick_entry) && !df.read_only && !df.is_virtual); }); } diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index d1732ee702..678a2b552f 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -192,9 +192,18 @@ frappe.ui.form.ScriptManager = class ScriptManager { } function setup_add_fetch(df) { - if ((['Data', 'Read Only', 'Text', 'Small Text', 'Currency', 'Check', - 'Text Editor', 'Code', 'Link', 'Float', 'Int', 'Date', 'Select', 'Duration'].includes(df.fieldtype) || df.read_only==1) - && df.fetch_from && df.fetch_from.indexOf(".")!=-1) { + let is_read_only_field = ( + ['Data', 'Read Only', 'Text', 'Small Text', 'Currency', 'Check', 'Text Editor', + 'Code', 'Link', 'Float', 'Int', 'Date', 'Select', 'Duration'].includes(df.fieldtype) + || df.read_only == 1 + || df.is_virtual == 1 + ) + + if ( + is_read_only_field + && df.fetch_from + && df.fetch_from.indexOf(".") != -1 + ) { var parts = df.fetch_from.split("."); me.frm.add_fetch(parts[0], parts[1], df.fieldname, df.parent); } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 3cde04313f..64960e0b09 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1672,7 +1672,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { frappe.model.is_value_type(field_doc) && field_doc.fieldtype !== "Read Only" && !field_doc.hidden && - !field_doc.read_only + !field_doc.read_only && + !field_doc.is_virtual ); }; diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index f5d9f3e110..a380d5574a 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -644,6 +644,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { // not a cancelled doc && data.docstatus !== 2 && !df.read_only + && !df.is_virtual && !df.hidden // not a standard field i.e., owner, modified_by, etc. && !frappe.model.std_fields_list.includes(df.fieldname)) @@ -1025,7 +1026,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { title += ` (${__(doctype)})`; } - const editable = frappe.model.is_non_std_field(fieldname) && !docfield.read_only; + const editable = frappe.model.is_non_std_field(fieldname) && !docfield.read_only && !docfield.is_virtual; const align = (() => { const is_numeric = frappe.model.is_numeric_field(docfield); diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js index 7179e4ab56..d5c6fb5e80 100644 --- a/frappe/public/js/frappe/views/treeview.js +++ b/frappe/public/js/frappe/views/treeview.js @@ -343,7 +343,7 @@ frappe.views.TreeView = class TreeView { this.ignore_fields = this.opts.ignore_fields || []; var mandatory_fields = $.map(me.opts.meta.fields, function(d) { - return (d.reqd || d.bold && !d.read_only) ? d : null }); + return (d.reqd || d.bold && !d.read_only && !!d.is_virtual) ? d : null }); var opts_field_names = this.fields.map(function(d) { return d.fieldname diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index d69d21c64d..1f27b350be 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -60,7 +60,7 @@ frappe.ui.form.on("Web Form", { options: field.options, reqd: field.reqd, default: field.default, - read_only: field.read_only, + read_only: field.read_only || field.is_virtual, depends_on: field.depends_on, mandatory_depends_on: field.mandatory_depends_on, read_only_depends_on: field.read_only_depends_on, From a0436250a6c8537f733d9cfad8e988f98c5bc0ac Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 1 Feb 2022 18:07:58 +0530 Subject: [PATCH 29/61] fix: Don't validate fieldtype changes for Virtual DocFields --- frappe/custom/doctype/custom_field/custom_field.py | 2 +- frappe/custom/doctype/customize_form/customize_form.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 2d03bfc0c8..cb1ea2c54d 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -54,7 +54,7 @@ class CustomField(Document): old_fieldtype = self.db_get('fieldtype') is_fieldtype_changed = (not self.is_new()) and (old_fieldtype != self.fieldtype) - if is_fieldtype_changed and not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype): + if not self.is_virtual and is_fieldtype_changed and not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype): frappe.throw(_("Fieldtype cannot be changed from {0} to {1}").format(old_fieldtype, self.fieldtype)) if not self.fieldname: diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 83fb529b8b..2ccfa87544 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -418,6 +418,9 @@ class CustomizeForm(Document): return property_value def validate_fieldtype_change(self, df, old_value, new_value): + if df.is_virtual: + return + allowed = self.allow_fieldtype_change(old_value, new_value) if allowed: old_value_length = cint(frappe.db.type_map.get(old_value)[1]) @@ -430,7 +433,8 @@ class CustomizeForm(Document): self.validate_fieldtype_length() else: self.flags.update_db = True - if not allowed: + + else: frappe.throw(_("Fieldtype cannot be changed from {0} to {1} in row {2}").format(old_value, new_value, df.idx)) def validate_fieldtype_length(self): From 5c4905dd375910828a3111b5d4cb000c341b9e9d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 1 Feb 2022 18:08:27 +0530 Subject: [PATCH 30/61] feat(minor): Allow expressions in options for DocFields This means you don't have to write a custom controller or change backend code to use Virtual DocFields! Write it in the options column for the virtual field in Customize Form Added doc and safe globals (from safe_exec) into the evaluation namespace --- frappe/model/base_document.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 408aa622ca..d0d33b9f5c 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -1,15 +1,14 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -import frappe import datetime + +import frappe from frappe import _ -from frappe.model import default_fields, table_fields +from frappe.model import default_fields, display_fieldtypes, table_fields from frappe.model.naming import set_new_name from frappe.model.utils.link_count import notify_link_count from frappe.modules import load_doctype_module -from frappe.model import display_fieldtypes -from frappe.utils import (cint, flt, now, cstr, strip_html, - sanitize_html, sanitize_email, cast_fieldtype) +from frappe.utils import cast_fieldtype, cint, cstr, flt, now, sanitize_html, strip_html from frappe.utils.html_utils import unescape_html max_positive_value = { @@ -246,10 +245,16 @@ class BaseDocument(object): df = self.meta.get_field(fieldname) if df and df.get("is_virtual"): + from frappe.utils.safe_exec import get_safe_globals + if d[fieldname] is None: - _val = getattr(self, fieldname, None) - if _val and not callable(_val): - d[fieldname] = _val + if df.get("options"): + # d[fieldname] = frappe.safe_eval(df.get("options"), {**get_safe_globals, "doc": self}) + d[fieldname] = frappe.safe_eval(df.get("options"), get_safe_globals(), {"doc": self}) + else: + _val = getattr(self, fieldname, None) + if _val and not callable(_val): + d[fieldname] = _val elif df: if df.fieldtype=="Check": d[fieldname] = 1 if cint(d[fieldname]) else 0 From 4d00579667d2fee370af78c320554f5b20056f46 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 1 Feb 2022 18:38:31 +0530 Subject: [PATCH 31/61] fix: Skip data field validations for virtual field --- frappe/core/doctype/doctype/doctype.py | 3 +++ frappe/model/base_document.py | 7 +++++-- frappe/model/meta.py | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 804796abfb..f0bc96b110 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1050,6 +1050,9 @@ def validate_fields(meta): field.fetch_from = field.fetch_from.strip('\n').strip() def validate_data_field_type(docfield): + if docfield.get("is_virtual"): + return + if docfield.fieldtype == "Data" and not (docfield.oldfieldtype and docfield.oldfieldtype != "Data"): if docfield.options and (docfield.options not in data_field_options): df_str = frappe.bold(_(docfield.label)) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index d0d33b9f5c..4c85885401 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -249,8 +249,11 @@ class BaseDocument(object): if d[fieldname] is None: if df.get("options"): - # d[fieldname] = frappe.safe_eval(df.get("options"), {**get_safe_globals, "doc": self}) - d[fieldname] = frappe.safe_eval(df.get("options"), get_safe_globals(), {"doc": self}) + d[fieldname] = frappe.safe_eval( + code=df.get("options"), + eval_globals=get_safe_globals(), + eval_locals={"doc": self}, + ) else: _val = getattr(self, fieldname, None) if _val and not callable(_val): diff --git a/frappe/model/meta.py b/frappe/model/meta.py index b2f7841abe..7156287c59 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -444,7 +444,7 @@ class Meta(Document): def get_fieldnames_with_value(self, with_field_meta=False): def is_value_field(docfield): return not ( - docfield.is_virtual + docfield.get("is_virtual") or docfield.fieldtype in no_value_fields ) From 8165cd2802d17475eb8887322ac2628236854fd5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 1 Feb 2022 18:39:44 +0530 Subject: [PATCH 32/61] test: Add test for docfield.options virtual df usage --- frappe/tests/test_document.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index bc00a4677a..a0c44c5c72 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -276,7 +276,8 @@ class TestDocument(unittest.TestCase): return patch("frappe.controllers", new={frappe.local.site: {'Note': CustomTestNote}}) @contextmanager - def customize_note(): + def customize_note(with_options=False): + options = "frappe.utils.now_datetime() - doc.creation" if with_options else "" custom_field = frappe.get_doc({ "doctype": "Custom Field", "dt": "Note", @@ -284,6 +285,7 @@ class TestDocument(unittest.TestCase): "fieldtype": "Data", "read_only": True, "is_virtual": True, + "options": options, }) try: @@ -304,3 +306,9 @@ class TestDocument(unittest.TestCase): self.assertIsInstance(doc.age, timedelta) self.assertIsInstance(doc.as_dict().get("age"), timedelta) self.assertIsInstance(doc.get_valid_dict().get("age"), timedelta) + + with customize_note(with_options=True): + doc = frappe.get_last_doc("Note") + self.assertIsInstance(doc, Note) + self.assertIsInstance(doc.as_dict().get("age"), timedelta) + self.assertIsInstance(doc.get_valid_dict().get("age"), timedelta) From c118334f2269879de95f62a1954bcc744f79a4ab Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 1 Feb 2022 19:17:02 +0530 Subject: [PATCH 33/61] fix: attribute error in `validate_links_table_fieldnames` --- frappe/core/doctype/doctype/doctype.py | 28 ++++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3754288145..1bc94fd0bf 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -755,29 +755,31 @@ def validate_series(dt, autoname=None, name=None): def validate_links_table_fieldnames(meta): """Validate fieldnames in Links table""" - if frappe.flags.in_patch: return - if frappe.flags.in_fixtures: return - if not meta.links: return + if not meta.links or frappe.flags.in_patch or frappe.flags.in_fixtures: + return + fieldnames = tuple(field.fieldname for field in meta.fields) for index, link in enumerate(meta.links): link_meta = frappe.get_meta(link.link_doctype) if not link_meta.get_field(link.link_fieldname): message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) - if link.is_child_table and not meta.get_field(link.table_fieldname): + if not link.is_child_table: + continue + + if not link.parent_doctype: + message = _("Document Links Row #{0}: Parent DocType is mandatory for internal links").format(index+1) + frappe.throw(message, frappe.ValidationError, _("Parent Missing")) + + if not link.table_fieldname: + message = _("Document Links Row #{0}: Table Fieldname is mandatory for internal links").format(index+1) + frappe.throw(message, frappe.ValidationError, _("Table Fieldname Missing")) + + if link.table_fieldname not in fieldnames: message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.table_fieldname), frappe.bold(meta.name)) frappe.throw(message, frappe.ValidationError, _("Invalid Table Fieldname")) - if link.is_child_table: - if not link.parent_doctype: - message = _("Document Links Row #{0}: Parent DocType is mandatory for internal links").format(index+1) - frappe.throw(message, frappe.ValidationError, _("Parent Missing")) - - if not link.table_fieldname: - message = _("Document Links Row #{0}: Table Fieldname is mandatory for internal links").format(index+1) - frappe.throw(message, frappe.ValidationError, _("Table Fieldname Missing")) - def validate_fields_for_doctype(doctype): meta = frappe.get_meta(doctype, cached=False) validate_links_table_fieldnames(meta) From dce336f660600cae7761d2a263b9d0fc3fb9cacf Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Wed, 2 Feb 2022 11:21:06 +0530 Subject: [PATCH 34/61] fix: added regex for alerts --- frappe/public/js/frappe/utils/common.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/utils/common.js b/frappe/public/js/frappe/utils/common.js index b324cecd39..1f3558b367 100644 --- a/frappe/public/js/frappe/utils/common.js +++ b/frappe/public/js/frappe/utils/common.js @@ -259,8 +259,16 @@ frappe.utils.xss_sanitise = function (string, options) { '/': '/' }; const REGEX_SCRIPT = /)<[^<]*)*<\/script>/gi; // used in jQuery 1.7.2 src/ajax.js Line 14 + const REGEX_ALERT = /confirm\(.*\)|alert\(.*\)|prompt\(.*\)/gi; // captures alert, confirm, prompt options = Object.assign({}, DEFAULT_OPTIONS, options); // don't deep copy, immutable beauty. + // Rule 3 - TODO: Check event handlers? + // script and alert should be checked first or else it will be escaped + if (options.strategies.includes('js')) { + sanitised = sanitised.replace(REGEX_SCRIPT, ""); + sanitised = sanitised.replace(REGEX_ALERT, ""); + } + // Rule 1 if (options.strategies.includes('html')) { for (let char in HTML_ESCAPE_MAP) { @@ -270,11 +278,6 @@ frappe.utils.xss_sanitise = function (string, options) { } } - // Rule 3 - TODO: Check event handlers? - if (options.strategies.includes('js')) { - sanitised = sanitised.replace(REGEX_SCRIPT, ""); - } - return sanitised; } From 6edb15aa6b190764c98ae5be3fa4c10241f265ed Mon Sep 17 00:00:00 2001 From: hrwx Date: Sun, 6 Feb 2022 22:02:52 +0000 Subject: [PATCH 35/61] fix: add try except for splitting email --- frappe/core/doctype/communication/communication.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 1ab07d92e4..140107b42a 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -449,8 +449,12 @@ def get_email_without_link(email): if not frappe.get_all("Email Account", filters={"enable_automatic_linking": 1}): return email - email_id = email.split("@")[0].split("+")[0] - email_host = email.split("@")[1] + try: + _email = email.split("@") + email_id = _email[0].split("+")[0] + email_host = _email[1] + except IndexError: + return email return "{0}@{1}".format(email_id, email_host) From a3043d8621e13478bb55bcc2699660ac17f22182 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 7 Feb 2022 14:49:16 +0530 Subject: [PATCH 36/61] fix: append chart options before render --- frappe/public/js/frappe/form/dashboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index df4dbf09e7..6e3dd3eb0b 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -549,14 +549,14 @@ frappe.ui.form.Dashboard = class FormDashboard { render_graph(args) { this.chart_area.show(); this.chart_area.body.empty(); - $.extend({ + $.extend(args, { type: 'line', colors: ['green'], truncateLegends: 1, axisOptions: { shortenYAxisNumbers: 1 } - }, args); + }); this.show(); this.chart = new frappe.Chart('.form-graph', args); From 2b12fa880c3900c25f8e8dda5384233714e2e63b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 8 Feb 2022 17:02:48 +0530 Subject: [PATCH 37/61] fix: UI fix for card block --- frappe/public/js/frappe/widgets/base_widget.js | 2 +- frappe/public/scss/desk/desktop.scss | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index aabb3526b0..dbcacbf50e 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -100,7 +100,7 @@ export default class Widget { let title = max_chars ? frappe.ellipsis(base, max_chars) : base; if (this.icon) { - let icon = frappe.utils.icon(this.icon); + let icon = frappe.utils.icon(this.icon, "md"); this.title_field[0].innerHTML = `${icon} ${title}`; } else { this.title_field[0].innerHTML = `${title}`; diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index b471feb878..435dafde87 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -154,7 +154,7 @@ body { svg { flex: none; - margin-right: 6px; + margin-right: var(--margin-sm); box-shadow: none; } } @@ -560,6 +560,8 @@ body { } &.links-widget-box { + padding: 16px 7px; + .link-item { display: flex; text-decoration: none; @@ -601,6 +603,8 @@ body { .indicator-pill { margin-right: var(--margin-sm); + height: 20px; + padding: 3px 8px; } } } From c12c8eb31444476ae6831b5709db5c23f6e7fef3 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 8 Feb 2022 19:28:08 +0530 Subject: [PATCH 38/61] style: made links card block pixel perfect --- .../public/js/frappe/widgets/base_widget.js | 2 +- frappe/public/scss/desk/desktop.scss | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index dbcacbf50e..45d4926904 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -100,7 +100,7 @@ export default class Widget { let title = max_chars ? frappe.ellipsis(base, max_chars) : base; if (this.icon) { - let icon = frappe.utils.icon(this.icon, "md"); + let icon = frappe.utils.icon(this.icon, "lg"); this.title_field[0].innerHTML = `${icon} ${title}`; } else { this.title_field[0].innerHTML = `${title}`; diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 435dafde87..fd9ee05f27 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -154,7 +154,8 @@ body { svg { flex: none; - margin-right: var(--margin-sm); + margin-right: 6px; + margin-left: -2px; box-shadow: none; } } @@ -560,23 +561,29 @@ body { } &.links-widget-box { - padding: 16px 7px; + padding: 18px 12px; .link-item { display: flex; text-decoration: none; + font-size: var(--text-md); color: var(--text-color); - padding: var(--padding-xs); - margin-left: -5px; + padding: 4px; + margin-left: -4px; + margin-bottom: 4px; border-radius: var(--border-radius-md); cursor: pointer; &:hover { - background-color: var(--bg-color); + background-color: var(--fg-hover-color); + + .indicator-pill { + background-color: var(--fg-color); + } } &:first-child { - margin-top: 15px; + margin-top: 18px; } &:last-child { From 951ad3f8441c3129259a05e59a1e9c0aeac6e0ea Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 6 Feb 2022 02:17:35 +0530 Subject: [PATCH 39/61] chore(deps): bump werkzeug to latest version --- frappe/app.py | 21 ---------- frappe/desk/page/setup_wizard/setup_wizard.py | 42 ++++++++++++------- requirements.txt | 3 +- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/frappe/app.py b/frappe/app.py index 609a8535d7..975a2e2002 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -294,7 +294,6 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No _sites_path = sites_path from werkzeug.serving import run_simple - patch_werkzeug_reloader() if profile or os.environ.get('USE_PROFILER'): application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls')) @@ -325,23 +324,3 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No use_debugger=not in_test_env, use_evalex=not in_test_env, threaded=not no_threading) - -def patch_werkzeug_reloader(): - """ - This function monkey patches Werkzeug reloader to ignore reloading files in - the __pycache__ directory. - - To be deprecated when upgrading to Werkzeug 2. - """ - - from werkzeug._reloader import WatchdogReloaderLoop - - trigger_reload = WatchdogReloaderLoop.trigger_reload - - def custom_trigger_reload(self, filename): - if os.path.basename(os.path.dirname(filename)) == "__pycache__": - return - - return trigger_reload(self, filename) - - WatchdogReloaderLoop.trigger_reload = custom_trigger_reload diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py index b42d8c58b7..8dc010e420 100755 --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -6,7 +6,7 @@ from frappe.utils import strip, cint from frappe.translate import (set_default_language, get_dict, send_translations) from frappe.geo.country_info import get_country_info from frappe.utils.password import update_password -from werkzeug.useragents import UserAgent +from ua_parser import user_agent_parser from . import install_fixtures def get_setup_stages(args): @@ -315,17 +315,10 @@ def prettify_args(args): return pretty_args def email_setup_wizard_exception(traceback, args): - if not frappe.local.conf.setup_wizard_exception_email: + if not frappe.conf.setup_wizard_exception_email: return pretty_args = prettify_args(args) - - if frappe.local.request: - user_agent = UserAgent(frappe.local.request.headers.get('User-Agent', '')) - - else: - user_agent = frappe._dict() - message = """ #### Traceback @@ -350,22 +343,43 @@ def email_setup_wizard_exception(traceback, args): - **Site:** {site} - **User:** {user} -- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language} +- **Browser:** {browser} - **Browser Languages**: `{accept_languages}`""".format( site=frappe.local.site, traceback=traceback, args="\n".join(pretty_args), user=frappe.session.user, - user_agent=user_agent, - headers=frappe.local.request.headers, - accept_languages=", ".join(frappe.local.request.accept_languages.values())) + browser=get_browser_string(), + headers=frappe.request.headers, + accept_languages=", ".join(frappe.request.accept_languages.values())) - frappe.sendmail(recipients=frappe.local.conf.setup_wizard_exception_email, + frappe.sendmail(recipients=frappe.conf.setup_wizard_exception_email, sender=frappe.session.user, subject="Setup failed: {}".format(frappe.local.site), message=message, delayed=False) +def get_browser_string(): + if not frappe.request: + return "" + + user_agent = user_agent_parser.ParseUserAgent( + frappe.request.headers.get('User-Agent', '') + ) + + browser = user_agent["family"] + + if user_agent["major"]: + browser += " " + user_agent["major"] + + if user_agent["minor"]: + browser += "." + user_agent["minor"] + + if user_agent["patch"]: + browser += "." + user_agent["patch"] + + return browser + def log_setup_wizard_exception(traceback, args): with open('../logs/setup-wizard.log', 'w+') as setup_log: setup_log.write(traceback) diff --git a/requirements.txt b/requirements.txt index 114ab5f61d..4426ef54be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,8 +62,9 @@ semantic-version~=2.8.5 sqlparse~=0.4.1 stripe~=2.56.0 terminaltables~=3.1.0 +ua-parser~=0.10.0 urllib3~=1.26.4 -Werkzeug~=0.16.1 +Werkzeug~=2.0.2 Whoosh~=2.7.4 wrapt~=1.12.1 xlrd~=2.0.1 From e5857fa56ad651217e2300b991704f6fc8b1bd6c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 8 Feb 2022 19:58:56 +0530 Subject: [PATCH 40/61] fix: improve setup wizard exception email --- frappe/desk/page/setup_wizard/setup_wizard.py | 29 ++----------------- requirements.txt | 3 +- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py index 8dc010e420..0c32e886f4 100755 --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -6,7 +6,6 @@ from frappe.utils import strip, cint from frappe.translate import (set_default_language, get_dict, send_translations) from frappe.geo.country_info import get_country_info from frappe.utils.password import update_password -from ua_parser import user_agent_parser from . import install_fixtures def get_setup_stages(args): @@ -342,16 +341,13 @@ def email_setup_wizard_exception(traceback, args): #### Basic Information - **Site:** {site} -- **User:** {user} -- **Browser:** {browser} -- **Browser Languages**: `{accept_languages}`""".format( +- **User:** {user}""".format( site=frappe.local.site, traceback=traceback, args="\n".join(pretty_args), user=frappe.session.user, - browser=get_browser_string(), headers=frappe.request.headers, - accept_languages=", ".join(frappe.request.accept_languages.values())) + ) frappe.sendmail(recipients=frappe.conf.setup_wizard_exception_email, sender=frappe.session.user, @@ -359,27 +355,6 @@ def email_setup_wizard_exception(traceback, args): message=message, delayed=False) -def get_browser_string(): - if not frappe.request: - return "" - - user_agent = user_agent_parser.ParseUserAgent( - frappe.request.headers.get('User-Agent', '') - ) - - browser = user_agent["family"] - - if user_agent["major"]: - browser += " " + user_agent["major"] - - if user_agent["minor"]: - browser += "." + user_agent["minor"] - - if user_agent["patch"]: - browser += "." + user_agent["patch"] - - return browser - def log_setup_wizard_exception(traceback, args): with open('../logs/setup-wizard.log', 'w+') as setup_log: setup_log.write(traceback) diff --git a/requirements.txt b/requirements.txt index 4426ef54be..2e05159203 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,9 +62,8 @@ semantic-version~=2.8.5 sqlparse~=0.4.1 stripe~=2.56.0 terminaltables~=3.1.0 -ua-parser~=0.10.0 urllib3~=1.26.4 -Werkzeug~=2.0.2 +Werkzeug~=2.0.3 Whoosh~=2.7.4 wrapt~=1.12.1 xlrd~=2.0.1 From ddd45a71bc6f71be277647e69cbe60ebf9cc11e8 Mon Sep 17 00:00:00 2001 From: Daizy Date: Mon, 7 Feb 2022 19:35:12 +0530 Subject: [PATCH 41/61] feat: Allow users to customize their own reports --- frappe/core/doctype/report/test_report.py | 52 ++++++++++++++++++ frappe/desk/reportview.py | 42 +++++++++++---- .../js/frappe/views/reports/report_view.js | 53 +++++++++++++++---- 3 files changed, 129 insertions(+), 18 deletions(-) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 36e3b09254..cd4c2f4553 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -4,6 +4,7 @@ import frappe, json, os import unittest from frappe.desk.query_report import run, save_report +from frappe.desk.reportview import delete_report, save_report as save_as_report from frappe.custom.doctype.customize_form.customize_form import reset_customization test_records = frappe.get_test_records('Report') @@ -30,6 +31,57 @@ class TestReport(unittest.TestCase): self.assertEqual(columns[1].get('label'), 'Module') self.assertTrue('User' in [d.get('name') for d in data]) + def test_can_save_or_delete_report(self): + '''Test case to test if if users can create, save or delete their own report of type Report Builder''' + frappe.set_user("Administrator") + + report = frappe.get_doc({ + 'doctype': 'Report', + 'ref_doctype': 'User', + 'report_name': 'Test Delete Report', + 'report_type': 'Report Builder', + 'is_standard': 'No', + }).insert() + + frappe.set_user("test@example.com") + self.assertRaisesRegex(frappe.exceptions.ValidationError, "Only Report owner or Report Manager can delete the reports", delete_report, report.name) + + frappe.set_user("Administrator") + + report.report_type = 'Custom Report' # change report type to validate + report.save() + + self.assertRaisesRegex(frappe.exceptions.ValidationError, "Only reports of type Report Builder can be deleted", delete_report, report.name) + + report.is_standard = 'Yes' # change is_standard to validate + report.save() + + self.assertRaisesRegex(frappe.exceptions.ValidationError, "Standard Reports can not be deleted", delete_report, report.name) + + frappe.set_user("test@example.com") + + report_name = save_as_report( + 'Dummy Report', + 'User', + json.dumps([{ + 'fieldname': 'email', + 'fieldtype': 'Data', + 'label': 'Email', + 'insert_after_index': 0, + 'link_field': 'name', + 'doctype': 'User', + 'options': 'Email', + 'width': 100, + 'id':'email', + 'name': 'Email' + }]) + ) + + doc = frappe.get_doc("Report", report_name) + + delete_report(doc.name) + + def test_custom_report(self): reset_customization('User') custom_report_name = save_report( diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index c45fc9bfdd..d1eaf00452 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -262,23 +262,47 @@ def compress(data, args=None): } @frappe.whitelist() -def save_report(): - """save report""" +def save_report(name, doctype, report_settings): + """save report if report type is report builder""" - data = frappe.local.form_dict - if frappe.db.exists('Report', data['name']): - d = frappe.get_doc('Report', data['name']) + if frappe.db.exists('Report', name): + d = frappe.get_doc('Report', name) + if d.is_standard == "Yes": + frappe.throw(_("Standard Reports can not be edited")) + + if d.report_type != "Report Builder": + frappe.throw(_("Only reports of type Report Builder can be created")) + + if d.owner != frappe.session.user: + frappe.throw(_("Only Report owner or Report Manager can save the reports")) else: d = frappe.new_doc('Report') - d.report_name = data['name'] - d.ref_doctype = data['doctype'] + d.report_name = name + d.ref_doctype = doctype d.report_type = "Report Builder" - d.json = data['json'] - frappe.get_doc(d).save() + d.json = report_settings + frappe.get_doc(d).save(ignore_permissions=True) frappe.msgprint(_("{0} is saved").format(d.name), alert=True) return d.name +@frappe.whitelist() +def delete_report(name): + """delete report type of report builder if user is report owner or has role Report Manager""" + + report_doc = frappe.get_doc("Report", name) + if report_doc.is_standard == "Yes": + frappe.throw(_("Standard Reports can not be deleted")) + + if report_doc.report_type != "Report Builder": + frappe.throw(_("Only reports of type Report Builder can be deleted")) + + if report_doc.owner != frappe.session.user: + frappe.throw(_("Only Report owner or Report Manager can delete the reports")) + + report_doc.delete(ignore_permissions=True) + frappe.msgprint(_("{0} is Deleted").format(report_doc.name), alert=True) + @frappe.whitelist() @frappe.read_only() def export_query(): diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 23e415ed3e..560657f36d 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -18,14 +18,14 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { setup_defaults() { super.setup_defaults(); this.page_title = __('Report:') + ' ' + this.page_title; - this.menu_items = this.report_menu_items(); - this.view = 'Report'; const route = frappe.get_route(); if (route.length === 4) { this.report_name = route[3]; } + this.view = 'Report'; + if (this.report_name) { return this.get_report_doc() .then(doc => { @@ -39,6 +39,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.page_length = this.report_doc.json.page_length || 20; this.order_by = this.report_doc.json.order_by || 'modified desc'; this.chart_args = this.report_doc.json.chart_args; + this.menu_items = this.report_menu_items(); }); } else { this.add_totals_row = this.view_user_settings.add_totals_row || 0; @@ -1207,7 +1208,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { args: { name: name, doctype: this.doctype, - json: JSON.stringify(report_settings) + report_settings: JSON.stringify(report_settings) }, callback:(r) => { if(r.exc) { @@ -1244,6 +1245,25 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } } + delete_report() { + const _delete_report = (name) => { + return frappe.call({ + method: 'frappe.desk.reportview.delete_report', + args: { name }, + callback: (r) => { + if (r.exc) { + frappe.msgprint(__("Report was not deleted (there were errors)")); + return; + } + } + }); + } + + if (this.report_name) { + _delete_report(this.report_name); + } + } + get_column_widths() { if (this.datatable) { return this.datatable @@ -1465,12 +1485,27 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } }); - // save buttons - if(frappe.user.is_report_manager()) { - items = items.concat([ - { label: __('Save'), action: () => this.save_report('save') }, - { label: __('Save As'), action: () => this.save_report('save_as') } - ]); + const can_save_or_delete = this.report_doc.owner === frappe.session.user || frappe.user.is_report_manager() + // A user with role Report Manager or Report Owner can save + if (can_save_or_delete) { + items.push({ + label: __("Save"), + action: () => this.save_report('save') + }); + } + + // anyone can save as + items.push({ + label: __('Save As'), + action: () => this.save_report('save_as') + }); + + // A user with role Report Manager or Report Owner can delete + if (can_save_or_delete) { + items.push({ + label: __("Delete"), + action: () => this.delete_report() + }); } // user permissions From 9b9956e1ce67f3a511921c2e28d0618d8b84d1e7 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 9 Feb 2022 12:15:10 +0530 Subject: [PATCH 42/61] fix: blur event was overlapping block list item click event --- .../public/js/frappe/views/workspace/blocks/paragraph.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 70f97c44c1..99c161439f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -62,7 +62,7 @@ export default class Paragraph extends Block { this.show_hide_block_list(); }); div.addEventListener('blur', () => { - setTimeout(() => this.show_hide_block_list(true), 10); + !this.over_block_list_item && this.show_hide_block_list(true); }); div.dataset.placeholder = this.api.i18n.t(this._placeholder); div.addEventListener('keyup', this.onKeyUp); @@ -95,6 +95,12 @@ export default class Paragraph extends Block { this.api.caret.setToBlock(index); }); + $block_list_item.mouseenter(() => { + this.over_block_list_item = true; + }).mouseleave(() => { + this.over_block_list_item = false; + }); + $block_list_container.append($block_list_item); }); From 5798cfaf4c76ac3feee033bdd6f194d6ff31f557 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 9 Feb 2022 12:12:24 +0530 Subject: [PATCH 43/61] build: Update iPython dependency Updating dependency due to arbitrary code execution vulnerability in IPython that stems from IPython executing untrusted files in CWD. This vulnerability allows one user to run code as another. ref: https://ipython.readthedocs.io/en/stable/whatsnew/version8.html#ipython-8-0-1-cve-2022-21699 Weaknesses: CWE-250, CWE-269, CWE-279 CVE ID: CVE-2022-21699 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 114ab5f61d..f47c296843 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ googlemaps~=4.4.5 gunicorn~=20.1.0 html2text==2020.1.16 html5lib~=1.1 -ipython~=7.27.0 +ipython~=7.31.1 Jinja2~=3.0.1 ldap3~=2.9 markdown2~=2.4.0 From f0a10efa629698195cdc07719d6002950d57316e Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 12:21:58 +0530 Subject: [PATCH 44/61] fix: improved validations --- frappe/core/doctype/report/test_report.py | 90 ++++++++++--------- frappe/desk/reportview.py | 68 ++++++++------ .../js/frappe/views/reports/report_view.js | 56 ++++++------ 3 files changed, 120 insertions(+), 94 deletions(-) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index cd4c2f4553..f275b25c43 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -4,7 +4,7 @@ import frappe, json, os import unittest from frappe.desk.query_report import run, save_report -from frappe.desk.reportview import delete_report, save_report as save_as_report +from frappe.desk.reportview import delete_report, save_report as _save_report from frappe.custom.doctype.customize_form.customize_form import reset_customization test_records = frappe.get_test_records('Report') @@ -31,55 +31,59 @@ class TestReport(unittest.TestCase): self.assertEqual(columns[1].get('label'), 'Module') self.assertTrue('User' in [d.get('name') for d in data]) - def test_can_save_or_delete_report(self): - '''Test case to test if if users can create, save or delete their own report of type Report Builder''' - frappe.set_user("Administrator") + def test_save_or_delete_report(self): + '''Test for validations when editing / deleting report of type Report Builder''' - report = frappe.get_doc({ - 'doctype': 'Report', - 'ref_doctype': 'User', - 'report_name': 'Test Delete Report', - 'report_type': 'Report Builder', - 'is_standard': 'No', - }).insert() + try: + report = frappe.get_doc({ + 'doctype': 'Report', + 'ref_doctype': 'User', + 'report_name': 'Test Delete Report', + 'report_type': 'Report Builder', + 'is_standard': 'No', + }).insert() - frappe.set_user("test@example.com") - self.assertRaisesRegex(frappe.exceptions.ValidationError, "Only Report owner or Report Manager can delete the reports", delete_report, report.name) + # Check for PermissionError + frappe.set_user("test@example.com") + self.assertRaises(frappe.PermissionError, delete_report, report.name) - frappe.set_user("Administrator") + # Check for Report Type + frappe.set_user("Administrator") + report.db_set("report_type", "Custom Report") + self.assertRaisesRegex( + frappe.ValidationError, + "Only reports of type Report Builder can be deleted", + delete_report, + report.name + ) - report.report_type = 'Custom Report' # change report type to validate - report.save() + # Cleanup + frappe.delete_doc(report.doctype, report.name) - self.assertRaisesRegex(frappe.exceptions.ValidationError, "Only reports of type Report Builder can be deleted", delete_report, report.name) + # Check if creating and deleting works with proper validations + frappe.set_user("test@example.com") + report_name = _save_report( + 'Dummy Report', + 'User', + json.dumps([{ + 'fieldname': 'email', + 'fieldtype': 'Data', + 'label': 'Email', + 'insert_after_index': 0, + 'link_field': 'name', + 'doctype': 'User', + 'options': 'Email', + 'width': 100, + 'id':'email', + 'name': 'Email' + }]) + ) - report.is_standard = 'Yes' # change is_standard to validate - report.save() + doc = frappe.get_doc("Report", report_name) + delete_report(doc.name) - self.assertRaisesRegex(frappe.exceptions.ValidationError, "Standard Reports can not be deleted", delete_report, report.name) - - frappe.set_user("test@example.com") - - report_name = save_as_report( - 'Dummy Report', - 'User', - json.dumps([{ - 'fieldname': 'email', - 'fieldtype': 'Data', - 'label': 'Email', - 'insert_after_index': 0, - 'link_field': 'name', - 'doctype': 'User', - 'options': 'Email', - 'width': 100, - 'id':'email', - 'name': 'Email' - }]) - ) - - doc = frappe.get_doc("Report", report_name) - - delete_report(doc.name) + finally: + frappe.set_user("Administrator") def test_custom_report(self): diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index d1eaf00452..a351bb4ff3 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -263,45 +263,61 @@ def compress(data, args=None): @frappe.whitelist() def save_report(name, doctype, report_settings): - """save report if report type is report builder""" + """Save reports of type Report Builder from Report View""" if frappe.db.exists('Report', name): - d = frappe.get_doc('Report', name) - if d.is_standard == "Yes": - frappe.throw(_("Standard Reports can not be edited")) + report = frappe.get_doc('Report', name) + if report.is_standard == "Yes": + frappe.throw(_("Standard Reports cannot be edited")) - if d.report_type != "Report Builder": - frappe.throw(_("Only reports of type Report Builder can be created")) + if report.report_type != "Report Builder": + frappe.throw(_("Only reports of type Report Builder can be edited")) - if d.owner != frappe.session.user: - frappe.throw(_("Only Report owner or Report Manager can save the reports")) + if ( + report.owner != frappe.session.user + and not frappe.has_permission("Report", "write") + ): + frappe.throw( + _("Insufficient Permissions for editing Report"), + frappe.PermissionError + ) else: - d = frappe.new_doc('Report') - d.report_name = name - d.ref_doctype = doctype + report = frappe.new_doc('Report') + report.report_name = name + report.ref_doctype = doctype - d.report_type = "Report Builder" - d.json = report_settings - frappe.get_doc(d).save(ignore_permissions=True) - frappe.msgprint(_("{0} is saved").format(d.name), alert=True) - return d.name + report.report_type = "Report Builder" + report.json = report_settings + report.save(ignore_permissions=True) + frappe.msgprint(_("{0} saved").format(frappe.bold(report.name)), alert=True) + return report.name @frappe.whitelist() def delete_report(name): - """delete report type of report builder if user is report owner or has role Report Manager""" - - report_doc = frappe.get_doc("Report", name) - if report_doc.is_standard == "Yes": - frappe.throw(_("Standard Reports can not be deleted")) + """Save reports of type Report Builder from Report View""" - if report_doc.report_type != "Report Builder": + report = frappe.get_doc("Report", name) + if report.is_standard == "Yes": + frappe.throw(_("Standard Reports cannot be deleted")) + + if report.report_type != "Report Builder": frappe.throw(_("Only reports of type Report Builder can be deleted")) - if report_doc.owner != frappe.session.user: - frappe.throw(_("Only Report owner or Report Manager can delete the reports")) + if ( + report.owner != frappe.session.user + and not frappe.has_permission("Report", "delete") + ): + frappe.throw( + _("Insufficient Permissions for deleting Report"), + frappe.PermissionError + ) - report_doc.delete(ignore_permissions=True) - frappe.msgprint(_("{0} is Deleted").format(report_doc.name), alert=True) + report.delete(ignore_permissions=True) + frappe.msgprint( + _("{0} deleted").format(frappe.bold(report.name)), + indicator="red", + alert=True, + ) @frappe.whitelist() @frappe.read_only() diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 560657f36d..41c8f07e2f 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -18,14 +18,13 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { setup_defaults() { super.setup_defaults(); this.page_title = __('Report:') + ' ' + this.page_title; + this.view = 'Report'; const route = frappe.get_route(); if (route.length === 4) { this.report_name = route[3]; } - this.view = 'Report'; - if (this.report_name) { return this.get_report_doc() .then(doc => { @@ -39,7 +38,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.page_length = this.report_doc.json.page_length || 20; this.order_by = this.report_doc.json.order_by || 'modified desc'; this.chart_args = this.report_doc.json.chart_args; - this.menu_items = this.report_menu_items(); }); } else { this.add_totals_row = this.view_user_settings.add_totals_row || 0; @@ -53,6 +51,11 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.page.main.addClass('report-view'); } + setup_page() { + this.menu_items = this.report_menu_items(); + super.setup_page(); + } + toggle_side_bar() { super.toggle_side_bar(); // refresh datatable when sidebar is toggled to accomodate extra space @@ -1246,22 +1249,14 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } delete_report() { - const _delete_report = (name) => { - return frappe.call({ - method: 'frappe.desk.reportview.delete_report', - args: { name }, - callback: (r) => { - if (r.exc) { - frappe.msgprint(__("Report was not deleted (there were errors)")); - return; - } - } - }); - } - - if (this.report_name) { - _delete_report(this.report_name); - } + return frappe.call({ + method: 'frappe.desk.reportview.delete_report', + args: { name: this.report_name }, + callback(response) { + if (response.exc) return; + window.history.back(); + } + }); } get_column_widths() { @@ -1485,9 +1480,20 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } }); - const can_save_or_delete = this.report_doc.owner === frappe.session.user || frappe.user.is_report_manager() + function can_edit_or_delete(action) { + const method = action == "delete" ? "can_delete" : "can_write"; + return ( + this.report_doc + && this.report_doc.is_standard !== "Yes" + && ( + frappe.model[method]("Report") + || this.report_doc.owner === frappe.session.user + ) + ); + } + // A user with role Report Manager or Report Owner can save - if (can_save_or_delete) { + if (can_edit_or_delete()) { items.push({ label: __("Save"), action: () => this.save_report('save') @@ -1495,13 +1501,13 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } // anyone can save as - items.push({ - label: __('Save As'), - action: () => this.save_report('save_as') + items.push({ + label: __('Save As'), + action: () => this.save_report('save_as') }); // A user with role Report Manager or Report Owner can delete - if (can_save_or_delete) { + if (can_edit_or_delete("delete")) { items.push({ label: __("Delete"), action: () => this.delete_report() From adc69cb3ecd35d2bc34aab0d5e67d15a70820ae1 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 9 Feb 2022 12:19:43 +0530 Subject: [PATCH 45/61] build: Upgrade Pillow dependency This upgrade handles multiple high severity vulnerabilities. I've not checked the affected code in great depth but the APIs we use may be affected. If they could actually be exploited is another matter which would take a whole lotta effort which I'd rather not test xD Fixes: CWE-74, CWE-125, CWE-120, CWE-125, CWE-400 CVE IDs: CVE-2022-22817, CVE-2022-22816, CVE-2021-34552, CVE-2021-23437 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f47c296843..97536d0e56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ openpyxl~=3.0.7 passlib~=1.7.4 paytmchecksum~=1.7.0 pdfkit~=0.6.1 -Pillow~=8.2.0 +Pillow~=9.0.0 premailer~=3.8.0 psutil~=5.8.0 psycopg2-binary~=2.9.1 From 05bb38ea46fce3f0bed2a317cf630b6fd5e77b0d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 9 Feb 2022 12:32:26 +0530 Subject: [PATCH 46/61] chore: fetch name only if first_document is checked --- frappe/desk/doctype/form_tour/form_tour.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js index 19fe623f60..d6390d7613 100644 --- a/frappe/desk/doctype/form_tour/form_tour.js +++ b/frappe/desk/doctype/form_tour/form_tour.js @@ -15,12 +15,12 @@ frappe.ui.form.on('Form Tour', { frm.add_custom_button(__('Show Tour'), async () => { const issingle = await check_if_single(frm.doc.reference_doctype); - const name = await get_first_document(frm.doc.reference_doctype); let route_changed = null; - + if (issingle) { route_changed = frappe.set_route('Form', frm.doc.reference_doctype); } else if (frm.doc.first_document) { + const name = await get_first_document(frm.doc.reference_doctype); route_changed = frappe.set_route('Form', frm.doc.reference_doctype, name); } else { route_changed = frappe.set_route('Form', frm.doc.reference_doctype, 'new'); From a49a07e4192f95776bff044a61798af47e709d83 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 12:35:44 +0530 Subject: [PATCH 47/61] fix: sider issues --- frappe/desk/reportview.py | 2 +- .../public/js/frappe/views/reports/report_view.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index a351bb4ff3..ab411fddfe 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -298,7 +298,7 @@ def delete_report(name): report = frappe.get_doc("Report", name) if report.is_standard == "Yes": - frappe.throw(_("Standard Reports cannot be deleted")) + frappe.throw(_("Standard Reports cannot be deleted")) if report.report_type != "Report Builder": frappe.throw(_("Only reports of type Report Builder can be deleted")) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 41c8f07e2f..7971b029f2 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -1257,7 +1257,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { window.history.back(); } }); - } + } get_column_widths() { if (this.datatable) { @@ -1495,9 +1495,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { // A user with role Report Manager or Report Owner can save if (can_edit_or_delete()) { items.push({ - label: __("Save"), - action: () => this.save_report('save') - }); + label: __("Save"), + action: () => this.save_report('save') + }); } // anyone can save as @@ -1509,9 +1509,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { // A user with role Report Manager or Report Owner can delete if (can_edit_or_delete("delete")) { items.push({ - label: __("Delete"), - action: () => this.delete_report() - }); + label: __("Delete"), + action: () => this.delete_report() + }); } // user permissions From d8fc39659a0086d54728b1ac966f1bc356dec963 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 12:37:41 +0530 Subject: [PATCH 48/61] fix: docstring --- frappe/desk/reportview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index ab411fddfe..880afae489 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -294,7 +294,7 @@ def save_report(name, doctype, report_settings): @frappe.whitelist() def delete_report(name): - """Save reports of type Report Builder from Report View""" + """Delete reports of type Report Builder from Report View""" report = frappe.get_doc("Report", name) if report.is_standard == "Yes": From f777700372afffc98307b5b5bf8332f5854500b4 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 13:14:11 +0530 Subject: [PATCH 49/61] test: create new user --- frappe/core/doctype/report/test_report.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index f275b25c43..5a22304f32 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -6,6 +6,7 @@ import unittest from frappe.desk.query_report import run, save_report from frappe.desk.reportview import delete_report, save_report as _save_report from frappe.custom.doctype.customize_form.customize_form import reset_customization +from frappe.core.doctype.user_permission.test_user_permission import create_user test_records = frappe.get_test_records('Report') test_dependencies = ['User'] @@ -44,7 +45,8 @@ class TestReport(unittest.TestCase): }).insert() # Check for PermissionError - frappe.set_user("test@example.com") + create_user("test_report_owner@example.com", "Website Manager") + frappe.set_user("test_report_owner@example.com") self.assertRaises(frappe.PermissionError, delete_report, report.name) # Check for Report Type @@ -57,9 +59,6 @@ class TestReport(unittest.TestCase): report.name ) - # Cleanup - frappe.delete_doc(report.doctype, report.name) - # Check if creating and deleting works with proper validations frappe.set_user("test@example.com") report_name = _save_report( @@ -84,6 +83,7 @@ class TestReport(unittest.TestCase): finally: frappe.set_user("Administrator") + frappe.db.rollback() def test_custom_report(self): From a18703a0b1eef958ac112153fdd2d0f79e1e36fa Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 13:52:37 +0530 Subject: [PATCH 50/61] fix: access to `this` and better UX --- frappe/public/js/frappe/views/reports/report_view.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 7971b029f2..f6aa17da8e 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -1480,7 +1480,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } }); - function can_edit_or_delete(action) { + const can_edit_or_delete = (action) => { const method = action == "delete" ? "can_delete" : "can_write"; return ( this.report_doc @@ -1510,7 +1510,11 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { if (can_edit_or_delete("delete")) { items.push({ label: __("Delete"), - action: () => this.delete_report() + action: () => frappe.confirm( + "Are you sure you want to delete this report?", + () => this.delete_report(), + ), + shortcut: "Shift+Ctrl+D" }); } From e21275f06034a5843085c7373dd7015d66153c7d Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 14:05:40 +0530 Subject: [PATCH 51/61] style: add missing semicolon --- frappe/public/js/frappe/views/reports/report_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index f6aa17da8e..1291e63543 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -1490,7 +1490,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { || this.report_doc.owner === frappe.session.user ) ); - } + }; // A user with role Report Manager or Report Owner can save if (can_edit_or_delete()) { From 976c3bedc393a2be78e0492b142aa6a820842bc1 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Feb 2022 15:45:30 +0530 Subject: [PATCH 52/61] fix: use correct indicator --- frappe/desk/reportview.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 880afae489..b0e1f901aa 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -289,7 +289,11 @@ def save_report(name, doctype, report_settings): report.report_type = "Report Builder" report.json = report_settings report.save(ignore_permissions=True) - frappe.msgprint(_("{0} saved").format(frappe.bold(report.name)), alert=True) + frappe.msgprint( + _("Report {0} saved").format(frappe.bold(report.name)), + indicator="green", + alert=True, + ) return report.name @frappe.whitelist() @@ -314,8 +318,8 @@ def delete_report(name): report.delete(ignore_permissions=True) frappe.msgprint( - _("{0} deleted").format(frappe.bold(report.name)), - indicator="red", + _("Report {0} deleted").format(frappe.bold(report.name)), + indicator="green", alert=True, ) From d3f768fef9b96c91a7a04e2454ca6d94c1236635 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 8 Feb 2022 10:41:20 +0530 Subject: [PATCH 53/61] feat(db/pg): ability to ignore pk colission --- frappe/model/base_document.py | 32 ++++++++++++++++++++++++-------- frappe/model/document.py | 6 +----- frappe/tests/test_db.py | 10 ++++++++++ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 307d95e84b..2f0a9d4943 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -375,12 +375,24 @@ class BaseDocument(object): fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype] return fieldname[0] if fieldname else None - def db_insert(self): - """INSERT the document (with valid columns) in the database.""" + def db_insert(self, ignore_if_duplicate=False): + """INSERT the document (with valid columns) in the database. + + args: + ignore_if_duplicate: ignore primary key collision + at database level (postgres) + in python (mariadb) + """ if not self.name: # name will be set by document class in most cases set_new_name(self) + conflict_handler = "" + # On postgres we can't implcitly ignore PK collision + # So instruct pg to ignore `name` field conflicts + if ignore_if_duplicate and frappe.db.db_type == "postgres": + conflict_handler = "on conflict (name) do nothing" + if not self.creation: self.creation = self.modified = now() self.created_by = self.modified_by = frappe.session.user @@ -391,10 +403,11 @@ class BaseDocument(object): columns = list(d) try: frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns}) - VALUES ({values})""".format( - doctype = self.doctype, - columns = ", ".join("`"+c+"`" for c in columns), - values = ", ".join(["%s"] * len(columns)) + VALUES ({values}) {conflict_handler}""".format( + doctype=self.doctype, + columns=", ".join("`"+c+"`" for c in columns), + values=", ".join(["%s"] * len(columns)), + conflict_handler=conflict_handler ), list(d.values())) except Exception as e: if frappe.db.is_primary_key_violation(e): @@ -407,8 +420,11 @@ class BaseDocument(object): self.db_insert() return - frappe.msgprint(_("{0} {1} already exists").format(self.doctype, frappe.bold(self.name)), title=_("Duplicate Name"), indicator="red") - raise frappe.DuplicateEntryError(self.doctype, self.name, e) + if not ignore_if_duplicate: + frappe.msgprint(_("{0} {1} already exists") + .format(self.doctype, frappe.bold(self.name)), + title=_("Duplicate Name"), indicator="red") + raise frappe.DuplicateEntryError(self.doctype, self.name, e) elif frappe.db.is_unique_key_violation(e): # unique constraint diff --git a/frappe/model/document.py b/frappe/model/document.py index f7ba9250fa..85603587cd 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -249,11 +249,7 @@ class Document(BaseDocument): if getattr(self.meta, "issingle", 0): self.update_single(self.get_valid_dict()) else: - try: - self.db_insert() - except frappe.DuplicateEntryError as e: - if not ignore_if_duplicate: - raise e + self.db_insert(ignore_if_duplicate=ignore_if_duplicate) # children for d in self.get_all_children(): diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 6e96849b35..bbd09590be 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -291,6 +291,16 @@ class TestDB(unittest.TestCase): frappe.db.MAX_WRITES_PER_TRANSACTION = Database.MAX_WRITES_PER_TRANSACTION + def test_pk_collision_ignoring(self): + # note has `name` generated from title + for _ in range(3): + frappe.get_doc(doctype="Note", title="duplicate name").insert(ignore_if_duplicate=True) + + with savepoint(): + self.assertRaises(frappe.DuplicateEntryError, frappe.get_doc(doctype="Note", title="duplicate name").insert) + # recover transaction to continue other tests + raise Exception + @run_only_if(db_type_is.MARIADB) class TestDDLCommandsMaria(unittest.TestCase): From 1439e667b2e5f65b974a36dc4eb5debf138deb75 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 8 Feb 2022 11:04:50 +0530 Subject: [PATCH 54/61] chore: add debug status temporarily --- frappe/database/database.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/database/database.py b/frappe/database/database.py index 9fa1ff161c..c833bdeed3 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -177,6 +177,8 @@ class Database(object): raise frappe.QueryTimeoutError(e) elif frappe.conf.db_type == 'postgres': + # TODO: added temporarily + print(e) raise if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): From e4ba46ae0dd54785b27da4c3abad407bcf772c60 Mon Sep 17 00:00:00 2001 From: hrwx Date: Mon, 7 Feb 2022 10:01:51 +0000 Subject: [PATCH 55/61] fix: add test cases for email parsing --- .../doctype/communication/communication.py | 23 +++++++++++-------- .../communication/test_communication.py | 14 +++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 140107b42a..f89f0d8765 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -2,6 +2,7 @@ # License: MIT. See LICENSE from collections import Counter +from typing import List import frappe from frappe import _ from frappe.model.document import Document @@ -367,15 +368,8 @@ def get_permission_query_conditions_for_communication(user): return """`tabCommunication`.email_account in ({email_accounts})"""\ .format(email_accounts=','.join(email_accounts)) -def get_contacts(email_strings, auto_create_contact=False): - email_addrs = [] - - for email_string in email_strings: - if email_string: - result = getaddresses([email_string]) - for email in result: - email_addrs.append(email[1]) - +def get_contacts(email_strings: List[str], auto_create_contact=False) -> List[str]: + email_addrs = get_emails(email_strings) contacts = [] for email in email_addrs: email = get_email_without_link(email) @@ -404,6 +398,17 @@ def get_contacts(email_strings, auto_create_contact=False): return contacts +def get_emails(email_strings: List[str]) -> List[str]: + email_addrs = [] + + for email_string in email_strings: + if email_string: + result = getaddresses([email_string]) + for email in result: + email_addrs.append(email[1]) + + return email_addrs + def add_contact_links_to_communication(communication, contact_name): contact_links = frappe.get_all("Dynamic Link", filters={ "parenttype": "Contact", diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index f26e70771b..d933c2f494 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -5,6 +5,7 @@ from urllib.parse import quote import frappe from frappe.email.doctype.email_queue.email_queue import EmailQueue +from frappe.core.doctype.communication.communication import get_emails test_records = frappe.get_test_records('Communication') @@ -201,6 +202,19 @@ class TestCommunication(unittest.TestCase): self.assertIn(("Note", note.name), doc_links) + def parse_emails(self): + emails = get_emails( + [ + 'comm_recipient+DocType+DocName@example.com', + '"First, LastName" ', + 'test@user.com' + ] + ) + + self.assertEqual(emails[0], "comm_recipient+DocType+DocName@example.com") + self.assertEqual(emails[1], "first.lastname@email.com") + self.assertEqual(emails[2], "test@user.com") + class TestCommunicationEmailMixin(unittest.TestCase): def new_communication(self, recipients=None, cc=None, bcc=None): recipients = ', '.join(recipients or []) From 26d802737f4745f4076c7788f441f54b00a3816a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:37:00 +0000 Subject: [PATCH 56/61] build(deps): bump node-sass from 4.14.1 to 7.0.0 Bumps [node-sass](https://github.com/sass/node-sass) from 4.14.1 to 7.0.0. - [Release notes](https://github.com/sass/node-sass/releases) - [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md) - [Commits](https://github.com/sass/node-sass/compare/v4.14.1...v7.0.0) --- updated-dependencies: - dependency-name: node-sass dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 753 +++++++++++++++++++++++++++++---------------------- 2 files changed, 432 insertions(+), 323 deletions(-) diff --git a/package.json b/package.json index 7d286e62a6..259a157311 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "localforage": "^1.9.0", "moment": "^2.20.1", "moment-timezone": "^0.5.28", - "node-sass": "^4.14.1", + "node-sass": "^7.0.0", "plyr": "^3.6.2", "popper.js": "^1.16.0", "quagga": "^0.12.1", diff --git a/yarn.lock b/yarn.lock index 18a8610a2a..65cc9d045f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -79,6 +79,16 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/minimist@^1.2.0": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -211,6 +221,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -251,6 +266,19 @@ aproba@^1.0.3: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -264,11 +292,6 @@ array-filter@^1.0.0: resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -279,6 +302,11 @@ arraybuffer.slice@~0.0.7: resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -438,13 +466,6 @@ blob@0.0.5: resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - bluebird@^3.1.1: version "3.5.3" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" @@ -520,25 +541,21 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^5.0.0: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -571,7 +588,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -591,10 +608,10 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.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== +chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -626,6 +643,11 @@ charenc@0.0.2: optionalDependencies: fsevents "~2.3.1" +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + clean-css@^4.1.11: version "4.2.1" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" @@ -704,6 +726,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colord@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/colord/-/colord-2.0.0.tgz#f8c19f2526b7dc5b22d6e57ef102f03a2a43a3d8" @@ -751,7 +778,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -console-control-strings@^1.0.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= @@ -843,13 +870,14 @@ cropperjs@^1.5.12: resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50" integrity sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw== -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= +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: - lru-cache "^4.0.1" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" crypt@0.0.2: version "0.0.2" @@ -989,13 +1017,6 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - custom-event-polyfill@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz#9bc993ddda937c1a30ccd335614c6c58c4f87aee" @@ -1061,7 +1082,15 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -decamelize@^1.0.0, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -1261,6 +1290,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + errno@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -1268,7 +1302,7 @@ errno@^0.1.1: dependencies: prr "~1.0.1" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -1539,14 +1573,6 @@ find-pkg@^0.1.2: dependencies: find-file-up "^0.1.2" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -1554,6 +1580,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -1635,6 +1669,13 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1645,16 +1686,6 @@ fsevents@~2.3.1: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fstream@^1.0.0, fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -1665,6 +1696,21 @@ fuse.js@^3.4.6: resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.6.tgz#545c3411fed88bf2e27c457cab6e73e7af697a45" integrity sha512-H6aJY4UpLFwxj1+5nAvufom5b2BT2v45P1MkPvdGIK8fWjQx/7o6tTT1+ALV0yawQvbmvCF0ufl2et8eJ7v7Cg== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -1765,7 +1811,19 @@ glob-parent@^5.1.0, glob-parent@~5.1.0: 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: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -1814,12 +1872,17 @@ graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.2.3: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0, har-validator@~5.1.3: +har-validator@~5.1.3: version "5.1.5" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== @@ -1827,6 +1890,11 @@ har-validator@~5.1.0, har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -1871,7 +1939,7 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-unicode@^2.0.0: +has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= @@ -1929,6 +1997,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + hsl-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" @@ -2015,17 +2090,10 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -in-publish@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c" - integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ== - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indexes-of@^1.0.1: version "1.0.1" @@ -2045,7 +2113,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2136,6 +2204,13 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.5.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" @@ -2164,11 +2239,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -2208,6 +2278,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + is-promise@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" @@ -2265,11 +2340,6 @@ is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - is-weakmap@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" @@ -2436,6 +2506,11 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" +kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + launch-editor@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.2.1.tgz#871b5a3ee39d6680fcc26d37930b6eeda89db0ca" @@ -2501,17 +2576,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - loader-utils@^0.2.16: version "0.2.17" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" @@ -2551,6 +2615,13 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -2583,15 +2654,7 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lru-cache@^4.0.1, lru-cache@^4.1.2: +lru-cache@^4.1.2: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -2599,6 +2662,13 @@ lru-cache@^4.0.1, lru-cache@^4.1.2: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -2607,11 +2677,16 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -map-obj@^1.0.0, map-obj@^1.0.1: +map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -2631,21 +2706,23 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -meow@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" merge-descriptors@1.0.1: version "1.0.1" @@ -2718,6 +2795,11 @@ 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== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2725,19 +2807,36 @@ minimatch@^3.0.4, minimatch@~3.0.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== +minipass@^3.0.0: + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: - minimist "^1.2.5" + yallist "^4.0.0" -mkdirp@^1.0.4, mkdirp@~1.0.4: +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -2842,56 +2941,52 @@ node-gyp-build@^4.2.2: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== -node-gyp@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" - integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== +node-gyp@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" node-releases@^1.1.71: version "1.1.72" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== -node-sass@^4.14.1: - version "4.14.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" - integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== +node-sass@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-7.0.0.tgz#33ee7c2df299d51f682f13d79f3d2a562225788e" + integrity sha512-6yUnsD3L8fVbgMX6nKQqZkjRcG7a/PpmF0pEyeWf+BgbTj2ToJlCYrnUifL2KbjV5gIY22I3oppahBWA3B+jUg== dependencies: async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" + chalk "^4.1.2" + cross-spawn "^7.0.3" gaze "^1.0.0" get-stdin "^4.0.1" glob "^7.0.3" - in-publish "^2.0.0" lodash "^4.17.15" - meow "^3.7.0" - mkdirp "^0.5.1" + meow "^9.0.0" nan "^2.13.2" - node-gyp "^3.8.0" - npmlog "^4.0.0" + node-gyp "^7.1.0" + npmlog "^5.0.0" request "^2.88.0" sass-graph "2.2.5" stdout-stream "^1.4.0" "true-case-path" "^1.0.2" -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" @@ -2902,7 +2997,7 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -2912,6 +3007,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -2927,7 +3032,7 @@ normalize-url@^4.5.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -2937,6 +3042,16 @@ normalize-url@^4.5.0: gauge "~2.7.3" set-blocking "~2.0.0" +npmlog@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + nth-check@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" @@ -2959,7 +3074,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -3016,25 +3131,12 @@ once@^1.3.0: dependencies: wrappy "1" -os-homedir@^1.0.0, os-homedir@^1.0.1: +os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@0: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-limit@^2.0.0: +p-limit@^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== @@ -3055,6 +3157,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -3096,13 +3205,6 @@ parse-data-uri@^0.2.0: dependencies: data-uri-to-buffer "0.0.3" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -3138,13 +3240,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -3160,6 +3255,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +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.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -3170,15 +3270,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - 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" @@ -3199,28 +3290,11 @@ picomatch@^2.0.5, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - piscina@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/piscina/-/piscina-2.2.0.tgz#8fd5236f07aaa9676c4100a3e4d90b6b7aaabf4c" @@ -3643,7 +3717,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: +psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -3753,11 +3827,6 @@ pug@^2.0.3: pug-runtime "^2.0.5" pug-strip-comments "^1.0.4" -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -3791,6 +3860,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + quill-delta@4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-4.2.1.tgz#ad4f191cdf3be5079c5dc3991b9603a5cc0db69a" @@ -3880,22 +3954,24 @@ raw-loader@^0.5.1: resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.3.5: version "2.3.7" @@ -3910,6 +3986,15 @@ readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.3.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -3917,13 +4002,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" + indent-string "^4.0.0" + strip-indent "^3.0.0" redis-commands@^1.7.0: version "1.7.0" @@ -3996,40 +4081,7 @@ repeat-string@^1.5.2: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -request@^2.44.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -request@^2.87.0, request@^2.88.0: +request@^2.44.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -4133,14 +4185,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -4170,7 +4215,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4220,10 +4265,12 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@^7.3.2, semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" send@0.17.1: version "0.17.1" @@ -4271,6 +4318,18 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +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@^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== + shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" @@ -4486,6 +4545,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -4547,6 +4615,13 @@ string.prototype.trimstart@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4582,19 +4657,19 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - is-utf8 "^0.2.0" + ansi-regex "^5.0.1" -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: - get-stdin "^4.0.1" + min-indent "^1.0.0" strip-json-comments@^3.1.1: version "3.1.1" @@ -4685,14 +4760,17 @@ svgo@^2.3.0: csso "^4.2.0" stable "^0.1.8" -tar@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" - integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== +tar@^6.0.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: - block-stream "*" - fstream "^1.0.12" - inherits "2" + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" through@^2.3.4: version "2.3.8" @@ -4752,14 +4830,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -4768,10 +4838,10 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== "true-case-path@^1.0.2": version "1.0.3" @@ -4797,6 +4867,21 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -4857,7 +4942,7 @@ url-polyfill@^1.1.8: resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.12.tgz#6cdaa17f6b022841b3aec0bf8dbd87ac0cd33331" integrity sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A== -util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -4972,13 +5057,20 @@ which-typed-array@^1.1.2: has-symbols "^1.0.1" is-typed-array "^1.1.3" -which@1, which@^1.2.12, which@^1.2.9: +which@^1.2.12: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +which@^2.0.1, which@^2.0.2: + 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" @@ -4986,6 +5078,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" @@ -5052,6 +5151,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" @@ -5078,6 +5182,11 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== +yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" From c4060d42305ce43f4bd2eb7f123def7c3a2585df Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Fri, 11 Feb 2022 11:24:11 +0530 Subject: [PATCH 57/61] fix: enumerate from 1 --- frappe/core/doctype/doctype/doctype.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 1bc94fd0bf..f40352533e 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -759,25 +759,25 @@ def validate_links_table_fieldnames(meta): return fieldnames = tuple(field.fieldname for field in meta.fields) - for index, link in enumerate(meta.links): + for index, link in enumerate(meta.links, 1): link_meta = frappe.get_meta(link.link_doctype) if not link_meta.get_field(link.link_fieldname): - message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) + message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) if not link.is_child_table: continue if not link.parent_doctype: - message = _("Document Links Row #{0}: Parent DocType is mandatory for internal links").format(index+1) + message = _("Document Links Row #{0}: Parent DocType is mandatory for internal links").format(index) frappe.throw(message, frappe.ValidationError, _("Parent Missing")) if not link.table_fieldname: - message = _("Document Links Row #{0}: Table Fieldname is mandatory for internal links").format(index+1) + message = _("Document Links Row #{0}: Table Fieldname is mandatory for internal links").format(index) frappe.throw(message, frappe.ValidationError, _("Table Fieldname Missing")) if link.table_fieldname not in fieldnames: - message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.table_fieldname), frappe.bold(meta.name)) + message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format(index, frappe.bold(link.table_fieldname), frappe.bold(meta.name)) frappe.throw(message, frappe.ValidationError, _("Invalid Table Fieldname")) def validate_fields_for_doctype(doctype): From 954b932c10f5448810a33b447468522777ad3084 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Mon, 14 Feb 2022 11:48:17 +0100 Subject: [PATCH 58/61] added menu translations (#15879) added menu translations when navigating over avater --- frappe/translations/de.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index f682a51e17..4a856131ab 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -148,6 +148,8 @@ More Information,Mehr Informationen, More...,Mehr..., Move,Bewegen, My Account,Mein Konto, +My Profile,Mein Profil, +My Settings,Meine Einstellungen, New Address,Neue Adresse, New Contact,Neuer Kontakt, Next,Weiter, From 16ef2d3b135718a30f1bc9df2116f71cc0e0b279 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 14 Feb 2022 17:48:20 +0530 Subject: [PATCH 59/61] ci(Mergify): Configuration update (#15968) --- .mergify.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mergify.yml b/.mergify.yml index 0bd9641d5b..63fe1a0086 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -48,3 +48,7 @@ pull_request_rules: actions: merge: method: squash + commit_message_template: | + {{ title }} (#{{ number }}) + + {{ body }} From 8995bbb8339561b4663734eba3970c7195aac701 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Mon, 14 Feb 2022 13:25:27 +0100 Subject: [PATCH 60/61] fix: Update translations (#15886) For better understanding on what this does. For "Submit" the german word "Buchen" or "gebucht" ist used mostly in the system. ![grafik](https://user-images.githubusercontent.com/22279621/152641036-ee89f81d-4a10-4686-8f67-646af6a8087f.png) --- frappe/translations/de.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index 4a856131ab..17151a14eb 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -408,7 +408,7 @@ Allow Self Approval,Erlaube Selbstgenehmigung, Allow approval for creator of the document,Genehmigung für den Ersteller des Dokuments zulassen, Allow events in timeline,Ereignisse in der Zeitleiste zulassen, Allow in Quick Entry,In Schnelleingabe zulassen, -Allow on Submit,Beim Übertragen zulassen, +Allow on Submit,Änderungen zulassen wenn gebucht, Allow only one session per user,Nur eine Sitzung pro Benutzer zulassen, Allow page break inside tables,Seitenumbruch innerhalb von Tabellen erlauben, Allow saving if mandatory fields are not filled,Speichern trotz leerer Pflichtfelder zulassen, From b5f17888a8b6d5325c09274adc9f100df8dcf764 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 14 Feb 2022 18:21:12 +0530 Subject: [PATCH 61/61] fix: Image action not in center of image in mobile view --- frappe/public/scss/desk/sidebar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/desk/sidebar.scss b/frappe/public/scss/desk/sidebar.scss index 82bd1cdf13..0bb6ba5f40 100644 --- a/frappe/public/scss/desk/sidebar.scss +++ b/frappe/public/scss/desk/sidebar.scss @@ -104,10 +104,10 @@ body[data-route^="Module"] .main-menu { } .sidebar-image-section { + width: min(100%, 170px); cursor: pointer; .sidebar-image { - width: min(100%, 170px); height: auto; max-height: 170px; object-fit: cover;