From 77895bbcfc3efa8614ba011732f27d5f69faa386 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 3 Aug 2021 12:43:01 +0530 Subject: [PATCH 01/21] feat: Support for Column comparison in DatabaseQuery --- frappe/model/db_query.py | 7 ++++++- frappe/query_builder/__init__.py | 2 +- frappe/query_builder/utils.py | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 7ed681644f..f007c4874b 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -3,6 +3,7 @@ """build query for doclistview and return results""" import frappe.defaults +from frappe.query_builder.utils import Column import frappe.share from frappe import _ import frappe.permissions @@ -546,8 +547,12 @@ class DatabaseQuery(object): value = flt(f.value) fallback = 0 + if isinstance(f.value, Column): + quote = '"' if frappe.conf.db_type == 'postgres' else "`" + value = f"{tname}.{quote}{f.value}{quote}" + # escape value - if isinstance(value, str) and not f.operator.lower() == 'between': + elif isinstance(value, str) and not f.operator.lower() == 'between': value = f"{frappe.db.escape(value, percent=False)}" if ( diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index 798c34b6cc..cf39550100 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1 +1 @@ -from frappe.query_builder.utils import get_query_builder +from frappe.query_builder.utils import Column, Data, get_query_builder diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index b52a3606e8..30e48f7e60 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -4,9 +4,26 @@ from typing import Any, Callable, Dict from pypika import Query import frappe + from .builder import MariaDB, Postgres +class Column: + """Represents a Database Column""" + def __init__(self, name) -> None: + self.name = name + + def __str__(self) -> str: + return self.name + +class Data: + """Represents a Data value...Specifically non column types""" + def __init__(self, name) -> None: + self.name = name + + def __str__(self) -> str: + return self.name + class db_type_is(Enum): MARIADB = "mariadb" POSTGRES = "postgres" From c8d1c26ac159dcc83a39447ef6773f72934c6603 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 7 Sep 2021 16:42:52 +0530 Subject: [PATCH 02/21] fix: Dashboard Setting already exist error --- .../js/frappe/views/workspace/workspace.js | 83 +++++++++---------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 1e0143c2a8..46aa0d389d 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -35,42 +35,6 @@ frappe.views.Workspace = class Workspace { 'My Workspaces', 'Public' ]; - this.tools = { - header: { - class: this.blocks['header'], - inlineToolbar: true - }, - paragraph: { - class: this.blocks['paragraph'], - inlineToolbar: true - }, - chart: { - class: this.blocks['chart'], - config: { - page_data: this.page_data || [] - } - }, - card: { - class: this.blocks['card'], - config: { - page_data: this.page_data || [] - } - }, - shortcut: { - class: this.blocks['shortcut'], - config: { - page_data: this.page_data || [] - } - }, - onboarding: { - class: this.blocks['onboarding'], - config: { - page_data: this.page_data || [] - } - }, - spacer: this.blocks['spacer'], - spacingTune: frappe.wspace_block.tunes['spacing_tune'], - }; this.prepare_container(); this.setup_pages(); @@ -86,7 +50,7 @@ frappe.views.Workspace = class Workspace { this.body = this.wrapper.find(".layout-main-section"); } - setup_pages() { + setup_pages(reload) { this.get_pages().then(pages => { this.all_pages = pages.pages; this.has_access = pages.has_access; @@ -115,7 +79,7 @@ frappe.views.Workspace = class Workspace { this.new_page = null; } this.make_sidebar(); - frappe.router.route(); + reload && this.show(); } }); } @@ -236,10 +200,7 @@ frappe.views.Workspace = class Workspace { return; } - let page = { - name: this.get_page_to_show().name, - public: this.get_page_to_show().public - }; + let page = this.get_page_to_show(); this.page.set_title(`${__(page.name)}`); this.show_page(page); @@ -670,6 +631,42 @@ frappe.views.Workspace = class Workspace { } initialize_editorjs(blocks) { + this.tools = { + header: { + class: this.blocks['header'], + inlineToolbar: true + }, + paragraph: { + class: this.blocks['paragraph'], + inlineToolbar: true + }, + chart: { + class: this.blocks['chart'], + config: { + page_data: this.page_data || [] + } + }, + card: { + class: this.blocks['card'], + config: { + page_data: this.page_data || [] + } + }, + shortcut: { + class: this.blocks['shortcut'], + config: { + page_data: this.page_data || [] + } + }, + onboarding: { + class: this.blocks['onboarding'], + config: { + page_data: this.page_data || [] + } + }, + spacer: this.blocks['spacer'], + spacingTune: frappe.wspace_block.tunes['spacing_tune'], + }; this.editor = new EditorJS({ data: { blocks: blocks || [] @@ -751,7 +748,7 @@ frappe.views.Workspace = class Workspace { reload() { this.$page.prepend(frappe.render_template('workspace_loading_skeleton')); this.$page.find('.codex-editor').addClass('hidden'); - this.setup_pages(); + this.setup_pages(true); this.undo.readOnly = true; } }; From a69020f2f3dd15d5dc10811567c10706556916aa Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 7 Sep 2021 19:01:41 +0530 Subject: [PATCH 03/21] fix: Page load optimization --- frappe/desk/doctype/workspace/workspace.py | 6 ++--- .../js/frappe/views/workspace/workspace.js | 27 ++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 25dd9b26d2..a0a22a43fc 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -208,17 +208,17 @@ def save_page(title, icon, parent, public, sb_public_items, sb_private_items, de if loads(deleted_pages): return delete_pages(loads(deleted_pages)) - return {"name": title, "public": public} + return {"name": title, "public": public, "label": doc.label} def delete_pages(deleted_pages): for page in deleted_pages: if page.get("public") and "Workspace Manager" not in frappe.get_roles(): - return {"name": page.get("title"), "public": 1} + return {"name": page.get("title"), "public": 1, "label": page.get("label")} if frappe.db.exists("Workspace", page.get("name")): frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True) - return {"name": "Home", "public": 1} + return {"name": "Home", "public": 1, "label": "Home"} def sort_pages(sb_public_items, sb_private_items): wspace_public_pages = get_page_list(['name', 'title'], {'public': 1}) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 46aa0d389d..36b2988935 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -23,6 +23,7 @@ frappe.views.Workspace = class Workspace { this.blocks = frappe.wspace_block.blocks; this.is_read_only = true; this.new_page = null; + this.pages = {}; this.sorted_public_items = []; this.sorted_private_items = []; this.deleted_sidebar_items = []; @@ -211,6 +212,11 @@ frappe.views.Workspace = class Workspace { page: page }).then(data => { this.page_data = data; + + // caching page data + this.pages[page.name] && delete this.pages[page.name]; + this.pages[page.name] = data; + if (!this.page_data || Object.keys(this.page_data).length === 0) return; return frappe.dashboard_utils.get_dashboard_settings().then(settings => { @@ -221,6 +227,7 @@ frappe.views.Workspace = class Workspace { chart.chart_settings = chart_config[chart.chart_name] || {}; }); } + this.pages[page.name] = this.page_data; } }); }); @@ -242,7 +249,7 @@ frappe.views.Workspace = class Workspace { return { name: page, public: is_public }; } - show_page(page) { + async show_page(page) { let section = this.current_page.public ? 'public' : 'private'; if (this.sidebar_items && this.sidebar_items[section] && this.sidebar_items[section][this.current_page.name]) { this.sidebar_items[section][this.current_page.name][0].firstElementChild.classList.remove("selected"); @@ -277,12 +284,17 @@ frappe.views.Workspace = class Workspace { this.add_custom_cards_in_content(); $('.item-anchor').addClass('disable-click'); - this.get_data(this_page).then(() => { - this.prepare_editorjs(); - $('.item-anchor').removeClass('disable-click'); - this.$page.find('.codex-editor').removeClass('hidden'); - this.$page.find('.workspace-skeleton').remove(); - }); + + if (this.pages && this.pages[this_page.name]) { + this.page_data = this.pages[this_page.name]; + } else { + await this.get_data(this_page); + } + + this.prepare_editorjs(); + $('.item-anchor').removeClass('disable-click'); + this.$page.find('.codex-editor').removeClass('hidden'); + this.$page.find('.workspace-skeleton').remove(); } } @@ -727,6 +739,7 @@ frappe.views.Workspace = class Workspace { frappe.dom.unfreeze(); if (res.message) { me.new_page = res.message; + me.pages[res.message.label] && delete me.pages[res.message.label]; me.title = ''; me.icon = ''; me.parent = ''; From fd18da985d0786fd65c8720e6ea12e45a8f31d7b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 7 Sep 2021 20:22:45 +0530 Subject: [PATCH 04/21] fix: Add page to sidebar minor fix --- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 36b2988935..8989814349 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -625,7 +625,7 @@ frappe.views.Workspace = class Workspace { let $sidebar_section = is_public ? $sidebar[1] : $sidebar[0]; if (!parent) { - !is_public && $sidebar.last().removeClass('hidden'); + !is_public && $sidebar.first().removeClass('hidden'); $sidebar_item.appendTo($sidebar_section); } else { let $item_container = $($sidebar_section).find(`[item-name="${parent}"]`); From 90bc9e23174b559fbfac0e3b708fee2e77cd6c53 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 9 Sep 2021 13:20:12 +0530 Subject: [PATCH 05/21] fix(UI): Clicking area around file view row opens the file --- frappe/public/js/frappe/list/list_view.js | 1 + frappe/public/js/frappe/views/file/file_view.js | 6 ++++-- frappe/public/scss/desk/list.scss | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index c5db7df88c..d7e808dd9f 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1139,6 +1139,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { if ( $target.hasClass("filterable") || $target.hasClass("select-like") || + $target.hasClass("file-select") || $target.hasClass("list-row-like") || $target.is(":checkbox") ) { diff --git a/frappe/public/js/frappe/views/file/file_view.js b/frappe/public/js/frappe/views/file/file_view.js index e020bff4dd..11204bb660 100644 --- a/frappe/public/js/frappe/views/file/file_view.js +++ b/frappe/public/js/frappe/views/file/file_view.js @@ -380,8 +380,10 @@ frappe.views.FileView = class FileView extends frappe.views.ListView { return `
- + + + ${file.subject_html} diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss index 7fe04338ee..848e60b491 100644 --- a/frappe/public/scss/desk/list.scss +++ b/frappe/public/scss/desk/list.scss @@ -131,7 +131,7 @@ } } - .select-like { + .select-like, .file-select { padding: 15px 0px 15px 15px; } } From 1e2287855496576fb373632d6f1879e7c4239123 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 9 Sep 2021 13:30:09 +0530 Subject: [PATCH 06/21] fix: remove unnecessary padding --- frappe/public/scss/desk/list.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss index 848e60b491..bdbcacd977 100644 --- a/frappe/public/scss/desk/list.scss +++ b/frappe/public/scss/desk/list.scss @@ -169,7 +169,7 @@ $level-margin-right: 8px; color: var(--text-color); } - .level-item { + .level-item:not(.file-select) { margin-right: $level-margin-right; } From 8d46b365d97c2dfdb2d7f96e9a840368d1983109 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 13 Sep 2021 21:30:51 +0530 Subject: [PATCH 07/21] chore(deps): bump ipython to latest version --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index be96520a02..a0ad0b6266 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,8 +24,7 @@ googlemaps~=4.4.5 gunicorn~=20.1.0 html2text==2020.1.16 html5lib~=1.1 -ipython~=7.16.1 -jedi==0.17.2 # not directly required. Pinned to fix upstream IPython issue (https://github.com/ipython/ipython/issues/12740) +ipython~=7.27.0 Jinja2~=3.0.1 ldap3~=2.9 markdown2~=2.4.0 From ae10160e618d9d413c25a5feaadcf3f057cc6dcb Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 14 Sep 2021 18:03:25 +0530 Subject: [PATCH 08/21] fix: section with collapsible content jerk issue --- frappe/public/scss/website/page_builder.scss | 5 ++++- .../section_with_collapsible_content.html | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/website/page_builder.scss b/frappe/public/scss/website/page_builder.scss index ff9f4ae1e6..252ad1bf9f 100644 --- a/frappe/public/scss/website/page_builder.scss +++ b/frappe/public/scss/website/page_builder.scss @@ -486,9 +486,12 @@ } .collapsible-content { + color: $gray-700; +} + +.collapsible-content p { margin-top: 1rem; margin-bottom: 0; - color: $gray-700; } .section-with-collapsible-content.align-center { diff --git a/frappe/website/web_template/section_with_collapsible_content/section_with_collapsible_content.html b/frappe/website/web_template/section_with_collapsible_content/section_with_collapsible_content.html index 355c25001c..84a009564c 100644 --- a/frappe/website/web_template/section_with_collapsible_content/section_with_collapsible_content.html +++ b/frappe/website/web_template/section_with_collapsible_content/section_with_collapsible_content.html @@ -21,7 +21,9 @@
- {{ frappe.utils.md_to_html(item.content) }} +
+ {{ frappe.utils.md_to_html(item.content) }} +
{%- endfor -%} From a298566d134ac6476558df1fba1eecb97ebf9914 Mon Sep 17 00:00:00 2001 From: ChillarAnand Date: Wed, 15 Sep 2021 16:42:11 +0530 Subject: [PATCH 09/21] fix: Use whoosh AsyncWriter to prevent write locks --- frappe/search/full_text_search.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/search/full_text_search.py b/frappe/search/full_text_search.py index 104398b0ef..560ad55bf3 100644 --- a/frappe/search/full_text_search.py +++ b/frappe/search/full_text_search.py @@ -8,6 +8,8 @@ from whoosh.index import create_in, open_dir, EmptyIndexError from whoosh.fields import TEXT, ID, Schema from whoosh.qparser import MultifieldParser, FieldsPlugin, WildcardPlugin from whoosh.query import Prefix +from whoosh.writing import AsyncWriter + class FullTextSearch: """ Frappe Wrapper for Whoosh """ @@ -75,7 +77,7 @@ class FullTextSearch: ix = self.get_index() with ix.searcher(): - writer = ix.writer() + writer = AsyncWriter(ix) writer.delete_by_term(self.id, document[self.id]) writer.add_document(**document) writer.commit(optimize=True) @@ -135,4 +137,4 @@ class FullTextSearch: return out def get_index_path(index_name): - return frappe.get_site_path("indexes", index_name) \ No newline at end of file + return frappe.get_site_path("indexes", index_name) From ad5783b1c3432f4f028afe2067a666277a4cc6d4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Sep 2021 11:38:23 +0530 Subject: [PATCH 10/21] fix: Use PyPika's Column also, Discard Data class. Since we're adding support for PyPika objects to natively interact with the Frappe ORM, this PR brings us closer toward that direction. --- frappe/model/db_query.py | 2 +- frappe/query_builder/__init__.py | 2 +- frappe/query_builder/utils.py | 17 +---------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 3d024fa69f..63f3a4695d 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -549,7 +549,7 @@ class DatabaseQuery(object): if isinstance(f.value, Column): quote = '"' if frappe.conf.db_type == 'postgres' else "`" - value = f"{tname}.{quote}{f.value}{quote}" + value = f"{tname}.{quote}{f.value.name}{quote}" # escape value elif isinstance(value, str) and not f.operator.lower() == 'between': diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index e09d7337e8..ca6d82e5f6 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1,3 +1,3 @@ from frappe.query_builder.utils import ( - Column, Data, get_query_builder, patch_query_execute + Column, get_query_builder, patch_query_execute ) diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index 9d6457f12d..c217d0975e 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -3,28 +3,13 @@ from typing import Any, Callable, Dict, get_type_hints from importlib import import_module from pypika import Query +from pypika.queries import Column import frappe from .builder import MariaDB, Postgres -class Column: - """Represents a Database Column""" - def __init__(self, name) -> None: - self.name = name - - def __str__(self) -> str: - return self.name - -class Data: - """Represents a Data value...Specifically non column types""" - def __init__(self, name) -> None: - self.name = name - - def __str__(self) -> str: - return self.name - class db_type_is(Enum): MARIADB = "mariadb" POSTGRES = "postgres" From 2756e226ec1404c1c2c2f5b90ac94a7a2416b1ab Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Sep 2021 12:03:08 +0530 Subject: [PATCH 11/21] fix: Do not allow to create workspace from list/form --- frappe/desk/doctype/workspace/workspace.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 1e111b8d12..756a40da4b 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -165,8 +165,6 @@ "default": "0", "fieldname": "is_standard", "fieldtype": "Check", - "in_list_view": 1, - "in_standard_filter": 1, "label": "Is Standard", "search_index": 1 }, @@ -181,7 +179,6 @@ "depends_on": "eval:doc.extends_another_page == 1 || doc.for_user", "fieldname": "extends", "fieldtype": "Link", - "in_standard_filter": 1, "label": "Extends", "options": "Workspace", "search_index": 1 @@ -228,6 +225,8 @@ "default": "0", "fieldname": "public", "fieldtype": "Check", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Public" }, { @@ -265,11 +264,13 @@ "label": "Roles" } ], + "in_create": 1, "links": [], - "modified": "2021-08-30 18:47:18.227154", + "modified": "2021-09-16 12:01:06.450621", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { From 2fa967ec44380c97f646b46892ec3f905b467cb4 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Sep 2021 12:32:55 +0530 Subject: [PATCH 12/21] fix: Remove Loading animation of editorjs --- frappe/public/scss/desk/desktop.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 1257d9b3a4..2ab6d98e20 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -886,6 +886,10 @@ body { } } + .codex-editor__loader { + display: none !important; + } + .codex-editor { min-height: 630px; From fb508607e0c45dc776a41c2fd661a145a751f9c0 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Thu, 16 Sep 2021 12:47:34 +0530 Subject: [PATCH 13/21] fix(grid): date or datetime fields disappearing on save --- frappe/public/js/frappe/form/grid_row_form.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row_form.js b/frappe/public/js/frappe/form/grid_row_form.js index 73131a00ae..31295899b5 100644 --- a/frappe/public/js/frappe/form/grid_row_form.js +++ b/frappe/public/js/frappe/form/grid_row_form.js @@ -123,10 +123,12 @@ export default class GridRowForm { .toggle(this.row.grid.is_editable()); } refresh_field(fieldname) { - if(this.fields_dict[fieldname]) { - this.fields_dict[fieldname].refresh(); - this.layout && this.layout.refresh_dependency(); - } + const field = this.fields_dict[fieldname]; + if (!field) return; + + field.docname = this.row.doc.name; + field.refresh(); + this.layout && this.layout.refresh_dependency(); } set_focus() { // wait for animation and then focus on the first row From 86fa811004502cf385c5f43e4477ca3a115bf76a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Sep 2021 13:04:25 +0530 Subject: [PATCH 14/21] test: Add test for DatabaseQuery Column comparison --- frappe/tests/test_db_query.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index c18db29259..84d0d31616 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -4,6 +4,7 @@ import frappe, unittest from frappe.model.db_query import DatabaseQuery from frappe.desk.reportview import get_filters_cond +from frappe.query_builder import Column from frappe.core.page.permission_manager.permission_manager import update, reset, add from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype @@ -373,6 +374,25 @@ class TestReportview(unittest.TestCase): owners = DatabaseQuery("DocType").execute(filters={"name": "DocType"}, pluck="owner") self.assertEqual(owners, ["Administrator"]) + def test_column_comparison(self): + """Test DatabaseQuery.execute to test column comparison + """ + users_unedited = frappe.get_all( + "User", + filters={"creation": Column("modified")}, + fields=["name", "creation", "modified"], + limit=1, + ) + users_edited = frappe.get_all( + "User", + filters={"creation": ("!=", Column("modified"))}, + fields=["name", "creation", "modified"], + limit=1, + ) + + self.assertEqual(users_unedited[0].modified, users_unedited[0].creation) + self.assertNotEqual(users_edited[0].modified, users_edited[0].creation) + def test_reportview_get(self): user = frappe.get_doc("User", "test@example.com") add_child_table_to_blog_post() From 3bde63eca26ceddd2832d5f9cf4a4b038855f9e9 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Thu, 16 Sep 2021 13:44:46 +0530 Subject: [PATCH 15/21] fix(UI): Checkbox hidden in List view in mobile view (#14144) --- frappe/public/js/frappe/list/list_view.js | 10 +++++----- frappe/public/scss/desk/mobile.scss | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 5f2c4a2a2a..bd2423384d 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -607,9 +607,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { const subject_field = this.columns[0].df; let subject_html = ` - - + ${__(subject_field.label)} @@ -646,7 +646,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
-
@@ -954,9 +954,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { let subject_html = ` - - + diff --git a/frappe/public/scss/desk/mobile.scss b/frappe/public/scss/desk/mobile.scss index 14fa25e50f..14bee62c74 100644 --- a/frappe/public/scss/desk/mobile.scss +++ b/frappe/public/scss/desk/mobile.scss @@ -202,8 +202,12 @@ body { } // listviews - .list-row { - padding: 13px 15px !important; + .select-like { + margin-right: unset !important; + } + + .list-count { + display: contents; } .doclist-row { From f24dd3ae3cb2291fd7957e1d866b5062979752bc Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Sep 2021 13:53:36 +0530 Subject: [PATCH 16/21] refactor: Access Log creation * Retry thrice before raising DuplicateEntryError * Don't commit if in_test --- frappe/core/doctype/access_log/access_log.py | 48 +++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/frappe/core/doctype/access_log/access_log.py b/frappe/core/doctype/access_log/access_log.py index d93da02d25..f631353d56 100644 --- a/frappe/core/doctype/access_log/access_log.py +++ b/frappe/core/doctype/access_log/access_log.py @@ -1,6 +1,7 @@ -# Copyright (c) 2019, Frappe Technologies and contributors +# Copyright (c) 2021, Frappe Technologies and contributors # License: MIT. See LICENSE import frappe +from tenacity import retry, retry_if_exception_type, stop_after_attempt from frappe.model.document import Document @@ -10,25 +11,40 @@ class AccessLog(Document): @frappe.whitelist() @frappe.write_only() -def make_access_log(doctype=None, document=None, method=None, file_type=None, - report_name=None, filters=None, page=None, columns=None): +@retry( + stop=stop_after_attempt(3), retry=retry_if_exception_type(frappe.DuplicateEntryError) +) +def make_access_log( + doctype=None, + document=None, + method=None, + file_type=None, + report_name=None, + filters=None, + page=None, + columns=None, +): user = frappe.session.user + in_request = frappe.request and frappe.request.method == "GET" - doc = frappe.get_doc({ - 'doctype': 'Access Log', - 'user': user, - 'export_from': doctype, - 'reference_document': document, - 'file_type': file_type, - 'report_name': report_name, - 'page': page, - 'method': method, - 'filters': frappe.utils.cstr(filters) if filters else None, - 'columns': columns - }) + doc = frappe.get_doc( + { + "doctype": "Access Log", + "user": user, + "export_from": doctype, + "reference_document": document, + "file_type": file_type, + "report_name": report_name, + "page": page, + "method": method, + "filters": frappe.utils.cstr(filters) if filters else None, + "columns": columns, + } + ) doc.insert(ignore_permissions=True) # `frappe.db.commit` added because insert doesnt `commit` when called in GET requests like `printview` - if frappe.request and frappe.request.method == 'GET': + # dont commit in test mode + if not frappe.flags.in_test or in_request: frappe.db.commit() From 09557ab8c74bc0c6d4a2f181660bb021eb61660e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Sep 2021 15:22:37 +0530 Subject: [PATCH 17/21] fix(db_query): Add fallback value irrespective of operator --- 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 f61f94f660..978f3062c5 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -492,7 +492,7 @@ class DatabaseQuery(object): f.value = date_range fallback = "'0001-01-01 00:00:00'" - if f.operator in ('>', '<') and (f.fieldname in ('creation', 'modified')): + if (f.fieldname in ('creation', 'modified')): value = cstr(f.value) fallback = "NULL" From ac9ab3bd95c8d18f8ec74efc89dd21f4a20cc7a6 Mon Sep 17 00:00:00 2001 From: Samuel Braun <42311986+MindLaborDev@users.noreply.github.com> Date: Thu, 16 Sep 2021 14:20:22 +0200 Subject: [PATCH 18/21] fix(database): Fixed sql convention inconsistency (#14230) --- frappe/database/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index c48e86d301..0ee11ea075 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -332,7 +332,7 @@ class Database(object): values[key] = value if isinstance(value, (list, tuple)): # value is a tuple like ("!=", 0) - _operator = value[0] + _operator = value[0].lower() values[key] = value[1] if isinstance(value[1], (tuple, list)): # value is a list in tuple ("in", ("A", "B")) From 9a66f185e7729a2f33a2641b1a03cc8bbc8ef7a7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 17 Sep 2021 10:28:49 +0530 Subject: [PATCH 19/21] fix: CSV upload failure --- frappe/public/js/frappe/form/grid.js | 9 +++++---- frappe/public/js/frappe/utils/utils.js | 11 ++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index dd1d622bab..e6629ba039 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -835,10 +835,11 @@ export default class Grid { $.each(row, (ci, value) => { var fieldname = fieldnames[ci]; var df = frappe.meta.get_docfield(me.df.options, fieldname); - - d[fieldnames[ci]] = value_formatter_map[df.fieldtype] - ? value_formatter_map[df.fieldtype](value) - : value; + if (df) { + d[fieldnames[ci]] = value_formatter_map[df.fieldtype] + ? value_formatter_map[df.fieldtype](value) + : value; + } }); } } diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 21841296dc..f534dff1c6 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -927,7 +927,16 @@ Object.assign(frappe.utils, { // decodes base64 to string let parts = dataURI.split(','); const encoded_data = parts[1]; - return decodeURIComponent(escape(atob(encoded_data))); + let decoded = atob(encoded_data); + try { + const escaped = escape(decoded); + decoded = decodeURIComponent(escaped); + + } catch (e) { + // pass decodeURIComponent failure + // just return atob response + } + return decoded; }, copy_to_clipboard(string) { let input = $(""); From a0e2d0ec20963c1a604053d6c5bdb12cb423dc0d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 17 Sep 2021 11:24:51 +0530 Subject: [PATCH 20/21] fix: Drop support for Python 3.6 * Bumping iPython by a minor version broke 3.6 installs for us via https://github.com/frappe/frappe/pull/14192 * We could just add another line in requirements.txt to solve this, but since PY36 is reaching end of life by end of this year and release of 3.10 is just around the corner, might as well just drop it now than later * Frappe v14 would probably have the support range of 3.7-3.10/11 given when we release it. Maintaining dependencies for such a large range can become cumbersome --- .github/workflows/docs-checker.yml | 2 +- .github/workflows/publish-assets-develop.yml | 2 +- .github/workflows/publish-assets-releases.yml | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml index 90453cd1b4..02a01bf4e4 100644 --- a/.github/workflows/docs-checker.yml +++ b/.github/workflows/docs-checker.yml @@ -12,7 +12,7 @@ jobs: - name: 'Setup Environment' uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: 'Clone repo' uses: actions/checkout@v2 diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index a23885b508..85f3f7c3b0 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -18,7 +18,7 @@ jobs: node-version: 14 - uses: actions/setup-python@v2 with: - python-version: '3.6' + python-version: '3.7' - name: Set up bench and build assets run: | npm install -g yarn diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml index a697517c23..a5cc1f8872 100644 --- a/.github/workflows/publish-assets-releases.yml +++ b/.github/workflows/publish-assets-releases.yml @@ -21,7 +21,7 @@ jobs: python-version: '12.x' - uses: actions/setup-python@v2 with: - python-version: '3.6' + python-version: '3.7' - name: Set up bench and build assets run: | npm install -g yarn diff --git a/setup.py b/setup.py index f4f826d6b1..71f7f1fab2 100644 --- a/setup.py +++ b/setup.py @@ -57,5 +57,5 @@ setup( { 'clean': CleanCommand }, - python_requires='>=3.6' + python_requires='>=3.7' ) From 47b0117eec3f5e1d6d5fb703cbbb53a26f9ed22e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 17 Sep 2021 13:23:15 +0530 Subject: [PATCH 21/21] fix(grid): Delete all functionality --- frappe/public/js/frappe/form/grid.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index e6629ba039..8afbfa561e 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -212,13 +212,12 @@ export default class Grid { delete_all_rows() { frappe.confirm(__("Are you sure you want to delete all rows?"), () => { - this.grid_rows.forEach(row => { - row.remove(); - }); - this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); - - this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').prop('checked', 0); + this.frm.doc[this.df.fieldname] = []; + $(this.parent).find('.rows').empty(); + this.grid_rows = []; this.refresh(); + this.frm && this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); + this.frm && this.frm.dirty(); this.scroll_to_top(); }); } @@ -244,8 +243,10 @@ export default class Grid { this.remove_rows_button.toggleClass('hidden', this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true); - this.remove_all_rows_button.toggleClass('hidden', - this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').length ? false : true); + + let select_all_checkbox_checked = this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').length; + let show_delete_all_btn = select_all_checkbox_checked && this.data.length > this.get_selected_children().length; + this.remove_all_rows_button.toggleClass('hidden', !show_delete_all_btn); } get_selected() {