From feed7884e2628a8564c41571db46d79cb8c923b3 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Mon, 14 Dec 2020 14:05:15 +0530 Subject: [PATCH 001/122] feat: virtual doctype Virtual Doctype's data souurce can be anything a file or a secondary database table or an api --- frappe/core/doctype/doctype/doctype.json | 9 ++++++++- frappe/desk/form/load.py | 6 ++---- frappe/desk/reportview.py | 10 +++++++--- frappe/model/document.py | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 215ef8cd62..622ca94db8 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -22,6 +22,7 @@ "track_views", "custom", "beta", + "virtual_doctype", "fields_section_break", "fields", "sb1", @@ -528,6 +529,12 @@ "fieldname": "index_web_pages_for_search", "fieldtype": "Check", "label": "Index Web Pages for Search" + }, + { + "default": "0", + "fieldname": "virtual_doctype", + "fieldtype": "Check", + "label": "Virtual DocType" } ], "icon": "fa fa-bolt", @@ -609,7 +616,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-09-24 13:13:58.227153", + "modified": "2020-12-14 12:48:33.752219", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index cacbd3c633..e045a22c91 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -27,9 +27,6 @@ def getdoc(doctype, name, user=None): if not name: name = doctype - if not frappe.db.exists(doctype, name): - return [] - try: doc = frappe.get_doc(doctype, name) run_onload(doc) @@ -43,7 +40,8 @@ def getdoc(doctype, name, user=None): # add file list doc.add_viewed() get_docinfo(doc) - + except frappe.DoesNotExistError: + return [] except Exception: frappe.errprint(frappe.utils.get_traceback()) raise diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 9f5a5d84c8..6562e46a2b 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -12,15 +12,19 @@ from frappe import _ from six import string_types, StringIO from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cstr, format_duration +from frappe.model.base_document import get_controller @frappe.whitelist() @frappe.read_only() def get(): args = get_form_params() - - data = compress(execute(**args), args = args) - + # If virtual doctype get data from controller het_list method + if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="virtual_doctype"): + controller = get_controller(args.doctype) + data = compress(controller(args.doctype).get_list(args)) + else: + data = compress(execute(**args), args = args) return data def execute(doctype, *args, **kwargs): diff --git a/frappe/model/document.py b/frappe/model/document.py index 3789e20b19..8e7645153c 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal'): + if not self.get('__islocal') and not self.meta.virtual_doctype: if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 04d63159f1bc680cee041124b436f082d201622e Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:18:26 +0530 Subject: [PATCH 002/122] fix: skip table creation for virtual doctype --- frappe/database/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index daabbaa61c..f2d32e0e3c 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,9 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new(): + if self.is_new() and not self.meta.virtual_doctype: self.create() - else: + elif not self.meta.virtual_doctype: frappe.cache().hdel('table_columns', self.table_name) self.alter() From 511a12eeeac121c94ec6dbfeda8f1b22156980b7 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:19:09 +0530 Subject: [PATCH 003/122] fix: link validation fetch from get doc --- frappe/model/base_document.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 5d86b3bac8..1537ec2325 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -518,6 +518,9 @@ class BaseDocument(object): if frappe.get_meta(doctype).issingle: values.name = doctype + if frappe.get_meta(doctype).virtual_doctype: + values = frappe.get_doc(doctype, docname) + if values: setattr(self, df.fieldname, values.name) From dc1b4b6b9f1801f2af97715f09baec4d9df692de Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:20:41 +0530 Subject: [PATCH 004/122] fix: listview throws table not exists error --- frappe/desk/reportview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6562e46a2b..6c9b373dbd 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -312,8 +312,9 @@ def get_stats(stats, doctype, filters=[]): try: columns = frappe.db.get_table_columns(doctype) - except frappe.db.InternalError: + except (frappe.db.InternalError, Exception): # raised when _user_tags column is added on the fly + # raised if its a virtual doctype columns = [] for tag in tags: From 663f3431f783726269ab311d2d17d974db422a76 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 16 Dec 2020 13:08:24 +0530 Subject: [PATCH 005/122] fix: fixed commenting and sharing for virtual doctypes --- frappe/core/doctype/comment/comment.py | 2 +- frappe/model/base_document.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index a2105c1511..af787339c3 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -144,7 +144,7 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): """Updates `_comments` property in parent Document with given dict. :param _comments: Dict of comments.""" - if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle"): + if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "virtual_doctype"): return try: diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 1537ec2325..167641d6eb 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -502,18 +502,18 @@ class BaseDocument(object): not _df.get('fetch_if_empty') or (_df.get('fetch_if_empty') and not self.get(_df.fieldname)) ] + if not frappe.get_meta(doctype).virtual_doctype: + if not fields_to_fetch: + # cache a single value type + values = frappe._dict(name=frappe.db.get_value(doctype, docname, + 'name', cache=True)) + else: + values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] + for _df in fields_to_fetch] - if not fields_to_fetch: - # cache a single value type - values = frappe._dict(name=frappe.db.get_value(doctype, docname, - 'name', cache=True)) - else: - values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] - for _df in fields_to_fetch] - - # don't cache if fetching other values too - values = frappe.db.get_value(doctype, docname, - values_to_fetch, as_dict=True) + # don't cache if fetching other values too + values = frappe.db.get_value(doctype, docname, + values_to_fetch, as_dict=True) if frappe.get_meta(doctype).issingle: values.name = doctype From 7b16837f8840a2de2eff249e2679ab9241644668 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 23 Dec 2020 20:38:06 +0530 Subject: [PATCH 006/122] fix: handle virtual doctype flag for existing doctypes --- frappe/database/schema.py | 4 ++-- frappe/model/document.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index f2d32e0e3c..00a59b93f7 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,9 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new() and not self.meta.virtual_doctype: + if self.is_new() and not self.meta.get('virtual_doctype'): self.create() - elif not self.meta.virtual_doctype: + elif not self.meta.get('virtual_doctype'): frappe.cache().hdel('table_columns', self.table_name) self.alter() diff --git a/frappe/model/document.py b/frappe/model/document.py index 8e7645153c..3ad3574097 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal') and not self.meta.virtual_doctype: + if not self.get('__islocal') and not self.meta.get('virtual_doctype'): if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 86f9747925bed1034c29b5f206b46ede4e237f3e Mon Sep 17 00:00:00 2001 From: Shridhar Patil Date: Wed, 23 Dec 2020 22:32:11 +0530 Subject: [PATCH 007/122] chore: clean up database schema sync for virtual doctype Co-authored-by: Chinmay D. Pai Signed-off-by: Chinmay D. Pai --- frappe/database/schema.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 00a59b93f7..64e444c0c0 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,12 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new() and not self.meta.get('virtual_doctype'): + if self.meta.get('virtual_doctype'): + # no schema to sync for virtual doctypes + return + if self.is_new(): self.create() - elif not self.meta.get('virtual_doctype'): + else: frappe.cache().hdel('table_columns', self.table_name) self.alter() From 2840146098251d88e054d467392b8f8ee2049bd0 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 23 Dec 2020 23:28:50 +0530 Subject: [PATCH 008/122] fix: renamed label and field name --- frappe/core/doctype/comment/comment.py | 2 +- frappe/core/doctype/doctype/doctype.json | 8 ++++---- frappe/desk/reportview.py | 2 +- frappe/model/base_document.py | 4 ++-- frappe/model/document.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index af787339c3..d6b0e70985 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -144,7 +144,7 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): """Updates `_comments` property in parent Document with given dict. :param _comments: Dict of comments.""" - if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "virtual_doctype"): + if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "is_virtual"): return try: diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 622ca94db8..91820926fa 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -22,7 +22,7 @@ "track_views", "custom", "beta", - "virtual_doctype", + "is_virtual", "fields_section_break", "fields", "sb1", @@ -532,9 +532,9 @@ }, { "default": "0", - "fieldname": "virtual_doctype", + "fieldname": "is_virtual", "fieldtype": "Check", - "label": "Virtual DocType" + "label": "Is Virtual" } ], "icon": "fa fa-bolt", @@ -616,7 +616,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-12-14 12:48:33.752219", + "modified": "2020-12-23 23:48:33.752219", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6c9b373dbd..e0754b41ec 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -20,7 +20,7 @@ from frappe.model.base_document import get_controller def get(): args = get_form_params() # If virtual doctype get data from controller het_list method - if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="virtual_doctype"): + if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="is_virtual"): controller = get_controller(args.doctype) data = compress(controller(args.doctype).get_list(args)) else: diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 167641d6eb..2b5c57b720 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -502,7 +502,7 @@ class BaseDocument(object): not _df.get('fetch_if_empty') or (_df.get('fetch_if_empty') and not self.get(_df.fieldname)) ] - if not frappe.get_meta(doctype).virtual_doctype: + if not frappe.get_meta(doctype).get('is_virtual'): if not fields_to_fetch: # cache a single value type values = frappe._dict(name=frappe.db.get_value(doctype, docname, @@ -518,7 +518,7 @@ class BaseDocument(object): if frappe.get_meta(doctype).issingle: values.name = doctype - if frappe.get_meta(doctype).virtual_doctype: + if frappe.get_meta(doctype).get('is_virtual'): values = frappe.get_doc(doctype, docname) if values: diff --git a/frappe/model/document.py b/frappe/model/document.py index 3ad3574097..f0e87982de 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal') and not self.meta.get('virtual_doctype'): + if not self.get('__islocal') and not self.meta.get('is_virtual'): if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 8e953d16ae39bd40170c2bfeba2be84e919be1ab Mon Sep 17 00:00:00 2001 From: Shridhar Date: Thu, 24 Dec 2020 15:35:31 +0530 Subject: [PATCH 009/122] feat: added test to create virtual doctype --- frappe/core/doctype/doctype/test_doctype.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 10169073e5..bc92c84c67 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -477,6 +477,21 @@ class TestDocType(unittest.TestCase): }) self.assertRaises(InvalidFieldNameError, doc.validate_links_table_fieldnames) + def test_create_virtual_doctype(self): + """Test virtual DOcTYpe.""" + virtual_doc = new_doctype('Test Virtual Doctype') + virtual_doc.is_virtual = 1 + virtual_doc.permissions = [{ + "role": "System Manager", + "read": 1, + "write": 1, + }] + virtual_doc.insert() + virtual_doc.save() + doc = frappe.get_doc("DocType", "Test Virtual Doctype") + + self.assertEqual(doc.is_virtual, 1) + def new_doctype(name, unique=0, depends_on='', fields=None): doc = frappe.get_doc({ From a02d2fb6945578753fc915521387a9c31255f18c Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Thu, 24 Dec 2020 00:33:50 +0530 Subject: [PATCH 010/122] chore: change an instance of virtual_doctype to is_virtual --- frappe/database/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 64e444c0c0..5f5ba06d8b 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,7 +30,7 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.meta.get('virtual_doctype'): + if self.meta.get('is_virtual'): # no schema to sync for virtual doctypes return if self.is_new(): From 348cdaa73aa0862029887219652f95cf4c6accab Mon Sep 17 00:00:00 2001 From: Shridhar Date: Thu, 24 Dec 2020 16:10:37 +0530 Subject: [PATCH 011/122] fix: test create virtual doctype --- frappe/core/doctype/doctype/test_doctype.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index bc92c84c67..dc15853083 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -481,11 +481,6 @@ class TestDocType(unittest.TestCase): """Test virtual DOcTYpe.""" virtual_doc = new_doctype('Test Virtual Doctype') virtual_doc.is_virtual = 1 - virtual_doc.permissions = [{ - "role": "System Manager", - "read": 1, - "write": 1, - }] virtual_doc.insert() virtual_doc.save() doc = frappe.get_doc("DocType", "Test Virtual Doctype") From 26d65a39f130434b51bd3c194922da061f72101a Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 29 Dec 2020 11:19:35 +0530 Subject: [PATCH 012/122] feat: added test. Virtual doctype should not create table --- frappe/core/doctype/doctype/test_doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index dc15853083..6cba80c2ff 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -486,7 +486,7 @@ class TestDocType(unittest.TestCase): doc = frappe.get_doc("DocType", "Test Virtual Doctype") self.assertEqual(doc.is_virtual, 1) - + self.assertFalse(frappe.db.table_exists('Test Virtual Doctype')) def new_doctype(name, unique=0, depends_on='', fields=None): doc = frappe.get_doc({ From bf0070f090d40cfdbb66ef57f487dbd9e4757893 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 29 Dec 2020 14:00:20 +0530 Subject: [PATCH 013/122] feat: added controller required for virtual doctype in boilerplate --- .../doctype/doctype/boilerplate/controller._py | 2 +- frappe/modules/utils.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/boilerplate/controller._py b/frappe/core/doctype/doctype/boilerplate/controller._py index 97e23c0037..583bd30908 100644 --- a/frappe/core/doctype/doctype/boilerplate/controller._py +++ b/frappe/core/doctype/doctype/boilerplate/controller._py @@ -7,4 +7,4 @@ from __future__ import unicode_literals {base_class_import} class {classname}({base_class}): - pass + {custom_controller} diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index b3debfc43c..132aa1e2a5 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -247,6 +247,21 @@ def make_boilerplate(template, doc, opts=None): base_class = 'NestedSet' base_class_import = 'from frappe.utils.nestedset import NestedSet' + custom_controller = 'pass' + if doc.get('is_virtual'): + custom_controller = """ + def db_insert(self): + pass + + def load_from_db(self): + pass + + def db_update(self): + pass + + def get_list(self, args): + pass""" + with open(target_file_path, 'w') as target: with open(os.path.join(get_module_path("core"), "doctype", scrub(doc.doctype), "boilerplate", template), 'r') as source: @@ -257,5 +272,6 @@ def make_boilerplate(template, doc, opts=None): classname=doc.name.replace(" ", ""), base_class_import=base_class_import, base_class=base_class, - doctype=doc.name, **opts) + doctype=doc.name, **opts, + custom_controller=custom_controller) )) From 4b14ae656d1ac22aee1960b374253e4612ec419f Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 5 Jan 2021 18:54:42 +0530 Subject: [PATCH 014/122] fix: added check to create virtual doctype Create virtual doctype only for non custom doctype Create virtual doctype only if developer mode is set --- frappe/core/doctype/doctype/doctype.js | 1 + frappe/core/doctype/doctype/doctype.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index b3469abf29..254ec82e36 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -18,6 +18,7 @@ frappe.ui.form.on('DocType', { frm.set_value("custom", 1); } frm.toggle_enable("custom", 0); + frm.toggle_enable("is_virtual", 0); frm.toggle_enable("beta", 0); } diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3e283e1699..e7ba8325d6 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -134,6 +134,9 @@ class DocType(Document): if not frappe.conf.get("developer_mode") and not self.custom: frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), CannotCreateStandardDoctypeError) + if self.is_virtual and self.custom: + frappe.throw(_("Not allowed to create custom Virtual DocType."), CannotCreateStandardDoctypeError) + def setup_fields_to_fetch(self): '''Setup query to update values for newly set fetch values''' try: From c7b29100af15c4d61935623b9a429e30a8d5f8a5 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Mon, 14 Dec 2020 14:05:15 +0530 Subject: [PATCH 015/122] feat: virtual doctype Virtual Doctype's data souurce can be anything a file or a secondary database table or an api --- frappe/core/doctype/doctype/doctype.json | 9 ++++++++- frappe/desk/form/load.py | 6 ++---- frappe/desk/reportview.py | 10 +++++++--- frappe/model/document.py | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 215ef8cd62..622ca94db8 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -22,6 +22,7 @@ "track_views", "custom", "beta", + "virtual_doctype", "fields_section_break", "fields", "sb1", @@ -528,6 +529,12 @@ "fieldname": "index_web_pages_for_search", "fieldtype": "Check", "label": "Index Web Pages for Search" + }, + { + "default": "0", + "fieldname": "virtual_doctype", + "fieldtype": "Check", + "label": "Virtual DocType" } ], "icon": "fa fa-bolt", @@ -609,7 +616,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-09-24 13:13:58.227153", + "modified": "2020-12-14 12:48:33.752219", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index cacbd3c633..e045a22c91 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -27,9 +27,6 @@ def getdoc(doctype, name, user=None): if not name: name = doctype - if not frappe.db.exists(doctype, name): - return [] - try: doc = frappe.get_doc(doctype, name) run_onload(doc) @@ -43,7 +40,8 @@ def getdoc(doctype, name, user=None): # add file list doc.add_viewed() get_docinfo(doc) - + except frappe.DoesNotExistError: + return [] except Exception: frappe.errprint(frappe.utils.get_traceback()) raise diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 9f5a5d84c8..6562e46a2b 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -12,15 +12,19 @@ from frappe import _ from six import string_types, StringIO from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cstr, format_duration +from frappe.model.base_document import get_controller @frappe.whitelist() @frappe.read_only() def get(): args = get_form_params() - - data = compress(execute(**args), args = args) - + # If virtual doctype get data from controller het_list method + if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="virtual_doctype"): + controller = get_controller(args.doctype) + data = compress(controller(args.doctype).get_list(args)) + else: + data = compress(execute(**args), args = args) return data def execute(doctype, *args, **kwargs): diff --git a/frappe/model/document.py b/frappe/model/document.py index 3789e20b19..8e7645153c 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal'): + if not self.get('__islocal') and not self.meta.virtual_doctype: if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 0b4940f017374bf7b1084fc7d5b93410366f5bf6 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:18:26 +0530 Subject: [PATCH 016/122] fix: skip table creation for virtual doctype --- frappe/database/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index daabbaa61c..f2d32e0e3c 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,9 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new(): + if self.is_new() and not self.meta.virtual_doctype: self.create() - else: + elif not self.meta.virtual_doctype: frappe.cache().hdel('table_columns', self.table_name) self.alter() From 156cc9805f74e4e0d7f3f983e56282a76acee4a0 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:19:09 +0530 Subject: [PATCH 017/122] fix: link validation fetch from get doc --- frappe/model/base_document.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 7a90ecaca5..f1667b4fa8 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -521,6 +521,9 @@ class BaseDocument(object): if frappe.get_meta(doctype).issingle: values.name = doctype + if frappe.get_meta(doctype).virtual_doctype: + values = frappe.get_doc(doctype, docname) + if values: setattr(self, df.fieldname, values.name) From 1bd0cf72ce469c59615cecadccf480140fda4937 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 15 Dec 2020 16:20:41 +0530 Subject: [PATCH 018/122] fix: listview throws table not exists error --- frappe/desk/reportview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6562e46a2b..6c9b373dbd 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -312,8 +312,9 @@ def get_stats(stats, doctype, filters=[]): try: columns = frappe.db.get_table_columns(doctype) - except frappe.db.InternalError: + except (frappe.db.InternalError, Exception): # raised when _user_tags column is added on the fly + # raised if its a virtual doctype columns = [] for tag in tags: From 092d32be3370ee54bfb02e76331f1a27ede35e82 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 16 Dec 2020 13:08:24 +0530 Subject: [PATCH 019/122] fix: fixed commenting and sharing for virtual doctypes --- frappe/core/doctype/comment/comment.py | 2 +- frappe/model/base_document.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index 04ecc83b38..01707978c8 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -144,7 +144,7 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): """Updates `_comments` property in parent Document with given dict. :param _comments: Dict of comments.""" - if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle"): + if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "virtual_doctype"): return try: diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index f1667b4fa8..e508d6abc2 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -505,18 +505,18 @@ class BaseDocument(object): not _df.get('fetch_if_empty') or (_df.get('fetch_if_empty') and not self.get(_df.fieldname)) ] + if not frappe.get_meta(doctype).virtual_doctype: + if not fields_to_fetch: + # cache a single value type + values = frappe._dict(name=frappe.db.get_value(doctype, docname, + 'name', cache=True)) + else: + values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] + for _df in fields_to_fetch] - if not fields_to_fetch: - # cache a single value type - values = frappe._dict(name=frappe.db.get_value(doctype, docname, - 'name', cache=True)) - else: - values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] - for _df in fields_to_fetch] - - # don't cache if fetching other values too - values = frappe.db.get_value(doctype, docname, - values_to_fetch, as_dict=True) + # don't cache if fetching other values too + values = frappe.db.get_value(doctype, docname, + values_to_fetch, as_dict=True) if frappe.get_meta(doctype).issingle: values.name = doctype From 42d889d2a41c75f383d36d7a1c5f00e02366ff2e Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 23 Dec 2020 20:38:06 +0530 Subject: [PATCH 020/122] fix: handle virtual doctype flag for existing doctypes --- frappe/database/schema.py | 4 ++-- frappe/model/document.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index f2d32e0e3c..00a59b93f7 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,9 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new() and not self.meta.virtual_doctype: + if self.is_new() and not self.meta.get('virtual_doctype'): self.create() - elif not self.meta.virtual_doctype: + elif not self.meta.get('virtual_doctype'): frappe.cache().hdel('table_columns', self.table_name) self.alter() diff --git a/frappe/model/document.py b/frappe/model/document.py index 8e7645153c..3ad3574097 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal') and not self.meta.virtual_doctype: + if not self.get('__islocal') and not self.meta.get('virtual_doctype'): if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 3aebcb1691613772efc198404e2e73be74aadb49 Mon Sep 17 00:00:00 2001 From: Shridhar Patil Date: Wed, 23 Dec 2020 22:32:11 +0530 Subject: [PATCH 021/122] chore: clean up database schema sync for virtual doctype Co-authored-by: Chinmay D. Pai Signed-off-by: Chinmay D. Pai --- frappe/database/schema.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 00a59b93f7..64e444c0c0 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,9 +30,12 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.is_new() and not self.meta.get('virtual_doctype'): + if self.meta.get('virtual_doctype'): + # no schema to sync for virtual doctypes + return + if self.is_new(): self.create() - elif not self.meta.get('virtual_doctype'): + else: frappe.cache().hdel('table_columns', self.table_name) self.alter() From d48e470b86819b8e22e8212889e910ef4ec8bc0f Mon Sep 17 00:00:00 2001 From: Shridhar Date: Wed, 23 Dec 2020 23:28:50 +0530 Subject: [PATCH 022/122] fix: renamed label and field name --- frappe/core/doctype/comment/comment.py | 2 +- frappe/core/doctype/doctype/doctype.json | 8 ++++---- frappe/desk/reportview.py | 2 +- frappe/model/base_document.py | 4 ++-- frappe/model/document.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index 01707978c8..6c403d8ad7 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -144,7 +144,7 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): """Updates `_comments` property in parent Document with given dict. :param _comments: Dict of comments.""" - if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "virtual_doctype"): + if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "is_virtual"): return try: diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 622ca94db8..91820926fa 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -22,7 +22,7 @@ "track_views", "custom", "beta", - "virtual_doctype", + "is_virtual", "fields_section_break", "fields", "sb1", @@ -532,9 +532,9 @@ }, { "default": "0", - "fieldname": "virtual_doctype", + "fieldname": "is_virtual", "fieldtype": "Check", - "label": "Virtual DocType" + "label": "Is Virtual" } ], "icon": "fa fa-bolt", @@ -616,7 +616,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-12-14 12:48:33.752219", + "modified": "2020-12-23 23:48:33.752219", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6c9b373dbd..e0754b41ec 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -20,7 +20,7 @@ from frappe.model.base_document import get_controller def get(): args = get_form_params() # If virtual doctype get data from controller het_list method - if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="virtual_doctype"): + if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="is_virtual"): controller = get_controller(args.doctype) data = compress(controller(args.doctype).get_list(args)) else: diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index e508d6abc2..44c012ace1 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -505,7 +505,7 @@ class BaseDocument(object): not _df.get('fetch_if_empty') or (_df.get('fetch_if_empty') and not self.get(_df.fieldname)) ] - if not frappe.get_meta(doctype).virtual_doctype: + if not frappe.get_meta(doctype).get('is_virtual'): if not fields_to_fetch: # cache a single value type values = frappe._dict(name=frappe.db.get_value(doctype, docname, @@ -521,7 +521,7 @@ class BaseDocument(object): if frappe.get_meta(doctype).issingle: values.name = doctype - if frappe.get_meta(doctype).virtual_doctype: + if frappe.get_meta(doctype).get('is_virtual'): values = frappe.get_doc(doctype, docname) if values: diff --git a/frappe/model/document.py b/frappe/model/document.py index 3ad3574097..f0e87982de 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -687,7 +687,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal') and not self.meta.get('virtual_doctype'): + if not self.get('__islocal') and not self.meta.get('is_virtual'): if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) From 612f062461e9e6afaf2b41e9a452d847ff8035d8 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Thu, 24 Dec 2020 15:35:31 +0530 Subject: [PATCH 023/122] feat: added test to create virtual doctype --- frappe/core/doctype/doctype/test_doctype.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 10169073e5..bc92c84c67 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -477,6 +477,21 @@ class TestDocType(unittest.TestCase): }) self.assertRaises(InvalidFieldNameError, doc.validate_links_table_fieldnames) + def test_create_virtual_doctype(self): + """Test virtual DOcTYpe.""" + virtual_doc = new_doctype('Test Virtual Doctype') + virtual_doc.is_virtual = 1 + virtual_doc.permissions = [{ + "role": "System Manager", + "read": 1, + "write": 1, + }] + virtual_doc.insert() + virtual_doc.save() + doc = frappe.get_doc("DocType", "Test Virtual Doctype") + + self.assertEqual(doc.is_virtual, 1) + def new_doctype(name, unique=0, depends_on='', fields=None): doc = frappe.get_doc({ From bcbe16236719ca2c7d70c85b052ab0de7c0f6d23 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Thu, 24 Dec 2020 00:33:50 +0530 Subject: [PATCH 024/122] chore: change an instance of virtual_doctype to is_virtual --- frappe/database/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 64e444c0c0..5f5ba06d8b 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,7 +30,7 @@ class DBTable: self.get_columns_from_docfields() def sync(self): - if self.meta.get('virtual_doctype'): + if self.meta.get('is_virtual'): # no schema to sync for virtual doctypes return if self.is_new(): From 97887f6af1166174eacf9af47ed4e235ec08a1da Mon Sep 17 00:00:00 2001 From: Shridhar Date: Thu, 24 Dec 2020 16:10:37 +0530 Subject: [PATCH 025/122] fix: test create virtual doctype --- frappe/core/doctype/doctype/test_doctype.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index bc92c84c67..dc15853083 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -481,11 +481,6 @@ class TestDocType(unittest.TestCase): """Test virtual DOcTYpe.""" virtual_doc = new_doctype('Test Virtual Doctype') virtual_doc.is_virtual = 1 - virtual_doc.permissions = [{ - "role": "System Manager", - "read": 1, - "write": 1, - }] virtual_doc.insert() virtual_doc.save() doc = frappe.get_doc("DocType", "Test Virtual Doctype") From 70dafa2dc0459eb10573891429773add379376f5 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 29 Dec 2020 11:19:35 +0530 Subject: [PATCH 026/122] feat: added test. Virtual doctype should not create table --- frappe/core/doctype/doctype/test_doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index dc15853083..6cba80c2ff 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -486,7 +486,7 @@ class TestDocType(unittest.TestCase): doc = frappe.get_doc("DocType", "Test Virtual Doctype") self.assertEqual(doc.is_virtual, 1) - + self.assertFalse(frappe.db.table_exists('Test Virtual Doctype')) def new_doctype(name, unique=0, depends_on='', fields=None): doc = frappe.get_doc({ From 08099056e39a21d62296a1285abad3d6d4586554 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 29 Dec 2020 14:00:20 +0530 Subject: [PATCH 027/122] feat: added controller required for virtual doctype in boilerplate --- .../doctype/doctype/boilerplate/controller._py | 2 +- frappe/modules/utils.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/boilerplate/controller._py b/frappe/core/doctype/doctype/boilerplate/controller._py index 97e23c0037..583bd30908 100644 --- a/frappe/core/doctype/doctype/boilerplate/controller._py +++ b/frappe/core/doctype/doctype/boilerplate/controller._py @@ -7,4 +7,4 @@ from __future__ import unicode_literals {base_class_import} class {classname}({base_class}): - pass + {custom_controller} diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index b3debfc43c..132aa1e2a5 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -247,6 +247,21 @@ def make_boilerplate(template, doc, opts=None): base_class = 'NestedSet' base_class_import = 'from frappe.utils.nestedset import NestedSet' + custom_controller = 'pass' + if doc.get('is_virtual'): + custom_controller = """ + def db_insert(self): + pass + + def load_from_db(self): + pass + + def db_update(self): + pass + + def get_list(self, args): + pass""" + with open(target_file_path, 'w') as target: with open(os.path.join(get_module_path("core"), "doctype", scrub(doc.doctype), "boilerplate", template), 'r') as source: @@ -257,5 +272,6 @@ def make_boilerplate(template, doc, opts=None): classname=doc.name.replace(" ", ""), base_class_import=base_class_import, base_class=base_class, - doctype=doc.name, **opts) + doctype=doc.name, **opts, + custom_controller=custom_controller) )) From fed664a4435133d1d25f1c4cbccd65efa93c5b69 Mon Sep 17 00:00:00 2001 From: Shridhar Date: Tue, 5 Jan 2021 18:54:42 +0530 Subject: [PATCH 028/122] fix: added check to create virtual doctype Create virtual doctype only for non custom doctype Create virtual doctype only if developer mode is set --- frappe/core/doctype/doctype/doctype.js | 1 + frappe/core/doctype/doctype/doctype.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index b3469abf29..254ec82e36 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -18,6 +18,7 @@ frappe.ui.form.on('DocType', { frm.set_value("custom", 1); } frm.toggle_enable("custom", 0); + frm.toggle_enable("is_virtual", 0); frm.toggle_enable("beta", 0); } diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 80a576230c..abe2e78e83 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -134,6 +134,9 @@ class DocType(Document): if not frappe.conf.get("developer_mode") and not self.custom: frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), CannotCreateStandardDoctypeError) + if self.is_virtual and self.custom: + frappe.throw(_("Not allowed to create custom Virtual DocType."), CannotCreateStandardDoctypeError) + def setup_fields_to_fetch(self): '''Setup query to update values for newly set fetch values''' try: From 66016ea76ccd3d0663fa37ef7b2adc39d75cbe95 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Mon, 11 Jan 2021 13:15:05 +0530 Subject: [PATCH 029/122] lint: fix spelling in comment Signed-off-by: Chinmay D. Pai --- 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 e0754b41ec..e6a4a45e0c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -19,7 +19,7 @@ from frappe.model.base_document import get_controller @frappe.read_only() def get(): args = get_form_params() - # If virtual doctype get data from controller het_list method + # If virtual doctype get data from controller get_list method if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="is_virtual"): controller = get_controller(args.doctype) data = compress(controller(args.doctype).get_list(args)) From 5830cdbd96a300cd18a378374a5a85b7e812e2c3 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Mon, 11 Jan 2021 14:40:24 +0530 Subject: [PATCH 030/122] fix: handle case when table does not exist in the database Signed-off-by: Chinmay D. Pai --- 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 e6a4a45e0c..b193635551 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -312,7 +312,7 @@ def get_stats(stats, doctype, filters=[]): try: columns = frappe.db.get_table_columns(doctype) - except (frappe.db.InternalError, Exception): + except (frappe.db.InternalError, frappe.db.ProgrammingError): # raised when _user_tags column is added on the fly # raised if its a virtual doctype columns = [] From 9d7468eb3dec0c2195c270c9ec20d28366b0e88b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 17 Mar 2021 16:04:19 +0530 Subject: [PATCH 031/122] fix: Typography for markdown content --- frappe/public/scss/website/doc.scss | 49 ---------- frappe/public/scss/website/markdown.scss | 119 +++++++++++++++++------ 2 files changed, 89 insertions(+), 79 deletions(-) diff --git a/frappe/public/scss/website/doc.scss b/frappe/public/scss/website/doc.scss index 1585f428f9..8cc12e7e55 100644 --- a/frappe/public/scss/website/doc.scss +++ b/frappe/public/scss/website/doc.scss @@ -169,25 +169,6 @@ $navbar-height-lg: 4.5rem; margin-top: 3rem; } - h1 { - font-size: $font-size-3xl; - font-weight: 500; - } - - h1 + p { - font-size: $font-size-lg; - } - - h2 { - font-size: $font-size-2xl; - font-weight: 500; - } - - h3 { - font-size: $font-size-xl; - font-weight: 500; - } - h1, h2, h3, @@ -202,36 +183,6 @@ $navbar-height-lg: 4.5rem; visibility: hidden; } } - - h4 { - font-size: $font-size-lg; - font-weight: 500; - } - - strong { - font-weight: 600; - } - - table { - border-color: $gray-200; - } - - table thead { - background-color: $light; - } - - .table-bordered, - .table-bordered th, - .table-bordered td { - border-left: none; - border-right: none; - border-color: $gray-200; - } - - .table-bordered thead th, - .table-bordered thead td { - border-bottom-width: 1px; - } } // next links diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 4b0c20cbc4..87a15ea275 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -1,9 +1,19 @@ +$font-sizes: ( + "sm": 0.75rem, + "base": 1rem, + "lg": 1.125rem, + "xl": 1.41rem, + "2xl": 1.6rem, + "3xl": 2rem +); + .from-markdown { color: $gray-700; - line-height: 1.625; + line-height: 1.7; + letter-spacing: -0.011em; > * + * { - margin-top: 1rem; + margin-top: 0.875rem; } > :first-child { @@ -27,13 +37,17 @@ list-style: decimal; } - li > * + * { - margin-top: 1rem; + li { + text-indent: 0.25rem; } - > ul > * + *, - > ol > * + * { - margin-top: 1rem; + li > * + * { + margin-top: 0.75rem; + } + + ul > * + *, + ol > * + * { + margin-top: 0.75rem; } > blockquote { @@ -55,60 +69,84 @@ b, strong { color: $gray-800; + font-weight: 600; } h1, h2, h3, h4, h5, h6 { color: $gray-900; + font-weight: 500; } - h1 + p { - margin-top: 0.75rem; - font-size: $font-size-base; + h1 { + font-size: map-get($font-sizes, '3xl'); + line-height: 1.5; + letter-spacing: -0.024em; - @include media-breakpoint-up(sm) { - margin-top: 1.25rem; - font-size: 1.125rem; - } - @include media-breakpoint-up(md) { - font-size: 1.25rem; + // for byline + & + p { + margin-top: 1.5rem; + margin-bottom: 1.25rem; + font-size: map-get($font-sizes, 'xl'); + letter-spacing: -0.0175em; + + // @include media-breakpoint-up(sm) { + // margin-top: 1.25rem; + // font-size: 1.125rem; + // } + // @include media-breakpoint-up(md) { + // font-size: 1.25rem; + // } } } h2 { + font-size: map-get($font-sizes, '2xl'); + line-height: 1.56; + letter-spacing: -0.0195em; margin-bottom: 1rem; - margin-top: 3.5rem; + margin-top: 3rem; } h3 { - margin-top: 3rem; + font-size: map-get($font-sizes, 'xl'); + line-height: 1.56; + letter-spacing: -0.0175em; + margin-top: 1.5rem; margin-bottom: 1rem; - font-weight: 600; - line-height: 1.25; - font-size: $font-size-xl; } h4 { + font-size: map-get($font-sizes, 'lg'); + line-height: 1.56; + letter-spacing: -0.014em; margin-top: 2.5rem; margin-bottom: 1rem; - font-size: 1.125rem; - font-weight: 600; - line-height: 1.25; } h5 { + font-size: map-get($font-sizes, 'base'); + line-height: 1.5; + letter-spacing: -0.011em; + font-weight: 600; margin-top: 2rem; margin-bottom: 1rem; - font-size: $font-size-base; - font-weight: 600; - line-height: 1.25; } h6 { + font-size: map-get($font-sizes, 'sm'); + line-height: 1.35; + font-weight: 600; + text-transform: uppercase; margin-top: 1.5rem; margin-bottom: 1rem; - font-size: $font-size-sm; - font-weight: 600; - line-height: 1.25; + } + + p + h3 { + margin-top: 3rem; + } + + h3 + p { + margin-top: 1rem; } tr > td, @@ -138,4 +176,25 @@ background: $light; border-radius: 0.125rem; } + + table { + border-color: $gray-200; + } + + table thead { + background-color: $light; + } + + .table-bordered, + .table-bordered th, + .table-bordered td { + border-left: none; + border-right: none; + border-color: $gray-200; + } + + .table-bordered thead th, + .table-bordered thead td { + border-bottom-width: 1px; + } } From 8ce75ba2d2c8b6313a99dcb9231f87c4df422357 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 14:36:06 +0530 Subject: [PATCH 032/122] fix: Styles for list items --- frappe/public/scss/website/markdown.scss | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 87a15ea275..bf03ad3cae 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -26,7 +26,7 @@ $font-sizes: ( ul, ol { - padding-left: 2.5rem; + padding-left: 2rem; } ul { @@ -39,15 +39,21 @@ $font-sizes: ( li { text-indent: 0.25rem; + padding-top: 1px; + padding-bottom: 1px; } - li > * + * { - margin-top: 0.75rem; + li > ul, li > ol { + padding-left: 1.5rem; + } + + ul > li:first-child { + margin-top: 3px; } ul > * + *, ol > * + * { - margin-top: 0.75rem; + margin-top: 2px; } > blockquote { From 68387f767a33b519f55e56be245646e8d63779f2 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 15:04:34 +0530 Subject: [PATCH 033/122] fix: Vertical spacing for headings and paragraphs --- frappe/public/scss/website/markdown.scss | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index bf03ad3cae..19e2133606 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -13,7 +13,8 @@ $font-sizes: ( letter-spacing: -0.011em; > * + * { - margin-top: 0.875rem; + margin-top: 0.75rem; + margin-bottom: 0; } > :first-child { @@ -109,16 +110,14 @@ $font-sizes: ( font-size: map-get($font-sizes, '2xl'); line-height: 1.56; letter-spacing: -0.0195em; - margin-bottom: 1rem; - margin-top: 3rem; + margin-top: 4rem; } h3 { font-size: map-get($font-sizes, 'xl'); line-height: 1.56; letter-spacing: -0.0175em; - margin-top: 1.5rem; - margin-bottom: 1rem; + margin-top: 2.25rem; } h4 { @@ -126,7 +125,6 @@ $font-sizes: ( line-height: 1.56; letter-spacing: -0.014em; margin-top: 2.5rem; - margin-bottom: 1rem; } h5 { @@ -135,7 +133,6 @@ $font-sizes: ( letter-spacing: -0.011em; font-weight: 600; margin-top: 2rem; - margin-bottom: 1rem; } h6 { @@ -144,15 +141,6 @@ $font-sizes: ( font-weight: 600; text-transform: uppercase; margin-top: 1.5rem; - margin-bottom: 1rem; - } - - p + h3 { - margin-top: 3rem; - } - - h3 + p { - margin-top: 1rem; } tr > td, From ab52f02b29d06223c8d29ec92e16954ce9cc2fc2 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 15:09:41 +0530 Subject: [PATCH 034/122] fix: Top spacing for screenshots --- frappe/public/scss/website/markdown.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 19e2133606..fe3c5c63c0 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -156,6 +156,7 @@ $font-sizes: ( .screenshot { border: 1px solid $gray-400; border-radius: 0.375rem; + margin-top: 0.5rem; } .screenshot + em { From 495ef5d5a82b430c78cd5829a9bc67f99c31937f Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 15:21:37 +0530 Subject: [PATCH 035/122] fix: Blockquote spacing --- frappe/public/scss/website/markdown.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index fe3c5c63c0..09fe8f0c9f 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -58,7 +58,7 @@ $font-sizes: ( } > blockquote { - padding: 1.25rem 1rem; + padding: 0.75rem 1rem 0.75rem 1.25rem; font-size: $font-size-sm; font-weight: 500; border: 1px solid $gray-200; From 6000abcf9f20e1af8c2a3607721148d093cff0a1 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 15:21:52 +0530 Subject: [PATCH 036/122] fix: Font weights for headings --- frappe/public/scss/website/markdown.scss | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 09fe8f0c9f..0595f96914 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -81,7 +81,14 @@ $font-sizes: ( h1, h2, h3, h4, h5, h6 { color: $gray-900; - font-weight: 500; + } + + h1 { + font-weight: 700; + } + + h2, h3, h4, h5, h6 { + font-weight: 600; } h1 { From f9464cbbb485250e160e4134153bef17772f948d Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 23 Mar 2021 15:25:17 +0530 Subject: [PATCH 037/122] fix: Spacing for byline --- frappe/public/scss/website/markdown.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 0595f96914..585baaaded 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -99,9 +99,9 @@ $font-sizes: ( // for byline & + p { margin-top: 1.5rem; - margin-bottom: 1.25rem; font-size: map-get($font-sizes, 'xl'); letter-spacing: -0.0175em; + line-height: 1.4; // @include media-breakpoint-up(sm) { // margin-top: 1.25rem; From 0d20d1a5d55104c07e3003d09814968ecd2281a4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 24 Mar 2021 17:36:23 +0530 Subject: [PATCH 038/122] fix: Read only mode for color input - fix style color readonly field - fix case where color control is hidden --- frappe/public/js/frappe/form/controls/color.js | 2 +- frappe/public/js/frappe/form/formatters.js | 6 ++++++ frappe/public/scss/common/color_picker.scss | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/color.js b/frappe/public/js/frappe/form/controls/color.js index 7890ea755c..bf04581abd 100644 --- a/frappe/public/js/frappe/form/controls/color.js +++ b/frappe/public/js/frappe/form/controls/color.js @@ -76,7 +76,7 @@ frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({ refresh() { this._super(); let color = this.get_color(); - if (this.picker.color !== color) { + if (this.picker && this.picker.color !== color) { this.picker.color = color; this.picker.refresh(); } diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 4578cf2ded..f792d5b173 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -293,6 +293,12 @@ frappe.form.formatters = { return frappe.format(value, link_field, options, row); }); return formatted_values.join(', '); + }, + Color: (value) => { + return `
+
+ ${value} +
`; } } diff --git a/frappe/public/scss/common/color_picker.scss b/frappe/public/scss/common/color_picker.scss index 627ab12a2e..7ab9b0c504 100644 --- a/frappe/public/scss/common/color_picker.scss +++ b/frappe/public/scss/common/color_picker.scss @@ -92,7 +92,7 @@ } } -.frappe-control[data-fieldtype='Color'] { +.frappe-control[data-fieldtype='Color'] { input { padding-left: 40px; } @@ -104,11 +104,20 @@ background-color: red; position: absolute; top: calc(50% + 1px); - left: 5px; + left: 8px; content: ' '; &.no-value { background: url('/assets/frappe/images/color-circle.png'); background-size: contain; } } -} \ No newline at end of file + .like-disabled-input { + .color-value { + padding-left: 25px; + } + .selected-color { + top: 20%; + cursor: default; + } + } +} From b22e5ddcb2d91c8b2fc69f6caefa5be052081219 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 24 Mar 2021 17:37:44 +0530 Subject: [PATCH 039/122] fix: Set color control as Data while adding filter --- frappe/public/js/frappe/ui/filters/filter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js index 945115af82..b2b2b623e2 100644 --- a/frappe/public/js/frappe/ui/filters/filter.js +++ b/frappe/public/js/frappe/ui/filters/filter.js @@ -495,6 +495,7 @@ frappe.ui.filter_utils = { 'Dynamic Link', 'Read Only', 'Assign', + 'Color', ].indexOf(df.fieldtype) != -1 ) { df.fieldtype = 'Data'; From c4b3507cf3c3f7073160413a857e19692f8a1918 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 25 Mar 2021 10:53:07 +0100 Subject: [PATCH 040/122] fix: unhide field, add intro --- frappe/website/web_form/request_data/request_data.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/website/web_form/request_data/request_data.json b/frappe/website/web_form/request_data/request_data.json index c3b0155d51..591ef4a031 100644 --- a/frappe/website/web_form/request_data/request_data.json +++ b/frappe/website/web_form/request_data/request_data.json @@ -8,18 +8,20 @@ "allow_print": 0, "amount": 0.0, "amount_based_on_field": 0, + "apply_document_permissions": 0, "breadcrumbs": "", - "button_label": "Submit", + "button_label": "Request Data", "creation": "2019-01-24 16:19:26.886096", "currency": "INR", "doc_type": "Personal Data Download Request", "docstatus": 0, "doctype": "Web Form", "idx": 0, + "introduction_text": "

Request a file containing your personally identifiable information (PII) that is saved on our system. The file will be in JSON format and is sent to you by email. If you would like to have your PII deleted from our system, please make a request to delete data.

", "is_standard": 1, "login_required": 0, "max_attachment_size": 0, - "modified": "2019-07-16 12:41:53.782126", + "modified": "2021-03-25 10:52:13.149538", "modified_by": "Administrator", "module": "Website", "name": "request-data", @@ -45,7 +47,7 @@ "max_length": 0, "max_value": 0, "options": "", - "read_only": 1, + "read_only": 0, "reqd": 1, "show_in_filter": 0 } From de31f578df0cb3a169bfd2f8d4b6926f02b0404b Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 25 Mar 2021 11:06:45 +0100 Subject: [PATCH 041/122] fix: add into to Request to Delete Data --- .../request_to_delete_data/request_to_delete_data.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json index 0aa1446ce3..8b97943e34 100644 --- a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json +++ b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json @@ -8,6 +8,7 @@ "allow_print": 0, "amount": 0.0, "amount_based_on_field": 0, + "apply_document_permissions": 0, "button_label": "Submit", "creation": "2019-01-25 14:24:12.588810", "currency": "INR", @@ -15,10 +16,11 @@ "docstatus": 0, "doctype": "Web Form", "idx": 0, + "introduction_text": "

Send a request to delete your personally identifiable information (PII) that is saved on our system. You will receive an email to verify your request. Once the request is verified we will take care of deleting your PII.

", "is_standard": 1, "login_required": 0, "max_attachment_size": 0, - "modified": "2019-02-21 17:14:55.095337", + "modified": "2021-03-25 11:06:01.812112", "modified_by": "Administrator", "module": "Website", "name": "request-to-delete-data", From 308f19eb7bc131597dc4f7f03a0c1bf3ecdf100b Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 25 Mar 2021 11:10:37 +0100 Subject: [PATCH 042/122] fix: intro of Request to Delete Data --- .../request_to_delete_data/request_to_delete_data.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json index 8b97943e34..b0180d833c 100644 --- a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json +++ b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json @@ -16,11 +16,11 @@ "docstatus": 0, "doctype": "Web Form", "idx": 0, - "introduction_text": "

Send a request to delete your personally identifiable information (PII) that is saved on our system. You will receive an email to verify your request. Once the request is verified we will take care of deleting your PII.

", + "introduction_text": "

Send a request to delete your personally identifiable information (PII) that is stored on our system. You will receive an email to verify your request. Once the request is verified we will take care of deleting your PII. If you just want to check what PII we have stored, you can request your data.

", "is_standard": 1, "login_required": 0, "max_attachment_size": 0, - "modified": "2021-03-25 11:06:01.812112", + "modified": "2021-03-25 11:08:49.580621", "modified_by": "Administrator", "module": "Website", "name": "request-to-delete-data", From 9ffe8e91f78accfaf4593e25dce5bc93b26ff443 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Fri, 26 Mar 2021 11:55:05 +0530 Subject: [PATCH 043/122] fix: Styles for mobile --- frappe/public/scss/website/markdown.scss | 64 +++++++++++++++--------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 585baaaded..c5f44d20d8 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -1,4 +1,4 @@ -$font-sizes: ( +$font-sizes-desktop: ( "sm": 0.75rem, "base": 1rem, "lg": 1.125rem, @@ -7,6 +7,15 @@ $font-sizes: ( "3xl": 2rem ); +$font-sizes-mobile: ( + "sm": 0.75rem, + "base": 1rem, + "lg": 1.125rem, + "xl": 1.25rem, + "2xl": 1.5rem, + "3xl": 1.75rem +); + .from-markdown { color: $gray-700; line-height: 1.7; @@ -83,59 +92,68 @@ $font-sizes: ( color: $gray-900; } - h1 { - font-weight: 700; - } - h2, h3, h4, h5, h6 { font-weight: 600; } h1 { - font-size: map-get($font-sizes, '3xl'); + font-size: map-get($font-sizes-mobile, '3xl'); line-height: 1.5; - letter-spacing: -0.024em; + letter-spacing: -0.021em; + font-weight: 700; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, '3xl'); + letter-spacing: -0.024em; + } // for byline & + p { margin-top: 1.5rem; - font-size: map-get($font-sizes, 'xl'); - letter-spacing: -0.0175em; + font-size: map-get($font-sizes-mobile, 'xl'); + letter-spacing: -0.014em; line-height: 1.4; - // @include media-breakpoint-up(sm) { - // margin-top: 1.25rem; - // font-size: 1.125rem; - // } - // @include media-breakpoint-up(md) { - // font-size: 1.25rem; - // } + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, 'xl'); + letter-spacing: -0.0175em; + } } } h2 { - font-size: map-get($font-sizes, '2xl'); + font-size: map-get($font-sizes-mobile, '2xl'); line-height: 1.56; - letter-spacing: -0.0195em; + letter-spacing: -0.015em; margin-top: 4rem; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, '2xl'); + letter-spacing: -0.0195em; + } } h3 { - font-size: map-get($font-sizes, 'xl'); + font-size: map-get($font-sizes-mobile, 'xl'); line-height: 1.56; - letter-spacing: -0.0175em; + letter-spacing: -0.014em; margin-top: 2.25rem; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, 'xl'); + letter-spacing: -0.0175em; + } } h4 { - font-size: map-get($font-sizes, 'lg'); + font-size: map-get($font-sizes-mobile, 'lg'); line-height: 1.56; letter-spacing: -0.014em; margin-top: 2.5rem; } h5 { - font-size: map-get($font-sizes, 'base'); + font-size: map-get($font-sizes-mobile, 'base'); line-height: 1.5; letter-spacing: -0.011em; font-weight: 600; @@ -143,7 +161,7 @@ $font-sizes: ( } h6 { - font-size: map-get($font-sizes, 'sm'); + font-size: map-get($font-sizes-mobile, 'sm'); line-height: 1.35; font-weight: 600; text-transform: uppercase; From 1c0e5c20c9b5509688f6f11980356411619b9e79 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 24 Mar 2021 17:06:08 +0530 Subject: [PATCH 044/122] fix: awesomplete style in page form --- frappe/public/scss/desk/list.scss | 4 ---- frappe/public/scss/desk/page.scss | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss index b7a195718e..ea3401b09e 100644 --- a/frappe/public/scss/desk/list.scss +++ b/frappe/public/scss/desk/list.scss @@ -332,10 +332,6 @@ input.list-check-all, input.list-row-checkbox { } .page-form { - // .awesomplete > ul { - // min-width: 300px; - // } - .standard-filter-section { flex-wrap: wrap; // width: 65%; diff --git a/frappe/public/scss/desk/page.scss b/frappe/public/scss/desk/page.scss index 85831dc2a0..65d535facc 100644 --- a/frappe/public/scss/desk/page.scss +++ b/frappe/public/scss/desk/page.scss @@ -117,6 +117,10 @@ display: none; } } + + .awesomplete > ul { + min-width: 300px; + } } .form-inner-toolbar { From e286cbfc8bcc830096c53c853b3eb5f5c78ef735 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 25 Mar 2021 10:07:36 +0530 Subject: [PATCH 045/122] fix: render info type comments on form timeline --- frappe/desk/form/load.py | 1 + frappe/public/js/frappe/form/footer/form_timeline.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 1f5c437330..c1429d361f 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -100,6 +100,7 @@ def get_docinfo(doc=None, doctype=None, name=None): "assignment_logs": get_comments(doc.doctype, doc.name, 'assignment'), "permissions": get_doc_permissions(doc), "shared": frappe.share.get_users(doc.doctype, doc.name), + "info_logs": get_comments(doc.doctype, doc.name, 'Info'), "share_logs": get_comments(doc.doctype, doc.name, 'share'), "like_logs": get_comments(doc.doctype, doc.name, 'Like'), "views": get_view_logs(doc.doctype, doc.name), diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index 7b8d36d90b..1da59a2fdf 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -139,6 +139,7 @@ class FormTimeline extends BaseTimeline { this.timeline_items.push(...this.get_custom_timeline_contents()); this.timeline_items.push(...this.get_assignment_timeline_contents()); this.timeline_items.push(...this.get_attachment_timeline_contents()); + this.timeline_items.push(...this.get_info_timeline_contents()); this.timeline_items.push(...this.get_milestone_timeline_contents()); } } @@ -269,6 +270,17 @@ class FormTimeline extends BaseTimeline { return assignment_timeline_contents; } + get_info_timeline_contents() { + let info_timeline_contents = []; + (this.doc_info.info_logs || []).forEach(info_log => { + info_timeline_contents.push({ + creation: info_log.creation, + content: `${this.get_user_link(info_log.comment_email)} ${info_log.content}`, + }); + }); + return info_timeline_contents; + } + get_attachment_timeline_contents() { let attachment_timeline_contents = []; (this.doc_info.attachment_logs || []).forEach(attachment_log => { From 64d2d99ba8b1eac311c3bd63ab7634cfa95a6ba7 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 12:24:54 +0530 Subject: [PATCH 046/122] fix: toggle modal scroll for field select in modal --- frappe/public/js/frappe/ui/filters/field_select.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frappe/public/js/frappe/ui/filters/field_select.js b/frappe/public/js/frappe/ui/filters/field_select.js index ed271a73aa..b7384ad8f1 100644 --- a/frappe/public/js/frappe/ui/filters/field_select.js +++ b/frappe/public/js/frappe/ui/filters/field_select.js @@ -36,6 +36,19 @@ frappe.ui.FieldSelect = Class.extend({ var item = me.awesomplete.get_item(value); me.$input.val(item.label); }); + this.$input.on("awesomplete-open", () => { + let modal = this.$input.parents('.modal-dialog')[0]; + console.log('opened', modal); + if (modal) { + $(modal).removeClass("modal-dialog-scrollable"); + } + }) + this.$input.on("awesomplete-close", () => { + let modal = this.$input.parents('.modal-dialog')[0]; + if (modal) { + $(modal).addClass("modal-dialog-scrollable"); + } + }); if(this.filter_fields) { for(var i in this.filter_fields) From ab65f7a7f050bc823b14472791e555a72aec8701 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 12:25:17 +0530 Subject: [PATCH 047/122] fix: don't show apply filter button if no filter button --- frappe/public/js/frappe/ui/filters/filter_list.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 0b2218ceda..709e2290b3 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -299,9 +299,12 @@ frappe.ui.FilterGroup = class { - + ${this.filter_button ? + `` + : '' + } ` From c0e949e8093255101f2f4b0592c696e8f0f10f04 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 13:13:21 +0530 Subject: [PATCH 048/122] fix: check if report result exists before formatting --- frappe/public/js/frappe/widgets/chart_widget.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js index 104c457991..47cb947a10 100644 --- a/frappe/public/js/frappe/widgets/chart_widget.js +++ b/frappe/public/js/frappe/widgets/chart_widget.js @@ -573,14 +573,17 @@ export default class ChartWidget extends Widget { xIsSeries: this.chart_doc.timeseries, shortenYAxisNumbers: 1 }, - tooltipOptions: { + }; + + if (this.report_result && this.report_result.chart) { + chart_args.tooltipOptions = { formatTooltipY: value => frappe.format(value, { fieldtype: this.report_result.chart.fieldtype, options: this.report_result.chart.options }, { always_show_decimals: true, inline: true }) } - }; + } if (this.chart_doc.type == "Heatmap") { const heatmap_year = parseInt(this.selected_heatmap_year || this.chart_settings.heatmap_year || this.chart_doc.heatmap_year); From e22542a2b143f797fb1600897d57d50b1e07923b Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 14:12:07 +0530 Subject: [PATCH 049/122] feat: don't render filters, sort, add doc button for dashbaord view --- frappe/public/js/frappe/list/base_list.js | 4 +++- frappe/public/js/frappe/list/list_view.js | 14 +++++++------- .../js/frappe/views/dashboard/dashboard_view.js | 6 ++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index 24e14ffc38..a2dc51d69b 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -285,6 +285,7 @@ frappe.views.BaseList = class BaseList { } setup_filter_area() { + if (this.hide_filters) return; this.filter_area = new FilterArea(this); if (this.filters && this.filters.length > 0) { @@ -293,6 +294,7 @@ frappe.views.BaseList = class BaseList { } setup_sort_selector() { + if (this.hide_sort_selector) return; this.sort_selector = new frappe.ui.SortSelector({ parent: this.$filter_section, doctype: this.doctype, @@ -410,7 +412,7 @@ frappe.views.BaseList = class BaseList { doctype: this.doctype, fields: this.get_fields(), filters: this.get_filters_for_args(), - order_by: this.sort_selector.get_sql_string(), + order_by: this.sort_selector && this.sort_selector.get_sql_string(), start: this.start, page_length: this.page_length, view: this.view, diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 48ae8b1d08..c55ec4b3ab 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -417,11 +417,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { get_no_result_message() { let help_link = this.get_documentation_link(); - let filters = this.filter_area.get(); - let no_result_message = filters.length + let filters = this.filter_area && this.filter_area.get(); + let no_result_message = filters && filters.length ? __("No {0} found", [__(this.doctype)]) : __("You haven't created a {0} yet", [__(this.doctype)]); - let new_button_label = filters.length + let new_button_label = filters && filters.length ? __("Create a new {0}", [__(this.doctype)]) : __("Create your first {0}", [__(this.doctype)]); let empty_state_image = @@ -461,7 +461,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } before_refresh() { - if (frappe.route_options) { + if (frappe.route_options && this.filter_area) { this.filters = this.parse_filters_from_route_options(); frappe.route_options = null; @@ -527,9 +527,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { this.view_name ); this.save_view_user_settings({ - filters: this.filter_area.get(), - sort_by: this.sort_selector.sort_by, - sort_order: this.sort_selector.sort_order, + filters: this.filter_area && this.filter_area.get(), + sort_by: this.sort_selector && this.sort_selector.sort_by, + sort_order: this.sort_selector && this.sort_selector.sort_order, }); this.toggle_paging && this.$paging_area.toggle(false); } diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js index 5137d840ec..252024463e 100644 --- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js +++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js @@ -20,6 +20,8 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView { setup_page() { this.hide_sidebar = true; this.hide_page_form = true; + this.hide_filters = true; + this.hide_sort_selector = true; super.setup_page(); } @@ -74,6 +76,10 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView { this.toggle_customization_buttons(false); } + set_primary_action() { + // Don't render Add doc button for dashboard view + } + toggle_customization_buttons(show) { this.save_customizations_button.toggle(show); this.discard_customizations_button.toggle(show); From 7fe570c297eb5cbbc46acde82637138db2251fe4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 14:30:39 +0530 Subject: [PATCH 050/122] style: fix formatting --- frappe/public/js/frappe/ui/filters/field_select.js | 3 +-- frappe/public/js/frappe/ui/filters/filter_list.js | 2 ++ frappe/public/js/frappe/widgets/chart_widget.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/filters/field_select.js b/frappe/public/js/frappe/ui/filters/field_select.js index b7384ad8f1..c362214ce2 100644 --- a/frappe/public/js/frappe/ui/filters/field_select.js +++ b/frappe/public/js/frappe/ui/filters/field_select.js @@ -38,11 +38,10 @@ frappe.ui.FieldSelect = Class.extend({ }); this.$input.on("awesomplete-open", () => { let modal = this.$input.parents('.modal-dialog')[0]; - console.log('opened', modal); if (modal) { $(modal).removeClass("modal-dialog-scrollable"); } - }) + }); this.$input.on("awesomplete-close", () => { let modal = this.$input.parents('.modal-dialog')[0]; if (modal) { diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 709e2290b3..3dff0013b9 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -283,6 +283,7 @@ frappe.ui.FilterGroup = class { } get_filter_area_template() { + /* eslint-disable indent */ return $(`
@@ -309,6 +310,7 @@ frappe.ui.FilterGroup = class {
` ); + /* eslint-disable indent */ } get_filters_as_object() { diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js index 47cb947a10..01314b436f 100644 --- a/frappe/public/js/frappe/widgets/chart_widget.js +++ b/frappe/public/js/frappe/widgets/chart_widget.js @@ -582,7 +582,7 @@ export default class ChartWidget extends Widget { fieldtype: this.report_result.chart.fieldtype, options: this.report_result.chart.options }, { always_show_decimals: true, inline: true }) - } + }; } if (this.chart_doc.type == "Heatmap") { From 3c33393084f60609244a0cf66dc1304c4e735f32 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Fri, 26 Mar 2021 14:42:54 +0530 Subject: [PATCH 051/122] fix: Search icon in search bar --- frappe/templates/doc.html | 15 --------------- frappe/website/js/website.js | 6 +++--- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/frappe/templates/doc.html b/frappe/templates/doc.html index 3a566a1227..f3a8ab8cc8 100644 --- a/frappe/templates/doc.html +++ b/frappe/templates/doc.html @@ -23,21 +23,6 @@
`); target.empty(); From 8af1616656b18b02b5d891ec4ab3cdb388a1e662 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 26 Mar 2021 17:00:11 +0530 Subject: [PATCH 052/122] fix: render map view in Views menu --- frappe/public/icons/timeless/symbol-defs.svg | 6 ++++++ frappe/public/js/frappe/list/base_list.js | 4 +++- frappe/public/js/frappe/list/list_view_select.js | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 2b0cc8b696..d2c162161f 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -693,4 +693,10 @@ + + + + + + diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index a2dc51d69b..beacb136e6 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -179,7 +179,8 @@ frappe.views.BaseList = class BaseList { 'Calendar': 'calendar', 'Gantt': 'gantt', 'Kanban': 'kanban', - 'Dashboard': 'dashboard' + 'Dashboard': 'dashboard', + 'Map': 'map', }; if (frappe.boot.desk_settings.view_switcher) { @@ -823,6 +824,7 @@ frappe.views.view_modes = [ "Image", "Inbox", "Tree", + "Map", ]; frappe.views.is_valid = (view_mode) => frappe.views.view_modes.includes(view_mode); diff --git a/frappe/public/js/frappe/list/list_view_select.js b/frappe/public/js/frappe/list/list_view_select.js index 826158ff3f..9607be6e90 100644 --- a/frappe/public/js/frappe/list/list_view_select.js +++ b/frappe/public/js/frappe/list/list_view_select.js @@ -123,7 +123,14 @@ frappe.views.ListViewSelect = class ListViewSelect { kanbans => this.setup_kanban_switcher(kanbans) ); } - } + }, + Map: { + condition: this.list_view.settings.get_coords_method || + (this.list_view.meta.fields.find(i => i.fieldname === "latitude") && + this.list_view.meta.fields.find(i => i.fieldname === "longitude")) || + (this.list_view.meta.fields.find(i => i.fieldname === 'location' && i.fieldtype == 'Geolocation')), + action: () => this.set_route("map") + }, }; frappe.views.view_modes.forEach(view => { From 8cdb4598951ad34132aaa9d17b2890761a93a273 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 27 Mar 2021 17:46:15 +0530 Subject: [PATCH 053/122] chore: Remove unused code (#12703) --- socketio.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/socketio.js b/socketio.js index 796b8aa94d..d5d6523752 100644 --- a/socketio.js +++ b/socketio.js @@ -2,24 +2,12 @@ var app = require('express')(); var server = require('http').Server(app); var io = require('socket.io')(server); var cookie = require('cookie'); -var fs = require('fs'); -var path = require('path'); var request = require('superagent'); var { get_conf, get_redis_subscriber } = require('./node_utils'); const log = console.log; // eslint-disable-line var conf = get_conf(); -var files_struct = { - name: null, - type: null, - size: 0, - data: [], - slice: 0, - site_name: null, - is_private: 0 -}; - var subscriber = get_redis_subscriber(); // serve socketio @@ -43,7 +31,6 @@ io.on('connection', function (socket) { } socket.user = cookie.parse(socket.request.headers.cookie).user_id; - socket.files = {}; // frappe.chat socket.on("frappe.chat.room:subscribe", function (rooms) { @@ -97,10 +84,6 @@ io.on('connection', function (socket) { join_chat_room(); - socket.on('disconnect', function () { - delete socket.files; - }); - socket.on('task_subscribe', function (task_id) { var room = get_task_room(socket, task_id); socket.join(room); From bbeb24123230593d7b5b237dd0eb0e21f9248f85 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 27 Mar 2021 17:52:00 +0530 Subject: [PATCH 054/122] fix: Always validate file URLs (#12685) * feat: validate file urls * fix: validate file url while downloading content * fix: cleaner validation * fix: added separate validation for web URLs Co-authored-by: Saurabh Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/core/doctype/file/file.py | 127 +++++++++++++++----------- frappe/core/doctype/file/test_file.py | 19 +++- 2 files changed, 92 insertions(+), 54 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 0cf38508b8..f55214d160 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -94,52 +94,89 @@ class File(Document): self.set_file_name() self.validate_duplicate_entry() self.validate_attachment_limit() + self.validate_folder() - if not self.file_url and not self.flags.ignore_file_validate: - if not self.is_folder: + if self.is_folder: + self.file_url = "" + else: + self.validate_url() + + self.file_size = frappe.form_dict.file_size or self.file_size + + def validate_url(self): + if not self.file_url or self.file_url.startswith(("http://", "https://")): + if not self.flags.ignore_file_validate: self.validate_file() - self.generate_content_hash() - if frappe.db.exists('File', {'name': self.name, 'is_folder': 0}): - old_file_url = self.file_url - if not self.is_folder and (self.is_private != self.db_get('is_private')): - private_files = frappe.get_site_path('private', 'files') - public_files = frappe.get_site_path('public', 'files') + return - file_name = self.file_url.split('/')[-1] - if not self.is_private: - shutil.move(os.path.join(private_files, file_name), - os.path.join(public_files, file_name)) + # Probably an invalid web URL + if not self.file_url.startswith(("/files/", "/private/files/")): + frappe.throw( + _("URL must start with http:// or https://"), + title=_('Invalid URL') + ) - self.file_url = "/files/{0}".format(file_name) + # Ensure correct formatting and type + self.file_url = unquote(self.file_url) + self.is_private = cint(self.is_private) - else: - shutil.move(os.path.join(public_files, file_name), - os.path.join(private_files, file_name)) + self.handle_is_private_changed() - self.file_url = "/private/files/{0}".format(file_name) + base_path = os.path.realpath(get_files_path(is_private=self.is_private)) + if not os.path.realpath(self.get_full_path()).startswith(base_path): + frappe.throw( + _("The File URL you've entered is incorrect"), + title=_('Invalid File URL') + ) - update_existing_file_docs(self) + def handle_is_private_changed(self): + if not frappe.db.exists( + 'File', { + 'name': self.name, + 'is_private': cint(not self.is_private) + } + ): + return - # update documents image url with new file url - if self.attached_to_doctype and self.attached_to_name: - if not self.attached_to_field: - field_name = None - reference_dict = frappe.get_doc(self.attached_to_doctype, self.attached_to_name).as_dict() - for key, value in reference_dict.items(): - if value == old_file_url: - field_name = key - break - self.attached_to_field = field_name - if self.attached_to_field: - frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, - self.attached_to_field, self.file_url) + old_file_url = self.file_url - self.validate_url() + file_name = self.file_url.split('/')[-1] + private_file_path = frappe.get_site_path('private', 'files', file_name) + public_file_path = frappe.get_site_path('public', 'files', file_name) - if self.file_url and (self.is_private != self.file_url.startswith('/private')): - frappe.throw(_('Invalid file URL. Please contact System Administrator.')) + if self.is_private: + shutil.move(public_file_path, private_file_path) + url_starts_with = "/private/files/" + else: + shutil.move(private_file_path, public_file_path) + url_starts_with = "/files/" + + self.file_url = "{0}{1}".format(url_starts_with, file_name) + update_existing_file_docs(self) + + if ( + not self.attached_to_doctype + or not self.attached_to_name + or not self.fetch_attached_to_field(old_file_url) + ): + return + + frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, + self.attached_to_field, self.file_url) + + def fetch_attached_to_field(self, old_file_url): + if self.attached_to_field: + return True + + reference_dict = frappe.get_doc( + self.attached_to_doctype, self.attached_to_name).as_dict() + + for key, value in reference_dict.items(): + if value == old_file_url: + self.attached_to_field = key + return True def validate_attachment_limit(self): attachment_limit = 0 @@ -335,8 +372,13 @@ class File(Document): def get_content(self): """Returns [`file_name`, `content`] for given file name `fname`""" + if self.is_folder: + frappe.throw(_("Cannot get file contents of a Folder")) + if self.get('content'): return self.content + + self.validate_url() file_path = self.get_full_path() # read the file @@ -423,23 +465,6 @@ class File(Document): else: raise Exception - - def validate_url(self, df=None): - if self.file_url: - if not self.file_url.startswith(("http://", "https://", "/files/", "/private/files/")): - frappe.throw(_("URL must start with 'http://' or 'https://'")) - return - - if not self.file_url.startswith(("http://", "https://")): - # local file - root_files_path = get_files_path(is_private=self.is_private) - if not os.path.commonpath([root_files_path]) == os.path.commonpath([root_files_path, self.get_full_path()]): - # basically the file url is skewed to not point to /files/ or /private/files - frappe.throw(_("{0} is not a valid file url").format(self.file_url)) - self.file_url = unquote(self.file_url) - self.file_size = frappe.form_dict.file_size or self.file_size - - def get_uploaded_content(self): # should not be unicode when reading a file, hence using frappe.form if 'filedata' in frappe.form_dict: diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index e627558680..216dfd5495 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -192,13 +192,10 @@ class TestSameContent(unittest.TestCase): class TestFile(unittest.TestCase): - - def setUp(self): self.delete_test_data() self.upload_file() - def tearDown(self): try: frappe.get_doc("File", {"file_name": "file_copy.txt"}).delete() @@ -352,6 +349,22 @@ class TestFile(unittest.TestCase): self.assertEqual(file1.file_url, file2.file_url) self.assertTrue(os.path.exists(file2.get_full_path())) + def test_parent_directory_validation_in_file_url(self): + file1 = frappe.get_doc({ + "doctype": "File", + "file_name": 'parent_dir.txt', + "attached_to_doctype": "", + "attached_to_name": "", + "is_private": 1, + "content": test_content1}).insert() + + file1.file_url = '/private/files/../test.txt' + self.assertRaises(frappe.exceptions.ValidationError, file1.save) + + # No validation to see if file exists + file1.reload() + file1.file_url = '/private/files/parent_dir2.txt' + file1.save() class TestAttachment(unittest.TestCase): test_doctype = 'Test For Attachment' From 5deec996b64da68d2a8a4a559922a547b413ab0f Mon Sep 17 00:00:00 2001 From: Shridhar Date: Mon, 29 Mar 2021 15:16:27 +0530 Subject: [PATCH 055/122] fix: updated postgres default port --- frappe/utils/connections.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/connections.py b/frappe/utils/connections.py index 6bd24d57ec..bd30c7c060 100644 --- a/frappe/utils/connections.py +++ b/frappe/utils/connections.py @@ -23,7 +23,7 @@ def is_open(ip, port, timeout=10): def check_database(): db_type = config.get("db_type", "mariadb") db_host = config.get("db_host", "localhost") - db_port = config.get("db_port", 3306 if db_type == "mariadb" else 5342) + db_port = config.get("db_port", 3306 if db_type == "mariadb" else 5432) return {db_type: is_open(db_host, db_port)} From f202953654b42e34c32fe5240ed6cf68ea5186c6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 29 Mar 2021 17:56:58 +0530 Subject: [PATCH 056/122] fix: Add version link to version content --- .../version_timeline_content_builder.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js index 0f57998475..a563286413 100644 --- a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js +++ b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js @@ -144,6 +144,27 @@ function get_version_timeline_content(version_doc, frm) { function get_version_comment(version_doc, text) { + // TODO: Replace with a better solution + if (text.includes(" { + if ($(element).is('a')) { + version_comment += unlinked_content ? frappe.utils.get_form_link('Version', version_doc.name, true, unlinked_content) : ""; + unlinked_content = ""; + version_comment += element.outerHTML; + } else { + unlinked_content += element.outerHTML || element.textContent; + } + }); + if (unlinked_content) { + version_comment += frappe.utils.get_form_link('Version', version_doc.name, true, unlinked_content); + } + return version_comment; + } return frappe.utils.get_form_link('Version', version_doc.name, true, text); } @@ -164,4 +185,5 @@ function get_user_link(doc) { return frappe.utils.get_form_link('User', user, true, user_display_text); } -export { get_version_timeline_content }; \ No newline at end of file +export { get_version_timeline_content }; + From 875056bb15233b03645e535e8427c05201398932 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 29 Mar 2021 18:22:12 +0530 Subject: [PATCH 057/122] fix: pull modules from Module Def instead of desktop config file (#12631) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/config/__init__.py | 46 ++++----------------------------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index cc9d0e6c67..30be82d0df 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -36,48 +36,10 @@ def get_modules_from_all_apps(): return modules_list def get_modules_from_app(app): - try: - modules = frappe.get_attr(app + '.config.desktop.get_data')() or {} - except ImportError: - return [] - - active_domains = frappe.get_active_domains() - - if isinstance(modules, dict): - active_modules_list = [] - for m, module in iteritems(modules): - module['module_name'] = m - module['app'] = app - active_modules_list.append(module) - else: - for m in modules: - if m.get("type") == "module" and "category" not in m: - m["category"] = "Modules" - - # Only newly formatted modules that have a category to be shown on desk - modules = [m for m in modules if m.get("category")] - active_modules_list = [] - - for m in modules: - to_add = True - module_name = m.get("module_name") - - # Check Domain - if is_domain(m) and module_name not in active_domains: - to_add = False - - # Check if config - if is_module(m) and not config_exists(app, frappe.scrub(module_name)): - to_add = False - - if "condition" in m and not m["condition"]: - to_add = False - - if to_add: - m["app"] = app - active_modules_list.append(m) - - return active_modules_list + return frappe.get_all('Module Def', + filters={'app_name': app}, + fields=['module_name', 'app_name as app'] + ) def get_all_empty_tables_by_module(): empty_tables = set(r[0] for r in frappe.db.multisql({ From a2ffea53f278d6101ec430bd7c026ef0c9204226 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 29 Mar 2021 21:44:08 +0530 Subject: [PATCH 058/122] fix(refactor): lockdown frappe.desk.reportview --- frappe/__init__.py | 2 +- frappe/desk/reportview.py | 153 ++++++++++++++++++++++++---------- frappe/model/db_query.py | 41 +-------- frappe/public/js/frappe/db.js | 2 +- 4 files changed, 114 insertions(+), 84 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 844a9238e3..d20e856177 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1378,7 +1378,7 @@ def get_list(doctype, *args, **kwargs): frappe.get_list("ToDo", fields="*", filters = {"description": ("like", "test%")}) """ import frappe.model.db_query - return frappe.model.db_query.DatabaseQuery(doctype).execute(None, *args, **kwargs) + return frappe.model.db_query.DatabaseQuery(doctype).execute(*args, **kwargs) def get_all(doctype, *args, **kwargs): """List database query via `frappe.model.db_query`. Will **not** check for permissions. diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 3003385601..e5c87d9603 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -8,6 +8,7 @@ import frappe, json from six.moves import range import frappe.permissions from frappe.model.db_query import DatabaseQuery +from frappe.model import default_fields, optional_fields from frappe import _ from six import string_types, StringIO from frappe.core.doctype.access_log.access_log import make_access_log @@ -18,10 +19,13 @@ from frappe.utils import cstr, format_duration @frappe.read_only() def get(): args = get_form_params() + return compress(execute(**args), args=args) - data = compress(execute(**args), args = args) - - return data +@frappe.whitelist() +@frappe.read_only() +def get_list(): + # uncompressed (refactored from frappe.model.db_query.get_list) + return execute(**get_form_params()) def execute(doctype, *args, **kwargs): return DatabaseQuery(doctype).execute(*args, **kwargs) @@ -29,9 +33,104 @@ def execute(doctype, *args, **kwargs): def get_form_params(): """Stringify GET request parameters.""" data = frappe._dict(frappe.local.form_dict) + clean_params(data) + parse_json(data) - is_report = data.get('view') == 'Report' + validate_fields(data) + if data.filters: + validate_filters(data, data.filters) + if data.or_filters: + validate_filters(data, data.or_filters) + data.strict = None + + return data + +def get_fields(data): + if ((isinstance(fields, string_types) and fields == "*") + or (isinstance(fields, (list, tuple)) and len(fields) == 1 and fields[0] == "*")): + data["fields"] = frappe.db.get_table_columns(data.doctype) + fields = data["fields"] + +def validate_fields(data): + update_star_field_param(data) + + for field in data.fields: + fieldname = extract_fieldname(field) + if is_standard(fieldname): continue + + meta, df = get_meta_and_docfield(fieldname, data) + + if not df: + raise_invalid_field(fieldname) + + # remove the field from the query if the report hide flag is set and current view is Report + if df.report_hide and data.view == 'Report': + data.fields.remove(field) + continue + + if df.fieldname in [_df.fieldname for _df in meta.get_high_permlevel_fields()]: + if df.get('permlevel') not in meta.get_permlevel_access(parenttype=data.doctype): + data.fields.remove(field) + +def validate_filters(data, filters): + if isinstance(filters, list): + # filters as list + for condition in filters: + if len(condition)==3: + # [fieldname, condition, value] + fieldname = condition[0] + if is_standard(fieldname): continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(condition[0]) + else: + # [doctype, fieldname, condition, value] + fieldname = condition[1] + if is_standard(fieldname): continue + meta = frappe.get_meta(condition[0]) + if not meta.get_field(fieldname): + raise_invalid_field(fieldname) + + else: + for fieldname in filters: + if is_standard(fieldname): continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(fieldname) + +def raise_invalid_field(fieldname): + frappe.throw(_('Field not permitted in query') + ': {0}'.format(fieldname), frappe.DataError) + +def is_standard(fieldname): + if '.' in fieldname: + parenttype, fieldname = get_parenttype_and_fieldname(fieldname, None) + return fieldname in default_fields or fieldname in optional_fields + +def extract_fieldname(field): + fieldname = field.split(" as ")[0] + + # certain functions allowed, extract the fieldname from the function + if (fieldname.startswith('count(') + or fieldname.startswith('sum(') + or fieldname.startswith('avg(')): + fieldname = fieldname.split('(', 1)[1].split(')')[0] + + return fieldname + +def get_meta_and_docfield(fieldname, data): + parenttype, fieldname = get_parenttype_and_fieldname(fieldname, data) + meta = frappe.get_meta(parenttype) + df = meta.get_field(fieldname) + return meta, df + +def update_star_field_param(data): + if ((isinstance(data.fields, string_types) and data.fields == "*") + or (isinstance(data.fields, (list, tuple)) and len(data.fields) == 1 and data.fields[0] == "*")): + data.fields = frappe.db.get_table_columns(data.doctype) + + +def clean_params(data): data.pop('cmd', None) data.pop('data', None) data.pop('ignore_permissions', None) @@ -41,8 +140,12 @@ def get_form_params(): if "csrf_token" in data: del data["csrf_token"] + +def parse_json(data): if isinstance(data.get("filters"), string_types): data["filters"] = json.loads(data["filters"]) + if isinstance(data.get("or_filters"), string_types): + data["or_filters"] = json.loads(data["or_filters"]) if isinstance(data.get("fields"), string_types): data["fields"] = json.loads(data["fields"]) if isinstance(data.get("docstatus"), string_types): @@ -52,47 +155,8 @@ def get_form_params(): else: data["save_user_settings"] = True - fields = data["fields"] - if ((isinstance(fields, string_types) and fields == "*") - or (isinstance(fields, (list, tuple)) and len(fields) == 1 and fields[0] == "*")): - parenttype = data.doctype - data["fields"] = frappe.db.get_table_columns(parenttype) - fields = data["fields"] - - for field in fields: - key = field.split(" as ")[0] - - if key.startswith('count('): continue - if key.startswith('sum('): continue - if key.startswith('avg('): continue - - parenttype, fieldname = get_parent_dt_and_field(key, data) - - if fieldname == "*": - # * inside list is not allowed with other fields - fields.remove(field) - - meta = frappe.get_meta(parenttype) - df = meta.get_field(fieldname) - - report_hide = df.report_hide if df else None - - # remove the field from the query if the report hide flag is set and current view is Report - if report_hide and is_report: - fields.remove(field) - - if df and fieldname in [df.fieldname for df in meta.get_high_permlevel_fields()]: - if df.get('permlevel') not in meta.get_permlevel_access(parenttype=data.doctype) and field in fields: - fields.remove(field) - - # queries must always be server side - data.query = None - data.strict = None - - return data - -def get_parent_dt_and_field(field, data): +def get_parenttype_and_fieldname(field, data): if "." in field: parenttype, fieldname = field.split(".")[0][4:-1], field.split(".")[1].strip("`") else: @@ -101,7 +165,6 @@ def get_parent_dt_and_field(field, data): return parenttype, fieldname - def compress(data, args = {}): """separate keys and values""" from frappe.desk.query_report import add_total_row diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 8eac75eb65..924686299a 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -32,7 +32,7 @@ class DatabaseQuery(object): self.flags = frappe._dict() self.reference_doctype = None - def execute(self, query=None, fields=None, filters=None, or_filters=None, + def execute(self, fields=None, filters=None, or_filters=None, docstatus=None, group_by=None, order_by=None, limit_start=False, limit_page_length=None, as_list=False, with_childnames=False, debug=False, ignore_permissions=False, user=None, with_comment_count=False, @@ -104,12 +104,9 @@ class DatabaseQuery(object): # no table & ignore_ddl, return if not self.columns: return [] - if query: - result = self.run_custom_query(query) - else: - result = self.build_and_run() - if return_query: - return result + result = self.build_and_run() + if return_query: + return result if with_comment_count and not as_list and self.doctype: self.add_comment_count(result) @@ -707,12 +704,6 @@ class DatabaseQuery(object): return " and ".join(conditions) if conditions else "" - - def run_custom_query(self, query): - if '%(key)s' in query: - query = query.replace('%(key)s', '`name`') - return frappe.db.sql(query, as_dict = (not self.as_list)) - def set_order_by(self, args): meta = frappe.get_meta(self.doctype) @@ -819,30 +810,6 @@ def get_order_by(doctype, meta): return order_by - -@frappe.whitelist() -def get_list(doctype, *args, **kwargs): - '''wrapper for DatabaseQuery''' - kwargs.pop('cmd', None) - kwargs.pop('ignore_permissions', None) - kwargs.pop('data', None) - kwargs.pop('strict', None) - kwargs.pop('user', None) - - # If doctype is child table - if frappe.is_table(doctype): - # Example frappe.db.get_list('Purchase Receipt Item', {'parent': 'Purchase Receipt'}) - # Here purchase receipt is the parent doctype of the child doctype Purchase Receipt Item - - if not kwargs.get('parent'): - frappe.flags.error_message = _('Parent is required to get child table data') - raise frappe.PermissionError(doctype) - - check_parent_permission(kwargs.get('parent'), doctype) - del kwargs['parent'] - - return DatabaseQuery(doctype).execute(None, *args, **kwargs) - def is_parent_only_filter(doctype, filters): #check if filters contains only parent doctype only_parent_doctype = True diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index cf716c67e5..1cfc6f3696 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -15,7 +15,7 @@ frappe.db = { } return new Promise ((resolve) => { frappe.call({ - method: 'frappe.model.db_query.get_list', + method: 'frappe.desk.reportview.get_list', args: args, type: 'GET', callback: function(r) { From 511a5ddc4bbb9bf3652b75c7f1ba644be6ac2d7e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 29 Mar 2021 22:02:21 +0530 Subject: [PATCH 059/122] fix(minor): fieldnames can't have commas --- frappe/desk/reportview.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index e5c87d9603..69bc94f383 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -108,6 +108,9 @@ def is_standard(fieldname): return fieldname in default_fields or fieldname in optional_fields def extract_fieldname(field): + if ',' in field: + raise_invalid_field(field) + fieldname = field.split(" as ")[0] # certain functions allowed, extract the fieldname from the function From 0c995940c126ba8685111bbba5d2b2d3b5249de2 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 29 Mar 2021 22:04:04 +0530 Subject: [PATCH 060/122] fix(minor): functions must end with bracket --- frappe/desk/reportview.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 69bc94f383..72c452ce0c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -117,6 +117,8 @@ def extract_fieldname(field): if (fieldname.startswith('count(') or fieldname.startswith('sum(') or fieldname.startswith('avg(')): + if not fieldname.endswith(')'): + raise_invalid_field(field) fieldname = fieldname.split('(', 1)[1].split(')')[0] return fieldname From 0def97543b2d16c75acb9449d7558a4ced39b8c1 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 29 Mar 2021 22:22:20 +0530 Subject: [PATCH 061/122] fix(minor): handle as fieldnames --- frappe/desk/reportview.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 72c452ce0c..8bad1a2612 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -111,15 +111,18 @@ def extract_fieldname(field): if ',' in field: raise_invalid_field(field) - fieldname = field.split(" as ")[0] + fieldname = field + for sep in (' as ', ' AS '): + if sep in fieldname: + fieldname = fieldname.split(sep)[0] # certain functions allowed, extract the fieldname from the function if (fieldname.startswith('count(') or fieldname.startswith('sum(') or fieldname.startswith('avg(')): - if not fieldname.endswith(')'): + if not fieldname.strip().endswith(')'): raise_invalid_field(field) - fieldname = fieldname.split('(', 1)[1].split(')')[0] + fieldname = fieldname.split('(', 1)[1][:-1] return fieldname From c9b367933a3de7e587acd12060a6da53cce4de3f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 29 Mar 2021 23:16:33 +0530 Subject: [PATCH 062/122] fix(minor): make group_by validation tighter --- frappe/model/db_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 924686299a..2d553335ee 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -745,7 +745,7 @@ class DatabaseQuery(object): return _lower = parameters.lower() - if 'select' in _lower and ' from ' in _lower: + if 'select' in _lower and 'from' in _lower: frappe.throw(_('Cannot use sub-query in order by')) if re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*").match(_lower): From 58d25810027f5ba85f5b35c706a6957dedba07b2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Mar 2021 09:39:20 +0530 Subject: [PATCH 063/122] chore(deps): [security] bump y18n from 4.0.0 to 4.0.1 (#12714) Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1. **This update includes a security fix.** - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3999616099..44f5555568 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8525,16 +8525,11 @@ xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -"y18n@^3.2.1 || ^4.0.0": +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" From 6d978a1df0b7c7cbebf244fa134a7fde6ad88a79 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 30 Mar 2021 09:47:07 +0530 Subject: [PATCH 064/122] fix(report): move count, aggregation to serverside --- frappe/desk/reportview.py | 24 +++++++++++++++++++ frappe/public/js/frappe/db.js | 14 +++++------ .../public/js/frappe/ui/group_by/group_by.js | 16 ++----------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 8bad1a2612..ae58f3baea 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -27,6 +27,13 @@ def get_list(): # uncompressed (refactored from frappe.model.db_query.get_list) return execute(**get_form_params()) +@frappe.whitelist() +@frappe.read_only() +def get_count(): + args = get_form_params() + args.fields = ['{distinct}count(name) as total_count'.format(distinct = 'distinct ' if args.distinct=='true' else '')] + return execute(**args)[0].get('total_count') + def execute(doctype, *args, **kwargs): return DatabaseQuery(doctype).execute(*args, **kwargs) @@ -35,6 +42,7 @@ def get_form_params(): data = frappe._dict(frappe.local.form_dict) clean_params(data) parse_json(data) + setup_group_by(data) validate_fields(data) if data.filters: @@ -99,6 +107,22 @@ def validate_filters(data, filters): if not df: raise_invalid_field(fieldname) +def setup_group_by(data): + ''' + Add columns for aggregated values e.g. count(name) + ''' + if data.group_by: + if data.aggregate_function.lower() not in ('count', 'sum', 'avg'): + frappe.throw('Invalid aggregate function') + if '`' in data.aggregate_on: + raise_invalid_field(data.aggregate_on) + data.fields.append('{aggregate_function}(`tab{doctype}`.`{aggregate_on}`) AS _aggregate_column'.format(**data)) + if data.aggregate_on: + data.fields.append(data.aggregate_on) + + data.pop('aggregate_on') + data.pop('aggregate_function') + def raise_invalid_field(fieldname): frappe.throw(_('Field not permitted in query') + ': {0}'.format(fieldname), frappe.DataError) diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index 1cfc6f3696..6073c7d3f0 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -92,25 +92,25 @@ frappe.db = { }, count: function(doctype, args={}) { let filters = args.filters || {}; - const with_child_table_filter = Array.isArray(filters) && filters.some(filter => { + + // has a filter with childtable? + const distinct = Array.isArray(filters) && filters.some(filter => { return filter[0] !== doctype; }); - const fields = [ - // cannot break this line as it adds extra \n's and \t's which breaks the query - `count(${with_child_table_filter ? 'distinct': ''} ${frappe.model.get_full_column_name('name', doctype)}) AS total_count` - ]; + const fields = []; return frappe.call({ type: 'GET', - method: 'frappe.desk.reportview.get', + method: 'frappe.desk.reportview.get_count', args: { doctype, filters, fields, + distinct, } }).then(r => { - return r.message.values[0][0]; + return r.message.values; }); }, get_link_options(doctype, txt = '', filters={}) { diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js index fa699b1a91..53e4914f0d 100644 --- a/frappe/public/js/frappe/ui/group_by/group_by.js +++ b/frappe/public/js/frappe/ui/group_by/group_by.js @@ -286,15 +286,6 @@ frappe.ui.GroupBy = class { set_args(args) { if (this.aggregate_function && this.group_by) { - let aggregate_column, aggregate_on_field; - - if (this.aggregate_function === 'count') { - aggregate_column = 'count(`tab' + this.doctype + '`.`name`)'; - } else { - aggregate_column = `${this.aggregate_function}(${this.aggregate_on})`; - aggregate_on_field = this.aggregate_on; - } - this.report_view.group_by = this.group_by; this.report_view.sort_by = '_aggregate_column'; this.report_view.sort_order = 'desc'; @@ -316,17 +307,14 @@ frappe.ui.GroupBy = class { '_aggregate_column', this.aggregate_on_doctype || this.doctype, ]); - args.fields.push(aggregate_column + ' as _aggregate_column'); - - if (aggregate_on_field) { - args.fields.push(aggregate_on_field); - } // setup columns in datatable this.report_view.setup_columns(); Object.assign(args, { with_comment_count: false, + aggregate_on: this.aggregate_on || 'name', + aggregate_function: this.aggregate_function || 'count', group_by: this.report_view.group_by || null, order_by: '_aggregate_column desc', }); From f1f64772a5bfae07583c8a9217f6d895ba46c6af Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 30 Mar 2021 10:30:20 +0530 Subject: [PATCH 065/122] fix(minor): disallow comments in fields --- frappe/desk/reportview.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index ae58f3baea..1e6b4e35b5 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -132,8 +132,9 @@ def is_standard(fieldname): return fieldname in default_fields or fieldname in optional_fields def extract_fieldname(field): - if ',' in field: - raise_invalid_field(field) + for text in (',', '/*', '#'): + if text in field: + raise_invalid_field(field) fieldname = field for sep in (' as ', ' AS '): From a638618788612825c0ef526a1204f6c6025a877e Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 30 Mar 2021 10:42:34 +0530 Subject: [PATCH 066/122] fix: translation syntax --- frappe/public/js/frappe/ui/filters/filter_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 3dff0013b9..38950db29d 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -294,7 +294,7 @@ frappe.ui.FilterGroup = class {