From 521102b5891545b7e623cdddcf4b4b7a2dab1348 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 16 Aug 2022 18:23:23 +0530 Subject: [PATCH 01/57] fix: update public workspace name with json file --- frappe/__init__.py | 7 +++++++ frappe/desk/doctype/workspace/workspace.py | 20 +++++++++++++++++--- frappe/modules/export_file.py | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 1d3cf7f62e..4f312ef908 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,6 +16,7 @@ import inspect import json import os import re +import shutil import warnings from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, overload @@ -530,6 +531,12 @@ def throw( ) +def delete_folder(path: str) -> None: + """Delete folder and all the content inside that folder.""" + if os.path.exists(path): + shutil.rmtree(path) + + def create_folder(path, with_init=False): """Create a folder in the given path and add an `__init__.py` file (optional). diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 9fa99884fb..0e49d1d7e1 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -9,7 +9,7 @@ from frappe.desk.desktop import save_new_widget from frappe.desk.utils import validate_route_conflict from frappe.model.document import Document from frappe.model.rename_doc import rename_doc -from frappe.modules.export_file import export_to_files +from frappe.modules.export_file import delete_folder, export_to_files class Workspace(Document): @@ -28,8 +28,22 @@ class Workspace(Document): if disable_saving_as_public(): return - if frappe.conf.developer_mode and self.module and self.public: - export_to_files(record_list=[["Workspace", self.name]], record_module=self.module) + if frappe.conf.developer_mode and self.public: + if self.module: + export_to_files(record_list=[["Workspace", self.name]], record_module=self.module) + + if self.has_value_changed("title") or self.has_value_changed("module"): + previous = self.get_doc_before_save() + if previous and previous.get("module") and previous.get("title"): + delete_folder(previous.get("module"), "Workspace", previous.get("title")) + + def before_export(self, doc): + if doc.title != doc.label and doc.label == doc.name: + self.name = doc.name = doc.label = doc.title + + def after_delete(self): + if self.module: + delete_folder(self.module, "Workspace", self.title) @staticmethod def get_module_page_map(): diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index b448c04f2f..7f183a750d 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -92,6 +92,20 @@ def get_module_name(doc): return module +def delete_folder(module, dt, dn): + if frappe.db.get_value("Module Def", module, "custom"): + module_path = get_custom_module_path(module) + else: + module_path = get_module_path(module) + + dt, dn = scrub_dt_dn(dt, dn) + + # delete folder + folder = os.path.join(module_path, dt, dn) + + frappe.delete_folder(folder) + + def create_folder(module, dt, dn, create_init): if frappe.db.get_value("Module Def", module, "custom"): module_path = get_custom_module_path(module) From de4e3f0c8ce7390398c91848a63425dc5818191f Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 16 Aug 2022 18:24:14 +0530 Subject: [PATCH 02/57] fix: make fields that needs to be updated from UI read_only --- frappe/desk/doctype/workspace/workspace.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 032de9de4e..529fc8bf8b 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -107,7 +107,8 @@ { "fieldname": "icon", "fieldtype": "Icon", - "label": "Icon" + "label": "Icon", + "read_only": 1 }, { "fieldname": "links", @@ -122,18 +123,21 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Public", + "read_only": 1, "search_index": 1 }, { "fieldname": "title", "fieldtype": "Data", "label": "Title", + "read_only": 1, "reqd": 1 }, { "fieldname": "parent_page", "fieldtype": "Data", - "label": "Parent Page" + "label": "Parent Page", + "read_only": 1 }, { "default": "[]", @@ -145,7 +149,8 @@ { "fieldname": "sequence_id", "fieldtype": "Float", - "label": "Sequence Id" + "label": "Sequence Id", + "read_only": 1 }, { "fieldname": "roles", @@ -172,7 +177,7 @@ ], "in_create": 1, "links": [], - "modified": "2022-05-12 13:00:03.925605", + "modified": "2022-08-16 18:01:42.632238", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", From f7942a4a8bc2accde46e6772c03e9ce35f670b72 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:11:18 +0530 Subject: [PATCH 03/57] fix: Keep action button size consistent --- frappe/public/js/frappe/web_form/web_form.js | 8 ++++---- frappe/public/scss/website/web_form.scss | 2 +- .../doctype/web_form/templates/web_form.html | 20 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index ccd88f0ba3..41f8014662 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -81,11 +81,11 @@ export default class WebForm extends frappe.ui.FieldGroup { } $(".web-form-footer .web-form-actions .left-area").prepend(` - + `); $(".web-form-footer .web-form-actions .right-area").prepend(` - + `); $(".btn-previous").on("click", function () { @@ -399,7 +399,7 @@ export default class WebForm extends frappe.ui.FieldGroup { render_success_page(data) { if (this.allow_edit && data.name) { $(".success-page").append(` - + ${__("Edit your response", null, "Button in web form")} `); @@ -407,7 +407,7 @@ export default class WebForm extends frappe.ui.FieldGroup { if (this.login_required && !this.allow_multiple && !this.show_list && data.name) { $(".success-page").append(` - + ${__("View your response", null, "Button in web form")} `); diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 0a4350e0bf..6bc515b173 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -137,7 +137,7 @@ justify-content: space-between; .btn { - font-size: var(--font-size-base); + font-size: var(--text-base); } .center-area { diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index fce6401457..08a8b87097 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -3,25 +3,25 @@ {% block breadcrumbs %}{% endblock %} {% macro header_buttons() %} + {% if allow_edit and doc_name and not is_form_editable %} + + {{ _("Edit Response", null, "Button in web form") }} + {% endif %} + {% if allow_print and not is_new %} {% set print_format_url = "/printview?doctype=" + doc_type + "&name=" + doc_name + "&format=" + print_format %} - + {% endif %} - - {% if allow_edit and doc_name and not is_form_editable %} - - {{ _("Edit", null, "Button in web form") }} - {% endif %} {% endmacro %} {% macro action_buttons() %} {% if is_new or is_form_editable %} {% else %} {% if show_list %} - {{ _("See previous responses", null, "Button in web form") }} + {{ _("See previous responses", null, "Button in web form") }} {% endif %} {% if not login_required or allow_multiple %} - {{ _("Submit another response", null, "Button in web form") }} + {{ _("Submit another response", null, "Button in web form") }} {% endif %} {% endif %} From b0adf0779027db83a9f2f0c21a196a8489511489 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:14:46 +0530 Subject: [PATCH 04/57] chore: renamed is_form_editable to in_edit_mode --- frappe/public/js/frappe/web_form/web_form.js | 2 +- frappe/public/js/frappe/web_form/webform_script.js | 5 +---- .../website/doctype/web_form/templates/web_form.html | 6 +++--- frappe/website/doctype/web_form/web_form.py | 10 +++++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 41f8014662..f64fae52ff 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -26,7 +26,7 @@ export default class WebForm extends frappe.ui.FieldGroup { this.set_field_values(); this.setup_listeners(); - if (this.is_new || this.is_form_editable) { + if (this.is_new || this.in_edit_mode) { this.setup_primary_action(); } diff --git a/frappe/public/js/frappe/web_form/webform_script.js b/frappe/public/js/frappe/web_form/webform_script.js index 75bbce9b27..f24e10cff3 100644 --- a/frappe/public/js/frappe/web_form/webform_script.js +++ b/frappe/public/js/frappe/web_form/webform_script.js @@ -36,9 +36,6 @@ frappe.ready(function () { function show_form() { let web_form = new WebForm({ parent: $(".web-form-wrapper"), - is_new: web_form_doc.is_new, - is_form_editable: web_form_doc.is_form_editable, - web_form_name: web_form_doc.name, }); let doc = reference_doc || {}; setup_fields(web_form_doc, doc); @@ -58,7 +55,7 @@ frappe.ready(function () { function setup_fields(web_form_doc, doc_data) { web_form_doc.web_form_fields.forEach((df) => { df.is_web_form = true; - df.read_only = !web_form_doc.is_new && !web_form_doc.is_form_editable; + df.read_only = !web_form_doc.is_new && !web_form_doc.in_edit_mode; if (df.fieldtype === "Table") { df.get_data = () => { let data = []; diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 08a8b87097..8d3df9b1b5 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -3,7 +3,7 @@ {% block breadcrumbs %}{% endblock %} {% macro header_buttons() %} - {% if allow_edit and doc_name and not is_form_editable %} + {% if allow_edit and doc_name and not in_edit_mode %} {{ _("Edit Response", null, "Button in web form") }} {% endif %} @@ -18,11 +18,11 @@ {% endmacro %} {% macro action_buttons() %} - {% if is_new or is_form_editable %} + {% if is_new or in_edit_mode %}
- {% if is_form_editable %} + {% if in_edit_mode %} {{ _("Reset Form", null, "Button in web form") }} {% else %} {{ _("Clear Form", null, "Button in web form") }} diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 718088212f..8f50731ef7 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -124,7 +124,7 @@ def get_context(context): def get_context(self, context): """Build context to render the `web_form.html` template""" - context.is_form_editable = False + context.in_edit_mode = False self.set_web_form_module() if frappe.form_dict.is_list: @@ -159,7 +159,7 @@ def get_context(context): frappe.redirect(f"/{self.route}/{frappe.form_dict.name}") if frappe.form_dict.is_edit: - context.is_form_editable = True + context.in_edit_mode = True if ( not frappe.form_dict.is_edit @@ -167,7 +167,7 @@ def get_context(context): and self.allow_edit and frappe.form_dict.name ): - context.is_form_editable = True + context.in_edit_mode = True frappe.redirect(f"/{frappe.local.path}/edit") if ( @@ -203,7 +203,7 @@ def get_context(context): # load web form doc context.web_form_doc = self.as_dict(no_nulls=True) - context.web_form_doc.update(dict_with_keys(context, ["is_list", "is_new", "is_form_editable"])) + context.web_form_doc.update(dict_with_keys(context, ["is_list", "is_new", "in_edit_mode"])) if self.show_sidebar and self.website_sidebar: context.sidebar_items = get_sidebar_items(self.website_sidebar) @@ -281,7 +281,7 @@ def get_context(context): context.title = strip_html( context.reference_doc.get(context.reference_doc.meta.get_title_field()) ) - if context.is_form_editable and context.parents: + if context.in_edit_mode and context.parents: context.parents.append( { "label": _(context.title), From 7cd528084e129c9fff6a08b57c8979671337fd2f Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:21:14 +0530 Subject: [PATCH 05/57] fix: added in_view_mode flag --- .../doctype/web_form/templates/web_form.html | 20 ++++++++++--------- frappe/website/doctype/web_form/web_form.py | 10 +++++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 8d3df9b1b5..858b7ea239 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -3,12 +3,12 @@ {% block breadcrumbs %}{% endblock %} {% macro header_buttons() %} - {% if allow_edit and doc_name and not in_edit_mode %} + {% if allow_edit and in_view_mode %} {{ _("Edit Response", null, "Button in web form") }} {% endif %} - {% if allow_print and not is_new %} + {% if allow_print and in_view_mode %} {% set print_format_url = "/printview?doctype=" + doc_type + "&name=" + doc_name + "&format=" + print_format %} @@ -18,8 +18,8 @@ {% endmacro %} {% macro action_buttons() %} - {% if is_new or in_edit_mode %} -
+ -
-
+ {% endif %} +
+
+
+ {% if not in_view_mode %} -
- {% endif %} + {% endif %} +
{% endmacro %} {% block page_content %} diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 8f50731ef7..7328689eab 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -125,6 +125,7 @@ def get_context(context): def get_context(self, context): """Build context to render the `web_form.html` template""" context.in_edit_mode = False + context.in_view_mode = False self.set_web_form_module() if frappe.form_dict.is_list: @@ -156,11 +157,15 @@ def get_context(context): frappe.redirect(f"/{self.route}/new") if frappe.form_dict.is_edit and not self.allow_edit: + context.in_view_mode = True frappe.redirect(f"/{self.route}/{frappe.form_dict.name}") if frappe.form_dict.is_edit: context.in_edit_mode = True + if frappe.form_dict.is_read: + context.in_view_mode = True + if ( not frappe.form_dict.is_edit and not frappe.form_dict.is_read @@ -179,6 +184,7 @@ def get_context(context): ): name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name") if name: + context.in_view_mode = True frappe.redirect(f"/{self.route}/{name}") # Show new form when @@ -203,7 +209,9 @@ def get_context(context): # load web form doc context.web_form_doc = self.as_dict(no_nulls=True) - context.web_form_doc.update(dict_with_keys(context, ["is_list", "is_new", "in_edit_mode"])) + context.web_form_doc.update( + dict_with_keys(context, ["is_list", "is_new", "in_edit_mode", "in_view_mode"]) + ) if self.show_sidebar and self.website_sidebar: context.sidebar_items = get_sidebar_items(self.website_sidebar) From 0797cc212004ab069323945f1f3db00aa26fa6dc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:23:48 +0530 Subject: [PATCH 06/57] fix: show Page 1 of 2 only in view mode --- frappe/public/js/frappe/web_form/web_form.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index f64fae52ff..7dd043340f 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -223,8 +223,18 @@ export default class WebForm extends frappe.ui.FieldGroup { } render_progress_dots() { + if (!this.is_multi_step_form) return; $(".center-area.paging").empty(); + if (this.in_view_mode) { + let paging_text = __("Page {0} of {1}", [ + this.current_section + 1, + this.page_breaks.length + 1, + ]); + $(".center-area.paging").append(`
${paging_text}
`); + return; + } + this.$slide_progress = $(`
`).appendTo( $(".center-area.paging") ); @@ -246,12 +256,6 @@ export default class WebForm extends frappe.ui.FieldGroup { } this.$slide_progress.append($dot); } - - let paging_text = __("Page {0} of {1}", [ - this.current_section + 1, - this.page_breaks.length + 1, - ]); - $(".center-area.paging").append(`
${paging_text}
`); } toggle_buttons() { From b3b773dcaed1276fd4250469c44f42e988b856e4 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:26:29 +0530 Subject: [PATCH 07/57] fix: show submit button always if allow_incomplete is enabled --- frappe/public/js/frappe/web_form/web_form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 7dd043340f..c6c166b71c 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -294,7 +294,7 @@ export default class WebForm extends frappe.ui.FieldGroup { show_next_and_hide_save_button() { $(".btn-next").show(); - $(".submit-btn").hide(); + !this.allow_incomplete && $(".submit-btn").hide(); } toggle_previous_button() { From fec57546ee636aeef10a3f0d5cae8edfa34ffcaf Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:33:59 +0530 Subject: [PATCH 08/57] fix: show empty fields in web form's view mode --- frappe/public/js/frappe/form/controls/base_control.js | 2 +- frappe/website/doctype/web_form/web_form.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index a315abc7e0..aed3f9ff3d 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -82,7 +82,7 @@ frappe.ui.form.Control = class BaseControl { is_null(value) && !in_list(["HTML", "Image", "Button"], this.df.fieldtype) ) - status = "None"; + status = "Read"; return status; } diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 7328689eab..f45c3b4018 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -489,7 +489,7 @@ def accept(web_form, data, docname=None): for field in web_form.web_form_fields: fieldname = field.fieldname df = meta.get_field(fieldname) - value = data.get(fieldname, None) + value = data.get(fieldname, "") if df and df.fieldtype in ("Attach", "Attach Image"): if value and "data:" and "base64" in value: From 1b230b75e332a98de9f260bbb18ace22e5e458a0 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 18:43:48 +0530 Subject: [PATCH 09/57] fix: keep progress dots always in center --- frappe/public/js/frappe/web_form/web_form.js | 2 +- frappe/public/scss/website/web_form.scss | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index c6c166b71c..73f56bf7c6 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -70,7 +70,7 @@ export default class WebForm extends frappe.ui.FieldGroup { if (this.page_breaks.length) return; this.page_breaks = $(`.page-break`); - this.is_multi_step_form = true; + this.is_multi_step_form = !!this.page_breaks.length; } setup_previous_next_button() { diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 6bc515b173..0a8ae43e30 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -140,14 +140,18 @@ font-size: var(--text-base); } + .left-area { + display: flex; + flex: 1; + } + .center-area { - padding: 0.5rem; display: flex; align-items: center; + font-size: var(--text-base); .slides-progress { display: flex; - margin-right: .5rem; .slide-step { @include flex(flex, center, center, null); @@ -204,6 +208,12 @@ } } } + + .right-area { + display: flex; + justify-content: flex-end; + flex: 1; + } } } } From 2eb70c381a2e0f8a1febc19cdcc8a2e41b19f0ac Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:02:40 +0530 Subject: [PATCH 10/57] chore: rearranged web form tabs and fields --- frappe/website/doctype/web_form/web_form.js | 17 ++- frappe/website/doctype/web_form/web_form.json | 132 +++++++++--------- 2 files changed, 75 insertions(+), 74 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index 05fc7ff87b..2871a03fe8 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -188,23 +188,22 @@ function get_fields_for_doctype(doctype) { function render_list_settings_message(frm) { // render list setting message if (frm.fields_dict["list_setting_message"] && !frm.doc.login_required) { - const switch_to_form_settings_tab = ` - - ${__("Form Settings Tab")} - + const go_to_login_required_field = ` + + ${__("login_required")} + `; $(frm.fields_dict["list_setting_message"].wrapper) .html( $( `
- ${__( - "Login is required to see web form list view. Enable login_required from {0} to see list settings", - [switch_to_form_settings_tab] - )} + ${__("Login is required to see web form list view. Enable {0} to see list settings", [ + go_to_login_required_field, + ])}
` ) ) - .find("span") + .find("code") .click(() => frm.scroll_to_field("login_required")); } else { $(frm.fields_dict["list_setting_message"].wrapper).empty(); diff --git a/frappe/website/doctype/web_form/web_form.json b/frappe/website/doctype/web_form/web_form.json index 8faa263e5b..f5ab147c64 100644 --- a/frappe/website/doctype/web_form/web_form.json +++ b/frappe/website/doctype/web_form/web_form.json @@ -5,50 +5,50 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "title_and_route_tab", + "form_tab", "title", "route", "published", - "column_break_4", + "column_break_1", "doc_type", "module", "is_standard", - "introduction", + "section_break_1", "introduction_text", - "form_settings_tab", + "web_form_fields", + "settings_tab", "login_required", "allow_multiple", "allow_edit", "allow_delete", - "column_break_18", + "column_break_2", "apply_document_permissions", "allow_print", "print_format", "allow_comments", "show_attachments", "allow_incomplete", - "form_fields", - "web_form_fields", + "section_break_2", "max_attachment_size", - "list_settings_tab", + "section_break_3", "list_setting_message", "show_list", "list_title", "list_columns", - "sidebar_settings_tab", + "section_break_4", "show_sidebar", "website_sidebar", "customization_tab", "button_label", "banner_image", - "column_break_37", + "column_break_3", "breadcrumbs", - "section_break_43", + "section_break_5", "success_title", "success_url", - "column_break_41", + "column_break_4", "success_message", - "scripting_style_tab", + "section_break_6", "client_script", "custom_css" ], @@ -81,10 +81,6 @@ "label": "Module", "options": "Module Def" }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, { "default": "0", "fieldname": "is_standard", @@ -158,12 +154,6 @@ "fieldtype": "Check", "label": "Allow Incomplete Forms" }, - { - "collapsible": 1, - "fieldname": "introduction", - "fieldtype": "Section Break", - "label": "Introduction" - }, { "fieldname": "introduction_text", "fieldtype": "Text Editor", @@ -250,21 +240,6 @@ "label": "List Columns", "options": "Web Form List Column" }, - { - "fieldname": "title_and_route_tab", - "fieldtype": "Tab Break", - "label": "Title & Route" - }, - { - "collapsible": 1, - "fieldname": "form_fields", - "fieldtype": "Section Break", - "label": "Form Fields" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, { "fieldname": "website_sidebar", "fieldtype": "Link", @@ -276,29 +251,6 @@ "fieldtype": "HTML", "label": "List Setting Message" }, - { - "fieldname": "form_settings_tab", - "fieldtype": "Tab Break", - "label": "Form Settings" - }, - { - "collapsible": 1, - "collapsible_depends_on": "show_list", - "fieldname": "list_settings_tab", - "fieldtype": "Tab Break", - "label": "List Settings" - }, - { - "collapsible": 1, - "fieldname": "sidebar_settings_tab", - "fieldtype": "Tab Break", - "label": "Sidebar Settings" - }, - { - "fieldname": "scripting_style_tab", - "fieldtype": "Tab Break", - "label": "Scripting / Style" - }, { "fieldname": "customization_tab", "fieldtype": "Tab Break", @@ -315,24 +267,74 @@ "label": "Banner Image" }, { - "fieldname": "column_break_41", + "fieldname": "form_tab", + "fieldtype": "Tab Break", + "label": "Form" + }, + { + "fieldname": "column_break_1", "fieldtype": "Column Break" }, { - "fieldname": "section_break_43", + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, + { + "fieldname": "settings_tab", + "fieldtype": "Tab Break", + "label": "Settings" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "show_list", + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "List Settings" + }, + { + "collapsible": 1, + "collapsible_depends_on": "show_sidebar", + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Sidebar Settings" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.success_title || doc.success_message || doc.success_url", + "fieldname": "section_break_5", "fieldtype": "Section Break", "label": "After Submission" }, { - "fieldname": "column_break_37", + "fieldname": "column_break_4", "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.client_script || doc.custom_css", + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Scripting / Style" } ], "has_web_view": 1, "icon": "icon-edit", "is_published_field": "published", "links": [], - "modified": "2022-08-11 16:27:25.914627", + "modified": "2022-08-17 18:58:49.451658", "modified_by": "Administrator", "module": "Website", "name": "Web Form", From 0c897e4d22d3169fe9f59e88cd0f338b8681942b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:06:23 +0530 Subject: [PATCH 11/57] fix: only allow 9 page breaks in web form --- frappe/website/doctype/web_form/web_form.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index 2871a03fe8..ab911e2465 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -32,6 +32,14 @@ frappe.ui.form.on("Web Form", { frm.scroll_to_field("web_form_fields"); frappe.throw(__("Atleast one field is required in Web Form Fields Table")); } + + let page_break_count = frm.doc.web_form_fields.filter( + (f) => f.fieldtype == "Page Break" + ).length; + + if (page_break_count >= 10) { + frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); + } }, add_publish_button(frm) { @@ -147,7 +155,16 @@ frappe.ui.form.on("Web Form List Column", { frappe.ui.form.on("Web Form Field", { fieldtype: function (frm, doctype, name) { - var doc = frappe.get_doc(doctype, name); + let doc = frappe.get_doc(doctype, name); + + let page_break_count = frm.doc.web_form_fields.filter( + (f) => f.fieldtype == "Page Break" + ).length; + + if (page_break_count >= 10) { + frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); + } + if (["Section Break", "Column Break", "Page Break"].includes(doc.fieldtype)) { doc.fieldname = ""; doc.options = ""; From 029bea6774a13660a8e49082fded928400db3ff1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:14:53 +0530 Subject: [PATCH 12/57] fix: formatted Section,Column,Page Breaks in webform field table and more Show fields label in Field column both in web form fields table and web form list column table. Also enabled mandatory field in grid view --- frappe/website/doctype/web_form/web_form.js | 27 ++++++++++++++++++- .../web_form_field/web_form_field.json | 7 ++--- .../web_form_list_column.json | 6 ++--- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index ab911e2465..68ebb5cb24 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -1,4 +1,28 @@ frappe.ui.form.on("Web Form", { + setup: function (frm) { + frappe.meta.docfield_map["Web Form Field"].fieldtype.formatter = (value) => { + const prefix = { + "Page Break": "--red-600", + "Section Break": "--blue-600", + "Column Break": "--yellow-600", + }; + if (prefix[value]) { + value = `${value}`; + } + return value; + }; + + frappe.meta.docfield_map["Web Form Field"].fieldname.formatter = (value) => { + if (!value) return; + return frappe.unscrub(value); + }; + + frappe.meta.docfield_map["Web Form List Column"].fieldname.formatter = (value) => { + if (!value) return; + return frappe.unscrub(value); + }; + }, + refresh: function (frm) { // show is-standard only if developer mode frm.get_field("is_standard").toggle(frappe.boot.developer_mode); @@ -105,7 +129,7 @@ frappe.ui.form.on("Web Form", { get_fields_for_doctype(doc.doc_type).then((fields) => { let as_select_option = (df) => ({ - label: df.label + " (" + df.fieldtype + ")", + label: df.label, value: df.fieldname, }); update_options(fields.map(as_select_option)); @@ -168,6 +192,7 @@ frappe.ui.form.on("Web Form Field", { if (["Section Break", "Column Break", "Page Break"].includes(doc.fieldtype)) { doc.fieldname = ""; doc.options = ""; + doc.label = ""; frm.refresh_field("web_form_fields"); } }, diff --git a/frappe/website/doctype/web_form_field/web_form_field.json b/frappe/website/doctype/web_form_field/web_form_field.json index dbadf52881..7fa1c4d6cf 100644 --- a/frappe/website/doctype/web_form_field/web_form_field.json +++ b/frappe/website/doctype/web_form_field/web_form_field.json @@ -32,7 +32,7 @@ "fieldname": "fieldname", "fieldtype": "Select", "in_list_view": 1, - "label": "Fieldname" + "label": "Field" }, { "fieldname": "fieldtype", @@ -45,7 +45,7 @@ "fieldname": "label", "fieldtype": "Data", "in_list_view": 1, - "label": "Label" + "label": "Custom Label" }, { "default": "0", @@ -58,6 +58,7 @@ "default": "0", "fieldname": "reqd", "fieldtype": "Check", + "in_list_view": 1, "label": "Mandatory" }, { @@ -146,7 +147,7 @@ ], "istable": 1, "links": [], - "modified": "2022-08-10 12:59:51.170546", + "modified": "2022-08-17 19:10:02.567957", "modified_by": "Administrator", "module": "Website", "name": "Web Form Field", diff --git a/frappe/website/doctype/web_form_list_column/web_form_list_column.json b/frappe/website/doctype/web_form_list_column/web_form_list_column.json index e55aeadca6..8be724f426 100644 --- a/frappe/website/doctype/web_form_list_column/web_form_list_column.json +++ b/frappe/website/doctype/web_form_list_column/web_form_list_column.json @@ -15,14 +15,14 @@ "fieldname": "fieldname", "fieldtype": "Select", "in_list_view": 1, - "label": "Fieldname", + "label": "Field", "reqd": 1 }, { "fieldname": "label", "fieldtype": "Data", "in_list_view": 1, - "label": "Label" + "label": "Custom Label" }, { "fieldname": "fieldtype", @@ -35,7 +35,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-06-21 17:22:14.978947", + "modified": "2022-08-17 19:09:01.417841", "modified_by": "Administrator", "module": "Website", "name": "Web Form List Column", From df3eff1a8d84a6d74423a970a6521e0d7d7301cc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:27:08 +0530 Subject: [PATCH 13/57] fix: show clear button only if form is dirty and move previous button on right side if clear button is visible --- frappe/public/js/frappe/web_form/web_form.js | 31 +++++++++---------- .../doctype/web_form/templates/web_form.html | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 73f56bf7c6..206c3fc61a 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -47,20 +47,16 @@ export default class WebForm extends frappe.ui.FieldGroup { } setup_listeners() { - // Event listener for triggering Save/Next button for Multi Step Forms // Do not use `on` event here since that can be used by user which will render this function useless // setTimeout has 200ms delay so that all the base_control triggers for the fields have been run - let me = this; - - if (!me.is_multi_step_form) { - return; - } for (let field of $(".input-with-feedback")) { $(field).change((e) => { setTimeout(() => { e.stopPropagation(); - me.toggle_buttons(); + frappe.form_dirty = true; + $(".web-form-footer .clear-btn").removeClass("hide"); + $(".web-form-footer .right-area").prepend(this.$previous_button); }, 200); }); } @@ -80,15 +76,18 @@ export default class WebForm extends frappe.ui.FieldGroup { return; } - $(".web-form-footer .web-form-actions .left-area").prepend(` - - `); + this.$next_button = $(``); - $(".web-form-footer .web-form-actions .right-area").prepend(` - - `); + this.$previous_button = $(``); - $(".btn-previous").on("click", function () { + $(".web-form-footer .right-area").prepend(this.$next_button); + $(".web-form-footer .left-area").prepend(this.$previous_button); + + this.$previous_button.on("click", () => { let is_validated = me.validate_section(); if (!is_validated) return false; @@ -115,7 +114,7 @@ export default class WebForm extends frappe.ui.FieldGroup { return false; }); - $(".btn-next").on("click", function () { + this.$next_button.on("click", () => { let is_validated = me.validate_section(); if (!is_validated) return false; @@ -155,7 +154,7 @@ export default class WebForm extends frappe.ui.FieldGroup { } setup_primary_action() { - $(".web-form-container").on("submit", () => this.save()); + $(".web-form").on("submit", () => this.save()); } validate_section() { diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 858b7ea239..c5c393576a 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -21,7 +21,7 @@
{% if not in_view_mode %} - + {% if in_edit_mode %} {{ _("Reset Form", null, "Button in web form") }} {% else %} From ce67ee1bc606dedb349387e79f50f4ddbae968df Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:32:59 +0530 Subject: [PATCH 14/57] fix: make clear button link which trigger warning dialog --- frappe/public/js/frappe/web_form/web_form.js | 20 +++++++++++++++++++ frappe/public/scss/website/web_form.scss | 9 +++++++++ .../doctype/web_form/templates/web_form.html | 4 ++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 206c3fc61a..67abc959db 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -28,6 +28,7 @@ export default class WebForm extends frappe.ui.FieldGroup { if (this.is_new || this.in_edit_mode) { this.setup_primary_action(); + this.setup_clear_action(); } this.setup_previous_next_button(); @@ -157,6 +158,25 @@ export default class WebForm extends frappe.ui.FieldGroup { $(".web-form").on("submit", () => this.save()); } + setup_clear_action() { + $(".web-form-footer .clear-btn").on("click", () => this.clear_form()); + } + + clear_form() { + let title = __("Clear Form?"); + let message = __("Are you sure you want to clear the form? It cannot be undone."); + let clear_button_text = __("Clear Form"); + + if (location.href.includes("/edit")) { + title = __("Reset Form?"); + message = __("Are you sure you want to reset all field values?"); + clear_button_text = __("Reset Form"); + } + + frappe.warn(title, message, () => location.reload(true), clear_button_text); + return false; + } + validate_section() { if (this.allow_incomplete) return true; diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 0a8ae43e30..a106fa8f92 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -140,6 +140,15 @@ font-size: var(--text-base); } + .btn-link { + padding-left: 0px; + color: var(--text-color); + + &:hover { + color: var(--text-on-light-blue); + } + } + .left-area { display: flex; flex: 1; diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index c5c393576a..7c4dc6d94d 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -21,13 +21,13 @@
From 68318aacd323a3d5ced72dc1be8f7daff116e3ae Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:39:42 +0530 Subject: [PATCH 15/57] fix: added cancel button in clear form dialog --- frappe/public/js/bootstrap-4-web.bundle.js | 13 +++++++++---- frappe/public/js/frappe/ui/dialog.js | 2 +- frappe/public/js/frappe/ui/messages.js | 1 - frappe/website/doctype/web_form/web_form.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/bootstrap-4-web.bundle.js b/frappe/public/js/bootstrap-4-web.bundle.js index 083b7aecaa..3845a7b185 100644 --- a/frappe/public/js/bootstrap-4-web.bundle.js +++ b/frappe/public/js/bootstrap-4-web.bundle.js @@ -32,10 +32,7 @@ frappe.get_modal = function (title, content) { ${content}
@@ -49,11 +46,19 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.Dialog { return this.$wrapper.find(".modal-footer .btn-primary"); } + get_secondary_btn() { + return this.$wrapper.find(".modal-footer .btn-secondary"); + } + set_primary_action(label, click) { this.$wrapper.find(".modal-footer").removeClass("hidden"); return super.set_primary_action(label, click).removeClass("hidden"); } + set_secondary_action(click) { + return super.set_secondary_action(click).removeClass("hidden"); + } + make() { super.make(); if (this.fields) { diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index 56cbfbf5a2..ed42b81b68 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -168,7 +168,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { set_secondary_action(click) { this.footer.removeClass("hide"); - this.get_secondary_btn().removeClass("hide").off("click").on("click", click); + return this.get_secondary_btn().removeClass("hide").off("click").on("click", click); } set_secondary_action_label(label) { diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 8b0585b727..bbd74dba4a 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -72,7 +72,6 @@ frappe.warn = function (title, message_html, proceed_action, primary_label, is_m d.$body.append(`
${message_html}
`); d.standard_actions.find(".btn-primary").removeClass("btn-primary").addClass("btn-danger"); - d.standard_actions.find(".btn-primary").removeClass("btn-primary").addClass("btn-danger"); d.show(); return d; diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index f45c3b4018..7299c84665 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -317,7 +317,7 @@ def get_context(context): context.reference_doc.doctype, context.reference_doc.name ) - context.reference_doc = json.loads(context.reference_doc.as_json()) + context.reference_doc = context.reference_doc.as_dict(no_nulls=True) def add_custom_context_and_script(self, context): """Update context from module if standard and append script""" From f03e5e2d1edf570b165200aac595b09550a5ee97 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 19:46:55 +0530 Subject: [PATCH 16/57] chore: minor fixes --- frappe/website/doctype/web_form/web_form.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index 68ebb5cb24..f9b62bc013 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -181,18 +181,18 @@ frappe.ui.form.on("Web Form Field", { fieldtype: function (frm, doctype, name) { let doc = frappe.get_doc(doctype, name); - let page_break_count = frm.doc.web_form_fields.filter( - (f) => f.fieldtype == "Page Break" - ).length; - - if (page_break_count >= 10) { - frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); + if (doc.fieldtype == "Page Break") { + let page_break_count = frm.doc.web_form_fields.filter( + (f) => f.fieldtype == "Page Break" + ).length; + page_break_count >= 10 && + frappe.throw(__("There can be only 9 Page Break fields in a Web Form")); } if (["Section Break", "Column Break", "Page Break"].includes(doc.fieldtype)) { doc.fieldname = ""; - doc.options = ""; doc.label = ""; + doc.options = ""; frm.refresh_field("web_form_fields"); } }, From 3c9bc6f1ec20ba1ccc0956c8fd667907ff63d43a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 20:01:21 +0530 Subject: [PATCH 17/57] fix: moved banner image in to page header section --- frappe/public/scss/website/web_form.scss | 633 +++++++++--------- .../doctype/web_form/templates/web_form.html | 14 +- 2 files changed, 327 insertions(+), 320 deletions(-) diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index a106fa8f92..88237e3282 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -1,380 +1,387 @@ @import "../common/form"; [data-doctype="Web Form"] { - .page_content { - max-width: 800px; - margin: auto; - - h1 { - font-size: 2.25rem; - margin-top: 0; - margin-bottom: 0; - } - - .web-form-banner-image { - margin: -4rem -14rem 5rem; - padding-top: 3rem; - position: relative; - - img { - position: absolute; - object-fit: cover; + .page-content-wrapper { + .container { + .page-header { width: 100%; - height: 250px; - z-index: -1; - } - } - .web-form-header { - border: 1px solid var(--dark-border-color); - border-bottom: none; - border-top-left-radius: var(--border-radius-md); - border-top-right-radius: var(--border-radius-md); - background-color: var(--fg-color); - padding: 2rem 2rem 0; - - .breadcrumb-container { - padding: 0px; - margin: 0 0 2rem; - - ol.breadcrumb { - padding: 0px; + img { + margin: -1rem 0rem -10.5rem; + object-fit: cover; + width: 100%; + height: 250px; + z-index: -1; } } - .web-form-head { - border-bottom: 1px solid var(--dark-border-color); - padding-bottom: 1.25rem; + .page_content { + max-width: 800px; + margin: auto; - .title { - display: flex; - justify-content: space-between; + h1 { + font-size: 2.25rem; + margin-top: 0; + margin-bottom: 0; } - .web-form-introduction { - color: var(--text-muted); - margin-top: 1.25rem; + .web-form-header { + border: 1px solid var(--dark-border-color); + border-bottom: none; + border-top-left-radius: var(--border-radius-md); + border-top-right-radius: var(--border-radius-md); + background-color: var(--fg-color); + padding: 2rem 2rem 0; - p { - color: var(--text-muted); - } - } - } - } + .breadcrumb-container { + padding: 0px; + margin: 0 0 2rem; - .web-form { - background-color: var(--fg-color); - padding: 1.25rem 2rem 2rem; - border: 1px solid var(--dark-border-color); - border-top: none; - border-bottom-left-radius: var(--border-radius); - border-bottom-right-radius: var(--border-radius); - - .web-form-wrapper { - .form-control { - color: var(--text-color); - background-color: var(--control-bg); - } - - .form-section { - .section-head { - font-weight: bold; - font-size: var(--text-xl); - padding: var(--padding-md) 0; - } - } - - .form-column { - padding: 0 var(--padding-sm); - - &:first-child { - padding-left: 0; - } - - &:last-child { - padding-right: 0; - } - - @include media-breakpoint-down(sm) { - padding: 0; - } - } - - .web-form-skeleton { - .box-group { - display: flex; - gap: 20px; - margin-bottom: 15px; - - .box-container { - width: 100%; - - .box { - background-color: var(--control-bg); - border-radius: var(--border-radius); - } - - .box-label { - height: 20px; - width: 100px; - margin-bottom: 0.5rem; - } - - .box-area { - height: 34px; - width: 100%; - } - } - } - } - } - - .web-form-footer { - margin-top: 1rem; - - .web-form-actions { - display: flex; - justify-content: space-between; - - .btn { - font-size: var(--text-base); - } - - .btn-link { - padding-left: 0px; - color: var(--text-color); - - &:hover { - color: var(--text-on-light-blue); + ol.breadcrumb { + padding: 0px; } } - .left-area { - display: flex; - flex: 1; - } + .web-form-head { + border-bottom: 1px solid var(--dark-border-color); + padding-bottom: 1.25rem; - .center-area { - display: flex; - align-items: center; - font-size: var(--text-base); - - .slides-progress { + .title { display: flex; + justify-content: space-between; + } - .slide-step { - @include flex(flex, center, center, null); + .web-form-introduction { + color: var(--text-muted); + margin-top: 1.25rem; - height: 18px; - width: 18px; - border-radius: var(--border-radius-full); - border: 1px solid var(--gray-300); - margin: 0 var(--margin-xs); - background-color: var(--card-bg); + p { + color: var(--text-muted); + } + } + } + } - .slide-step-indicator { - height: 6px; - width: 6px; - background-color: var(--gray-300); - border-radius: var(--border-radius-full); - } + .web-form { + background-color: var(--fg-color); + padding: 1.25rem 2rem 2rem; + border: 1px solid var(--dark-border-color); + border-top: none; + border-bottom-left-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); - .slide-step-complete { - display: none; + .web-form-wrapper { + .form-control { + color: var(--text-color); + background-color: var(--control-bg); + } - .icon-xs { - height: 10px; - width: 10px; - } - } + .form-section { + .section-head { + font-weight: bold; + font-size: var(--text-xl); + padding: var(--padding-md) 0; + } + } - &.active { - border: 1px solid var(--primary); + .form-column { + padding: 0 var(--padding-sm); - .slide-step-indicator { - display: block; - background-color: var(--primary); - } - } + &:first-child { + padding-left: 0; + } - &.step-success:not(.active) { - background-color: var(--primary); - border: 1px solid var(--primary); + &:last-child { + padding-right: 0; + } - .slide-step-indicator { - display: none; + @include media-breakpoint-down(sm) { + padding: 0; + } + } + + .web-form-skeleton { + .box-group { + display: flex; + gap: 20px; + margin-bottom: 15px; + + .box-container { + width: 100%; + + .box { + background-color: var(--control-bg); + border-radius: var(--border-radius); } - .slide-step-complete { - display: flex; + .box-label { + height: 20px; + width: 100px; + margin-bottom: 0.5rem; + } - .icon use { - stroke-width: 2; - stroke: var(--white); + .box-area { + height: 34px; + width: 100%; + } + } + } + } + } + + .web-form-footer { + margin-top: 1rem; + + .web-form-actions { + display: flex; + justify-content: space-between; + + .btn { + font-size: var(--text-base); + } + + .btn-link { + padding-left: 0px; + color: var(--text-color); + + &:hover { + color: var(--text-on-light-blue); + } + } + + .left-area { + display: flex; + flex: 1; + } + + .center-area { + display: flex; + align-items: center; + font-size: var(--text-base); + + .slides-progress { + display: flex; + + .slide-step { + @include flex(flex, center, center, null); + + height: 18px; + width: 18px; + border-radius: var(--border-radius-full); + border: 1px solid var(--gray-300); + margin: 0 var(--margin-xs); + background-color: var(--card-bg); + + .slide-step-indicator { + height: 6px; + width: 6px; + background-color: var(--gray-300); + border-radius: var(--border-radius-full); + } + + .slide-step-complete { + display: none; + + .icon-xs { + height: 10px; + width: 10px; + } + } + + &.active { + border: 1px solid var(--primary); + + .slide-step-indicator { + display: block; + background-color: var(--primary); + } + } + + &.step-success:not(.active) { + background-color: var(--primary); + border: 1px solid var(--primary); + + .slide-step-indicator { + display: none; + } + + .slide-step-complete { + display: flex; + + .icon use { + stroke-width: 2; + stroke: var(--white); + } + } } } } } + + .right-area { + display: flex; + justify-content: flex-end; + flex: 1; + } } } + } - .right-area { + .attachments { + margin-top: 2rem; + padding: 2rem; + border-radius: var(--border-radius); + border: 1px solid var(--dark-border-color); + + .attachment { display: flex; - justify-content: flex-end; - flex: 1; - } - } - } - } + justify-content: space-between; + gap: 6px; + color: var(--text-muted); + font-size: var(--text-md); - .attachments { - margin-top: 2rem; - padding: 2rem; - border-radius: var(--border-radius); - border: 1px solid var(--dark-border-color); - - .attachment { - display: flex; - justify-content: space-between; - gap: 6px; - color: var(--text-muted); - font-size: var(--text-md); - - &:hover { - text-decoration: none; - .file-name span { - text-decoration: underline; - } - } - } - } - - .success-page { - background-color: var(--fg-color); - padding: 2rem; - border: 1px solid var(--dark-border-color); - border-radius: var(--border-radius); - text-align: center; - - svg.icon { - width: 5rem; - height: 5rem; - margin: 1rem; - } - - h2 { - margin-top: 0; - margin-bottom: 0; - } - - .success-message { - margin-bottom: 1.6rem; - } - } - - .web-list-container { - min-height: 470px; - border: 1px solid var(--dark-border-color); - border-radius: var(--border-radius-md); - padding: 2rem; - - .web-list-header { - display: flex; - justify-content: space-between; - border-bottom: 1px solid var(--dark-border-color); - padding-bottom: 1.25rem; - - .web-list-actions { - align-self: center; - } - } - - .web-list-filters { - display: flex; - flex-wrap: wrap; - margin: 1.25rem 0; - gap: 10px; - - .form-group.frappe-control { - min-width: 145px; - padding: 0px; - margin: 0px; - align-self: center; - - .checkbox { - .input-xs { - height: var(--checkbox-size); + &:hover { + text-decoration: none; + .file-name span { + text-decoration: underline; + } } + } + } - .help-box { - display: none; + .success-page { + background-color: var(--fg-color); + padding: 2rem; + border: 1px solid var(--dark-border-color); + border-radius: var(--border-radius); + text-align: center; + + svg.icon { + width: 5rem; + height: 5rem; + margin: 1rem; + } + + h2 { + margin-top: 0; + margin-bottom: 0; + } + + .success-message { + margin-bottom: 1.6rem; + } + } + + .web-list-container { + min-height: 470px; + border: 1px solid var(--dark-border-color); + border-radius: var(--border-radius-md); + padding: 2rem; + + .web-list-header { + display: flex; + justify-content: space-between; + border-bottom: 1px solid var(--dark-border-color); + padding-bottom: 1.25rem; + + .web-list-actions { + align-self: center; } } - .input-xs { - height: 28px; - line-height: 1.2; - } - } - } + .web-list-filters { + display: flex; + flex-wrap: wrap; + margin: 1.25rem 0; + gap: 10px; - .web-list-table { - overflow: auto; + .form-group.frappe-control { + min-width: 145px; + padding: 0px; + margin: 0px; + align-self: center; - .table { - border-bottom: 1px solid var(--border-color); - border-top: 1px solid var(--border-color); + .checkbox { + .input-xs { + height: var(--checkbox-size); + } - thead tr { - th { - border: 0; - font-size: 13px; - font-weight: normal; - color: var(--text-muted); + .help-box { + display: none; + } + } - input[type="checkbox"] { - margin-bottom: -2px; + .input-xs { + height: 28px; + line-height: 1.2; } } } - tbody tr { - color: var(--text-color); - cursor: pointer; + .web-list-table { + overflow: auto; - td { - font-size: 13px; + .table { + border-bottom: 1px solid var(--border-color); + border-top: 1px solid var(--border-color); + + thead tr { + th { + border: 0; + font-size: 13px; + font-weight: normal; + color: var(--text-muted); + + input[type="checkbox"] { + margin-bottom: -2px; + } + } + } + + tbody tr { + color: var(--text-color); + cursor: pointer; + + td { + font-size: 13px; + border-top: 1px solid var(--border-color); + } + } + + input[type="checkbox"] { + margin-top: 2px; + } + + .list-col-checkbox { + width: 1rem; + } + + .list-col-serial { + width: 1.5rem; + } + } + + .no-result { + min-height: 330px; border-top: 1px solid var(--border-color); } } - input[type="checkbox"] { - margin-top: 2px; - } - - .list-col-checkbox { - width: 1rem; - } - - .list-col-serial { - width: 1.5rem; + .web-list-footer { + text-align: right; } } - .no-result { - min-height: 330px; - border-top: 1px solid var(--border-color); + .breadcrumb-container.container { + @include media-breakpoint-up(sm) { + padding-left: 0; + } } } - .web-list-footer { - text-align: right; - } - } - - .breadcrumb-container.container { - @include media-breakpoint-up(sm) { + @include media-breakpoint-down(lg) { padding-left: 0; + padding-right: 0; } } } diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 7c4dc6d94d..a3ebc35e69 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -2,6 +2,13 @@ {% block breadcrumbs %}{% endblock %} +{% block header %} + {% if banner_image %} + + Banner Image + {% endif %} +{% endblock %} + {% macro header_buttons() %} {% if allow_edit and in_view_mode %} @@ -40,13 +47,6 @@ {% endmacro %} {% block page_content %} - - {% if banner_image %} -
- Banner Image -
- {% endif %} -
From 0889d9137fbcb0c3dfb733657d4b034bb5a38580 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 20:12:46 +0530 Subject: [PATCH 18/57] fix: made web form and web list responsive --- .../js/frappe/web_form/web_form_list.js | 2 +- frappe/public/scss/website/web_form.scss | 61 ++++++++++++++++++- .../doctype/web_form/templates/web_form.html | 4 +- .../doctype/web_form/templates/web_list.html | 4 +- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form_list.js b/frappe/public/js/frappe/web_form/web_form_list.js index 4481901e0b..5f2a65bbec 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -381,7 +381,7 @@ frappe.ui.WebFormListRow = class WebFormListRow { formatter(this.doc[field.fieldname], field, { only_value: 1 }, this.doc) )) || ""; - let cell = $(`${value}`); + let cell = $(`

${value}

`); cell.appendTo(this.row); }); diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 88237e3282..e2f85b787e 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -23,6 +23,7 @@ font-size: 2.25rem; margin-top: 0; margin-bottom: 0; + padding-bottom: 2px; } .web-form-header { @@ -49,6 +50,19 @@ .title { display: flex; justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; + + .web-form-actions { + display: flex; + align-items: center; + justify-content: flex-end; + flex: 1; + + .btn { + font-size: var(--text-base); + } + } } .web-form-introduction { @@ -95,7 +109,7 @@ padding-right: 0; } - @include media-breakpoint-down(sm) { + @include media-breakpoint-down(xs) { padding: 0; } } @@ -135,6 +149,7 @@ .web-form-actions { display: flex; justify-content: space-between; + flex-wrap: wrap; .btn { font-size: var(--text-base); @@ -152,6 +167,10 @@ .left-area { display: flex; flex: 1; + + @include media-breakpoint-down(sm) { + order: 1 + } } .center-area { @@ -214,14 +233,30 @@ } } } + + @include media-breakpoint-down(xs) { + width: 16px; + height: 16px; + } } } + + @include media-breakpoint-down(sm) { + order: 0; + width: 100%; + justify-content: center; + margin-bottom: 1.5rem; + } } .right-area { display: flex; justify-content: flex-end; flex: 1; + + @include media-breakpoint-down(sm) { + order: 2 + } } } } @@ -281,11 +316,16 @@ .web-list-header { display: flex; justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; border-bottom: 1px solid var(--dark-border-color); padding-bottom: 1.25rem; .web-list-actions { - align-self: center; + display: flex; + align-items: center; + justify-content: flex-end; + flex: 1; } } @@ -345,6 +385,18 @@ td { font-size: 13px; border-top: 1px solid var(--border-color); + + .ql-editor, p { + width: max-content; + max-width: 200px; + margin-bottom: 0; + + p { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } } } @@ -377,6 +429,11 @@ padding-left: 0; } } + + @include media-breakpoint-down(lg) { + padding-left: 1.5rem; + padding-right: 1.5rem; + } } @include media-breakpoint-down(lg) { diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index a3ebc35e69..93884f5e90 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -63,7 +63,9 @@ {% endif %}
-

{{ _(title) }}

+
+

{{ _(title) }}

+
{{ header_buttons() }}
diff --git a/frappe/website/doctype/web_form/templates/web_list.html b/frappe/website/doctype/web_form/templates/web_list.html index 2ec6edaf1c..177ebe3c87 100644 --- a/frappe/website/doctype/web_form/templates/web_list.html +++ b/frappe/website/doctype/web_form/templates/web_list.html @@ -7,7 +7,9 @@
-

{{ _(list_title or title) }}

+
+

{{ _(list_title or title) }}

+
{%- if allow_multiple -%} New From eb7dccaae283b71c5925976b2a22307f64c8ed0e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 20:15:22 +0530 Subject: [PATCH 19/57] fix: made web form skeleton responsive --- frappe/public/scss/website/web_form.scss | 17 +++++++++++++++-- .../web_form/templates/web_form_skeleton.html | 12 ++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index e2f85b787e..c809d1e33f 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -117,11 +117,24 @@ .web-form-skeleton { .box-group { display: flex; - gap: 20px; - margin-bottom: 15px; + flex-wrap: wrap; .box-container { width: 100%; + padding: 0 var(--padding-sm); + margin-bottom: 15px; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + + @include media-breakpoint-down(xs) { + padding: 0; + } .box { background-color: var(--control-bg); diff --git a/frappe/website/doctype/web_form/templates/web_form_skeleton.html b/frappe/website/doctype/web_form/templates/web_form_skeleton.html index 82fb2bccac..299356cee6 100644 --- a/frappe/website/doctype/web_form/templates/web_form_skeleton.html +++ b/frappe/website/doctype/web_form/templates/web_form_skeleton.html @@ -1,10 +1,10 @@
-
+
-
+
@@ -16,21 +16,21 @@
-
+
-
+
-
+
-
+
From 05b1a99e535afc0cd009a4fbb44e32b03f6e4b59 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 20:34:48 +0530 Subject: [PATCH 20/57] chore: sider fixes --- frappe/website/doctype/web_form/web_form.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index f9b62bc013..c494781f1f 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -1,5 +1,5 @@ frappe.ui.form.on("Web Form", { - setup: function (frm) { + setup: function () { frappe.meta.docfield_map["Web Form Field"].fieldtype.formatter = (value) => { const prefix = { "Page Break": "--red-600", @@ -235,16 +235,12 @@ function render_list_settings_message(frm) { ${__("login_required")} `; + let message = __( + "Login is required to see web form list view. Enable {0} to see list settings", + [go_to_login_required_field] + ); $(frm.fields_dict["list_setting_message"].wrapper) - .html( - $( - `
- ${__("Login is required to see web form list view. Enable {0} to see list settings", [ - go_to_login_required_field, - ])} -
` - ) - ) + .html($(`
${message}
`)) .find("code") .click(() => frm.scroll_to_field("login_required")); } else { From 7ae2202b75f752a3c2ba5a85f1ab0486d94e19bc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 21:00:55 +0530 Subject: [PATCH 21/57] fix(minor): success page button css (responsive) --- frappe/public/js/frappe/web_form/web_form.js | 4 ++-- frappe/public/scss/website/web_form.scss | 4 ++++ frappe/website/doctype/web_form/templates/web_form.html | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 67abc959db..759d36b446 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -422,7 +422,7 @@ export default class WebForm extends frappe.ui.FieldGroup { render_success_page(data) { if (this.allow_edit && data.name) { $(".success-page").append(` - + ${__("Edit your response", null, "Button in web form")} `); @@ -430,7 +430,7 @@ export default class WebForm extends frappe.ui.FieldGroup { if (this.login_required && !this.allow_multiple && !this.show_list && data.name) { $(".success-page").append(` - + ${__("View your response", null, "Button in web form")} `); diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index c809d1e33f..795130d533 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -318,6 +318,10 @@ .success-message { margin-bottom: 1.6rem; } + + a { + margin: 0rem 0.3rem 1rem; + } } .web-list-container { diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 93884f5e90..c5b17c27c9 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -137,7 +137,7 @@
{% else %} {% if show_list %} - {{ _("See previous responses", null, "Button in web form") }} + {{ _("See previous responses", null, "Button in web form") }} {% endif %} {% if not login_required or allow_multiple %} {{ _("Submit another response", null, "Button in web form") }} From 2181233c7529040e78577e525e0f25373b6d6bd6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 21:27:09 +0530 Subject: [PATCH 22/57] test: fixed failing UI test --- cypress/integration/web_form.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js index 9173bfaeb3..d7f210e7d2 100644 --- a/cypress/integration/web_form.js +++ b/cypress/integration/web_form.js @@ -45,7 +45,7 @@ context("Web Form", () => { cy.login(); cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "Form Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('input[data-fieldname="login_required"]').check({ force: true }); cy.save(); @@ -65,7 +65,8 @@ context("Web Form", () => { cy.login(); cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "List Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get(".section-head").contains("List Settings").click(); cy.get('input[data-fieldname="show_list"]').check(); cy.save(); @@ -78,7 +79,7 @@ context("Web Form", () => { it("Show Custom List Title", () => { cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "List Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.fill_field("list_title", "Note List"); cy.save(); @@ -97,7 +98,7 @@ context("Web Form", () => { cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "List Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('[data-fieldname="list_columns"] .grid-footer button') .contains("Add Row") @@ -108,19 +109,19 @@ context("Web Form", () => { cy.get("@grid-rows").find('.grid-row:first [data-fieldname="fieldname"]').click(); cy.get("@grid-rows") .find('.grid-row:first select[data-fieldname="fieldname"]') - .select("Title (Data)"); + .select("Title"); cy.get("@add-row").click(); cy.get("@grid-rows").find('.grid-row[data-idx="2"] [data-fieldname="fieldname"]').click(); cy.get("@grid-rows") .find('.grid-row[data-idx="2"] select[data-fieldname="fieldname"]') - .select("Public (Check)"); + .select("Public"); cy.get("@add-row").click(); cy.get("@grid-rows").find('.grid-row:last [data-fieldname="fieldname"]').click(); cy.get("@grid-rows") .find('.grid-row:last select[data-fieldname="fieldname"]') - .select("Content (Text Editor)"); + .select("Content"); cy.save(); @@ -171,7 +172,7 @@ context("Web Form", () => { it("Edit Mode", () => { cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "Form Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('input[data-fieldname="allow_edit"]').check(); cy.save(); @@ -179,7 +180,7 @@ context("Web Form", () => { cy.visit("/note/Note 1"); cy.url().should("include", "/note/Note%201"); - cy.get(".web-form-actions a").contains("Edit").click(); + cy.get(".web-form-actions a").contains("Edit Response").click(); cy.url().should("include", "/note/Note%201/edit"); // Editable Field @@ -194,7 +195,7 @@ context("Web Form", () => { it("Allow Multiple Response", () => { cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "Form Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('input[data-fieldname="allow_multiple"]').check(); cy.save(); @@ -212,7 +213,7 @@ context("Web Form", () => { it("Allow Delete", () => { cy.visit("/app/web-form/note"); - cy.findByRole("tab", { name: "Form Settings" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('input[data-fieldname="allow_delete"]').check(); cy.save(); @@ -235,7 +236,7 @@ context("Web Form", () => { it("Navigate and Submit a WebForm", () => { cy.visit("/update-profile"); - cy.get(".web-form-actions a").contains("Edit").click(); + cy.get(".web-form-actions a").contains("Edit Response").click(); cy.fill_field("middle_name", "_Test User"); @@ -247,7 +248,7 @@ context("Web Form", () => { cy.call("frappe.tests.ui_test_helpers.update_webform_to_multistep").then(() => { cy.visit("/update-profile-duplicate"); - cy.get(".web-form-actions a").contains("Edit").click(); + cy.get(".web-form-actions a").contains("Edit Response").click(); cy.fill_field("middle_name", "_Test User"); From 6ed42f5213780dd4e2e8a8fb765e3ad74d71004c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Aug 2022 21:37:20 +0530 Subject: [PATCH 23/57] test: fixed failing unit test --- frappe/website/doctype/web_form/test_web_form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/test_web_form.py b/frappe/website/doctype/web_form/test_web_form.py index c1885d91cd..5a2269b64d 100644 --- a/frappe/website/doctype/web_form/test_web_form.py +++ b/frappe/website/doctype/web_form/test_web_form.py @@ -71,7 +71,7 @@ class TestWebForm(FrappeTestCase): def test_webform_render(self): set_request(method="GET", path="manage-events/new") content = get_response_content("manage-events/new") - self.assertIn("

New Manage Events

", content) + self.assertIn('

New Manage Events

', content) self.assertIn('data-doctype="Web Form"', content) self.assertIn('data-path="manage-events/new"', content) self.assertIn('source-type="Generator"', content) From 9288d0cebf2b17e43a13d4216216246d0f0c4586 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Aug 2022 13:18:55 +0530 Subject: [PATCH 24/57] fix: replaced clear button with discard button --- frappe/public/js/frappe/web_form/web_form.js | 33 +++++++++---------- .../doctype/web_form/templates/web_form.html | 17 +++------- frappe/website/doctype/web_form/web_form.py | 8 ----- 3 files changed, 20 insertions(+), 38 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 759d36b446..5670a428f7 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -28,7 +28,7 @@ export default class WebForm extends frappe.ui.FieldGroup { if (this.is_new || this.in_edit_mode) { this.setup_primary_action(); - this.setup_clear_action(); + this.setup_discard_action(); } this.setup_previous_next_button(); @@ -56,8 +56,6 @@ export default class WebForm extends frappe.ui.FieldGroup { setTimeout(() => { e.stopPropagation(); frappe.form_dirty = true; - $(".web-form-footer .clear-btn").removeClass("hide"); - $(".web-form-footer .right-area").prepend(this.$previous_button); }, 200); }); } @@ -85,7 +83,7 @@ export default class WebForm extends frappe.ui.FieldGroup { ${__("Previous")} `); - $(".web-form-footer .right-area").prepend(this.$next_button); + $(".web-form-footer .right-area").append(this.$next_button); $(".web-form-footer .left-area").prepend(this.$previous_button); this.$previous_button.on("click", () => { @@ -158,22 +156,21 @@ export default class WebForm extends frappe.ui.FieldGroup { $(".web-form").on("submit", () => this.save()); } - setup_clear_action() { - $(".web-form-footer .clear-btn").on("click", () => this.clear_form()); + setup_discard_action() { + $(".web-form-footer .discard-btn").on("click", () => this.discard_form()); } - clear_form() { - let title = __("Clear Form?"); - let message = __("Are you sure you want to clear the form? It cannot be undone."); - let clear_button_text = __("Clear Form"); - - if (location.href.includes("/edit")) { - title = __("Reset Form?"); - message = __("Are you sure you want to reset all field values?"); - clear_button_text = __("Reset Form"); - } - - frappe.warn(title, message, () => location.reload(true), clear_button_text); + discard_form() { + frappe.warn( + __("Discard?"), + __("Are you sure you want to discard the changes?"), + () => { + let path = window.location.href; + // remove new or edit after last / from url + window.location.href = path.substring(0, path.lastIndexOf("/")); + }, + __("Discard") + ); return false; } diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index c5b17c27c9..920fe57a45 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -25,21 +25,14 @@ {% endmacro %} {% macro action_buttons() %} -
- {% if not in_view_mode %} - - - {% endif %} -
+
{% if not in_view_mode %} + + {% endif %} diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 7299c84665..0d8c273b37 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -289,14 +289,6 @@ def get_context(context): context.title = strip_html( context.reference_doc.get(context.reference_doc.meta.get_title_field()) ) - if context.in_edit_mode and context.parents: - context.parents.append( - { - "label": _(context.title), - "route": f"{self.route}/{context.doc_name}", - } - ) - context.title = _("Editing {0}").format(context.title) context.reference_doc.add_seen() context.reference_doctype = context.reference_doc.doctype context.reference_name = context.reference_doc.name From 8bd012da0cfb8d72edb91d43b543c1a47964b602 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Aug 2022 15:12:29 +0530 Subject: [PATCH 25/57] fix: remove page breaks without any fields in it --- frappe/public/js/frappe/web_form/web_form.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 5670a428f7..69975ca3d6 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -62,9 +62,17 @@ export default class WebForm extends frappe.ui.FieldGroup { } set_page_breaks() { - if (this.page_breaks.length) return; + this.page_breaks = $(".page-break"); - this.page_breaks = $(`.page-break`); + if (this.page_breaks.length) { + this.page_breaks.forEach((page_break) => { + if (!$(page_break).find("form").length) { + $(page_break).remove(); + } + }); + } + + this.page_breaks = $(".page-break"); this.is_multi_step_form = !!this.page_breaks.length; } From fc2defcc3ff1610ce38e975054b80aa3a7485716 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Aug 2022 15:19:59 +0530 Subject: [PATCH 26/57] fix: foreach doesnt work for html element looping --- frappe/public/js/frappe/web_form/web_form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 69975ca3d6..6c00216388 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -65,7 +65,7 @@ export default class WebForm extends frappe.ui.FieldGroup { this.page_breaks = $(".page-break"); if (this.page_breaks.length) { - this.page_breaks.forEach((page_break) => { + this.page_breaks.each((i, page_break) => { if (!$(page_break).find("form").length) { $(page_break).remove(); } From 5cdf21d8ee52e5dd37968f73d289ca9c808684ab Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Aug 2022 16:39:21 +0530 Subject: [PATCH 27/57] fix: minor success page css changes --- frappe/public/scss/website/web_form.scss | 40 ++++++++++----- .../doctype/web_form/templates/web_form.html | 51 +++++++++++-------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 795130d533..2fc05934ff 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -299,27 +299,41 @@ .success-page { background-color: var(--fg-color); - padding: 2rem; + padding: 5rem 2rem; + margin-top: 3rem; border: 1px solid var(--dark-border-color); border-radius: var(--border-radius); text-align: center; - svg.icon { - width: 5rem; - height: 5rem; - margin: 1rem; + .success-header { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + margin-top: 1rem; + + .success-icon { + width: 3rem; + height: 3rem; + margin: 0; + + @include media-breakpoint-down(sm) { + width: 2rem; + height: 2rem; + } + } + + .success-title { + margin-top: 0; + margin-bottom: 0; + } } - h2 { - margin-top: 0; - margin-bottom: 0; + .success-body .success-message { + margin: 1rem 0rem 1.5rem; } - .success-message { - margin-bottom: 1.6rem; - } - - a { + .success-footer a { margin: 0rem 0.3rem 1rem; } } diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 920fe57a45..91deee0e2a 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -112,30 +112,37 @@
- - - -

{{ _(success_title) or _("Submitted") }}

-

{{ _(success_message) or _("Thank you for spending your valuable time to fill this form") }}

+
+ + + +

{{ _(success_title) or _("Submitted") }}

+
- {% if success_url %} -
-

- Click on this - {{_("URL")}} - if you are not redirected within - 5 - seconds. -

-
- {% else %} - {% if show_list %} - {{ _("See previous responses", null, "Button in web form") }} +
+

{{ _(success_message) or _("Thank you for spending your valuable time to fill this form") }}

+
+ +
{% endblock page_content %} From c5f64d43bbb7d00a48bea977df987cb445db6320 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Aug 2022 18:22:32 +0530 Subject: [PATCH 28/57] fix: text editor overflow issue --- frappe/public/js/frappe/form/formatters.js | 5 ++++- frappe/public/js/frappe/web_form/web_form_list.js | 4 ++++ frappe/public/scss/website/web_form.scss | 10 +++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index e0286a4af1..7c580ff2b3 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -294,7 +294,10 @@ frappe.form.formatters = { let formatted_value = frappe.form.formatters.Text(value); // to use ql-editor styles try { - if (!$(formatted_value).find(".ql-editor").length) { + if ( + !$(formatted_value).find(".ql-editor").length && + !$(formatted_value).hasClass("ql-editor") + ) { formatted_value = `
${formatted_value}
`; } } catch (e) { diff --git a/frappe/public/js/frappe/web_form/web_form_list.js b/frappe/public/js/frappe/web_form/web_form_list.js index 5f2a65bbec..6d89b8ac95 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -382,6 +382,10 @@ frappe.ui.WebFormListRow = class WebFormListRow { )) || ""; let cell = $(`

${value}

`); + if (field.fieldtype === "Text Editor") { + value = $(value).addClass("ellipsis"); + cell = $("").append(value); + } cell.appendTo(this.row); }); diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 2fc05934ff..19cb7207a1 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -416,16 +416,16 @@ td { font-size: 13px; border-top: 1px solid var(--border-color); + max-width: 160px; .ql-editor, p { width: max-content; - max-width: 200px; + max-width: 150px; margin-bottom: 0; - p { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; + &.read-mode { + display: inline-flex; + gap: 5px; } } } From 364366ae99d4fd1c1cf324917bb3a7dccfac256e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 19 Aug 2022 11:41:50 +0530 Subject: [PATCH 29/57] fix: submit button should always be on extreme right --- frappe/public/js/frappe/web_form/web_form.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 6c00216388..8a4a562ead 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -91,7 +91,8 @@ export default class WebForm extends frappe.ui.FieldGroup { ${__("Previous")} `); - $(".web-form-footer .right-area").append(this.$next_button); + this.$next_button.insertAfter(".web-form-footer .right-area .discard-btn"); + this.in_view_mode && $(".web-form-footer .right-area").append(this.$next_button); $(".web-form-footer .left-area").prepend(this.$previous_button); this.$previous_button.on("click", () => { @@ -426,7 +427,7 @@ export default class WebForm extends frappe.ui.FieldGroup { render_success_page(data) { if (this.allow_edit && data.name) { - $(".success-page").append(` + $(".success-footer").append(` ${__("Edit your response", null, "Button in web form")} @@ -434,7 +435,7 @@ export default class WebForm extends frappe.ui.FieldGroup { } if (this.login_required && !this.allow_multiple && !this.show_list && data.name) { - $(".success-page").append(` + $(".success-footer").append(` ${__("View your response", null, "Button in web form")} From d274b5681714f2bca015f2c068965f53b50d0814 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 19 Aug 2022 15:56:44 +0530 Subject: [PATCH 30/57] chore: code cleanup --- frappe/__init__.py | 7 ------- frappe/modules/export_file.py | 4 +++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 1b7181be93..a796db9a83 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,7 +16,6 @@ import inspect import json import os import re -import shutil import warnings from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, overload @@ -531,12 +530,6 @@ def throw( ) -def delete_folder(path: str) -> None: - """Delete folder and all the content inside that folder.""" - if os.path.exists(path): - shutil.rmtree(path) - - def create_folder(path, with_init=False): """Create a folder in the given path and add an `__init__.py` file (optional). diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index 7f183a750d..edbf5ccaca 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -2,6 +2,7 @@ # License: MIT. See LICENSE import os +import shutil import frappe import frappe.model @@ -103,7 +104,8 @@ def delete_folder(module, dt, dn): # delete folder folder = os.path.join(module_path, dt, dn) - frappe.delete_folder(folder) + if os.path.exists(folder): + shutil.rmtree(folder) def create_folder(module, dt, dn, create_init): From a3cd00a3edbeb708f2d4c2a80ae0681c2a896c14 Mon Sep 17 00:00:00 2001 From: phot0n Date: Fri, 19 Aug 2022 20:40:58 +0530 Subject: [PATCH 31/57] fix: fetch values from email domain for email account * chore: remove dead code related to fetching the same --- .../doctype/email_account/email_account.js | 50 ++++++------------- .../doctype/email_account/email_account.py | 34 ++++++------- 2 files changed, 29 insertions(+), 55 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.js b/frappe/email/doctype/email_account/email_account.js index 3e42af7051..673afc5618 100644 --- a/frappe/email/doctype/email_account/email_account.js +++ b/frappe/email/doctype/email_account/email_account.js @@ -158,7 +158,6 @@ frappe.ui.form.on("Email Account", { }, refresh: function (frm) { - frm.events.set_domain_fields(frm); frm.events.enable_incoming(frm); frm.events.notify_if_unreplied(frm); frm.events.show_gmail_message_for_less_secure_apps(frm); @@ -211,41 +210,22 @@ frappe.ui.form.on("Email Account", { oauth_access(frm); }, - email_id: function (frm) { - //pull domain and if no matching domain go create one - frm.events.update_domain(frm); - }, - - update_domain: function (frm) { - if (!frm.doc.email_id && !frm.doc.service) { - return; + domain: function (frm) { + if (frm.doc.domain) { + frappe.call({ + method: "get_domain_values", + args: { + domain: frm.doc.domain, + }, + callback: function (r) { + if (!r.exc) { + for (let field in r.message) { + frm.set_value(field, r.message[field]); + } + } + }, + }); } - - frappe.call({ - method: "get_domain", - doc: frm.doc, - args: { - email_id: frm.doc.email_id, - }, - callback: function (r) { - if (r.message) { - frm.events.set_domain_fields(frm, r.message); - } - }, - }); - }, - - set_domain_fields: function (frm, args) { - if (!args) { - args = frappe.route_flags.set_domain_values ? frappe.route_options : {}; - } - - for (var field in args) { - frm.set_value(field, args[field]); - } - - delete frappe.route_flags.set_domain_values; - frappe.route_options = {}; }, email_sync_option: function (frm) { diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 15eba0faa5..769c2fcd5d 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -179,26 +179,20 @@ class EmailAccount(Document): email_account.save() @frappe.whitelist() - def get_domain(self, email_id): - """look-up the domain and then full""" - try: - domain = email_id.split("@") - fields = [ - "name as domain", - "use_imap", - "email_server", - "use_ssl", - "use_starttls", - "smtp_server", - "use_tls", - "smtp_port", - "incoming_port", - "append_emails_to_sent_folder", - "use_ssl_for_outgoing", - ] - return frappe.db.get_value("Email Domain", domain[1], fields, as_dict=True) - except Exception: - pass + def get_domain_values(self, domain: str): + fields = [ + "use_imap", + "email_server", + "use_ssl", + "use_starttls", + "smtp_server", + "use_tls", + "smtp_port", + "incoming_port", + "append_emails_to_sent_folder", + "use_ssl_for_outgoing", + ] + return frappe.db.get_value("Email Domain", domain, fields, as_dict=True) def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"): """Returns logged in POP3/IMAP connection object.""" From c450058a37387d1905cb6503480ef49a65b6b3df Mon Sep 17 00:00:00 2001 From: phot0n Date: Fri, 19 Aug 2022 23:56:20 +0530 Subject: [PATCH 32/57] chore: move all email domain attributes to a global variable --- .../doctype/email_account/email_account.py | 15 ++-------- .../doctype/email_domain/email_domain.py | 28 ++++++++++--------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 769c2fcd5d..72439291af 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -12,6 +12,7 @@ from poplib import error_proto import frappe from frappe import _, are_emails_muted, safe_encode from frappe.desk.form import assign_to +from frappe.email.doctype.email_domain.email_domain import EMAIL_DOMAIN_FIELDS from frappe.email.receive import EmailServer, InboundMail, SentEmailInInboxError from frappe.email.smtp import SMTPServer from frappe.email.utils import get_port @@ -180,19 +181,7 @@ class EmailAccount(Document): @frappe.whitelist() def get_domain_values(self, domain: str): - fields = [ - "use_imap", - "email_server", - "use_ssl", - "use_starttls", - "smtp_server", - "use_tls", - "smtp_port", - "incoming_port", - "append_emails_to_sent_folder", - "use_ssl_for_outgoing", - ] - return frappe.db.get_value("Email Domain", domain, fields, as_dict=True) + return frappe.db.get_value("Email Domain", domain, EMAIL_DOMAIN_FIELDS, as_dict=True) def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"): """Returns logged in POP3/IMAP connection object.""" diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index e532c87c72..528407916a 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -11,6 +11,20 @@ from frappe.email.utils import get_port from frappe.model.document import Document from frappe.utils import cint +EMAIL_DOMAIN_FIELDS = [ + "email_server", + "use_imap", + "use_ssl", + "use_starttls", + "use_tls", + "attachment_limit", + "smtp_server", + "smtp_port", + "use_ssl_for_outgoing", + "append_emails_to_sent_folder", + "incoming_port", +] + def get_error_message(event): return { @@ -52,19 +66,7 @@ class EmailDomain(Document): for email_account in frappe.get_all("Email Account", filters={"domain": self.name}): try: email_account = frappe.get_doc("Email Account", email_account.name) - for attr in [ - "email_server", - "use_imap", - "use_ssl", - "use_starttls", - "use_tls", - "attachment_limit", - "smtp_server", - "smtp_port", - "use_ssl_for_outgoing", - "append_emails_to_sent_folder", - "incoming_port", - ]: + for attr in EMAIL_DOMAIN_FIELDS: email_account.set(attr, self.get(attr, default=0)) email_account.save() From b1944916c3daff3e2a9d5fc8035b6d798bf91fb8 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 13:16:30 +0530 Subject: [PATCH 33/57] fix: show discard dialog only if form is dirty --- frappe/public/js/frappe/web_form/web_form.js | 32 +++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index 8a4a562ead..f01bd7d54f 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -166,20 +166,30 @@ export default class WebForm extends frappe.ui.FieldGroup { } setup_discard_action() { - $(".web-form-footer .discard-btn").on("click", () => this.discard_form()); + $(".web-form-footer .discard-btn").on("click", (e) => { + setTimeout(() => { + e.stopPropagation(); + this.discard_form(); + }, 200); + return false; + }); } discard_form() { - frappe.warn( - __("Discard?"), - __("Are you sure you want to discard the changes?"), - () => { - let path = window.location.href; - // remove new or edit after last / from url - window.location.href = path.substring(0, path.lastIndexOf("/")); - }, - __("Discard") - ); + let path = window.location.href; + // remove new or edit after last / from url + path = path.substring(0, path.lastIndexOf("/")); + + if (frappe.form_dirty) { + frappe.warn( + __("Discard?"), + __("Are you sure you want to discard the changes?"), + () => (window.location.href = path), + __("Discard") + ); + } else { + window.location.href = path; + } return false; } From 66389932d1cfbfa5af3eb87a488f55d42533727f Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 13:19:03 +0530 Subject: [PATCH 34/57] fix: show introduction in edit mode --- frappe/website/doctype/web_form/templates/web_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 91deee0e2a..ed5a25e654 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -63,7 +63,7 @@ {{ header_buttons() }}
- {% if is_new and introduction_text %} + {% if introduction_text and (is_new or in_edit_mode) %}
{{ introduction_text }}
{% endif %}
From 309b42f1a32ad87cd94faa479d17daf8f87cd178 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 15:57:46 +0530 Subject: [PATCH 35/57] fix: show webform name and webform doc name on header --- frappe/public/scss/website/web_form.scss | 4 ++++ frappe/website/doctype/web_form/templates/web_form.html | 7 ++++++- frappe/website/doctype/web_form/web_form.py | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 19cb7207a1..cc7c16b0e1 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -53,6 +53,10 @@ flex-wrap: wrap; gap: 1rem; + .web-form-title p { + margin-bottom: 0; + } + .web-form-actions { display: flex; align-items: center; diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index ed5a25e654..1abc0b23b9 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -57,7 +57,12 @@
-

{{ _(title) }}

+ {% if show_list and not is_new %} +

{{ _(web_form_title.capitalize()) }}

+

{{ _(title) }}

+ {% else %} +

{{ _(title) }}

+ {% endif %}
{{ header_buttons() }} diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 0d8c273b37..a4efb77056 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -286,6 +286,7 @@ def get_context(context): if frappe.form_dict.name: context.doc_name = frappe.form_dict.name context.reference_doc = frappe.get_doc(self.doc_type, context.doc_name) + context.web_form_title = context.title context.title = strip_html( context.reference_doc.get(context.reference_doc.meta.get_title_field()) ) From 4b4d66d2a3d03d2664526905b7373aa828b6fff9 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 16:47:15 +0530 Subject: [PATCH 36/57] fix: title should never be empty --- frappe/website/doctype/web_form/templates/web_form.html | 2 +- frappe/website/doctype/web_form/web_form.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 1abc0b23b9..7fa7f4fef9 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -58,7 +58,7 @@
{% if show_list and not is_new %} -

{{ _(web_form_title.capitalize()) }}

+

{{ _(web_form_title) }}

{{ _(title) }}

{% else %}

{{ _(title) }}

diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index a4efb77056..08e0c39ccd 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -287,8 +287,9 @@ def get_context(context): context.doc_name = frappe.form_dict.name context.reference_doc = frappe.get_doc(self.doc_type, context.doc_name) context.web_form_title = context.title - context.title = strip_html( - context.reference_doc.get(context.reference_doc.meta.get_title_field()) + context.title = ( + strip_html(context.reference_doc.get(context.reference_doc.meta.get_title_field())) + or context.doc_name ) context.reference_doc.add_seen() context.reference_doctype = context.reference_doc.doctype From 02da933c160a65ea8fabe98fb46e731a9e9236fc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 18:19:38 +0530 Subject: [PATCH 37/57] fix: setup field's change event instead of input change event --- frappe/public/js/frappe/web_form/web_form.js | 28 +++++++------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index f01bd7d54f..c75d3cac79 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -24,7 +24,6 @@ export default class WebForm extends frappe.ui.FieldGroup { super.make(); this.set_page_breaks(); this.set_field_values(); - this.setup_listeners(); if (this.is_new || this.in_edit_mode) { this.setup_primary_action(); @@ -36,6 +35,7 @@ export default class WebForm extends frappe.ui.FieldGroup { // webform client script frappe.init_client_script && frappe.init_client_script(); + this.setup_listeners(); frappe.web_form.events.trigger("after_load"); this.after_load && this.after_load(); } @@ -44,21 +44,19 @@ export default class WebForm extends frappe.ui.FieldGroup { let field = this.fields_dict[fieldname]; field.df.change = () => { handler(field, field.value); + frappe.form_dirty = true; }; } setup_listeners() { - // Do not use `on` event here since that can be used by user which will render this function useless - // setTimeout has 200ms delay so that all the base_control triggers for the fields have been run - - for (let field of $(".input-with-feedback")) { - $(field).change((e) => { - setTimeout(() => { - e.stopPropagation(); + // setup change event for all fields if not already set through client script + this.fields.forEach((field) => { + if (!field.change) { + field.change = () => { frappe.form_dirty = true; - }, 200); - }); - } + }; + } + }); } set_page_breaks() { @@ -166,13 +164,7 @@ export default class WebForm extends frappe.ui.FieldGroup { } setup_discard_action() { - $(".web-form-footer .discard-btn").on("click", (e) => { - setTimeout(() => { - e.stopPropagation(); - this.discard_form(); - }, 200); - return false; - }); + $(".web-form-footer .discard-btn").on("click", () => this.discard_form()); } discard_form() { From fb8ba3d881bd4f48ca66d7272cd49574b9c8ebb6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 18:21:48 +0530 Subject: [PATCH 38/57] fix: include Color field in Web Form fieldtypes --- frappe/website/doctype/web_form_field/web_form_field.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/doctype/web_form_field/web_form_field.json b/frappe/website/doctype/web_form_field/web_form_field.json index 7fa1c4d6cf..4fb566be88 100644 --- a/frappe/website/doctype/web_form_field/web_form_field.json +++ b/frappe/website/doctype/web_form_field/web_form_field.json @@ -39,7 +39,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Fieldtype", - "options": "Attach\nAttach Image\nCheck\nCurrency\nData\nDate\nDatetime\nDuration\nFloat\nHTML\nInt\nLink\nPassword\nRating\nSelect\nSignature\nSmall Text\nText\nText Editor\nTable\nTime\nSection Break\nColumn Break\nPage Break" + "options": "Attach\nAttach Image\nCheck\nCurrency\nColor\nData\nDate\nDatetime\nDuration\nFloat\nHTML\nInt\nLink\nPassword\nRating\nSelect\nSignature\nSmall Text\nText\nText Editor\nTable\nTime\nSection Break\nColumn Break\nPage Break" }, { "fieldname": "label", @@ -147,7 +147,7 @@ ], "istable": 1, "links": [], - "modified": "2022-08-17 19:10:02.567957", + "modified": "2022-08-22 17:22:39.026893", "modified_by": "Administrator", "module": "Website", "name": "Web Form Field", From 63e760e3adba5549bdc0d7146ede1f28820a85de Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 26 Jun 2022 10:24:24 +0530 Subject: [PATCH 39/57] chore: remove old code where User Permissions were set in `tabDefaultValue` --- frappe/defaults.py | 3 +-- frappe/model/delete_doc.py | 3 --- frappe/model/rename_doc.py | 8 -------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 02076b1fda..744a3fad5d 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -6,8 +6,7 @@ from frappe.cache_manager import clear_defaults_cache, common_default_keys from frappe.desk.notifications import clear_notifications from frappe.query_builder import DocType -# Note: DefaultValue records are identified by parenttype -# __default, __global or 'User Permission' +# Note: DefaultValue records are identified by parent (e.g. __default, __global) def set_user_default(key, value, user=None, parenttype=None): diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 332a4337e2..072b9a1d66 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -167,9 +167,6 @@ def delete_doc( except ImportError: pass - # delete user_permissions - frappe.defaults.clear_default(parenttype="User Permission", key=doctype, value=name) - def add_to_deleted_document(doc): """Add this document to Deleted Document table. Called after delete""" diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 0a832580cd..46a239d0aa 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -197,14 +197,6 @@ def rename_doc( if not merge: rename_password(doctype, old, new) - # update user_permissions - DefaultValue = frappe.qb.DocType("DefaultValue") - frappe.qb.update(DefaultValue).set(DefaultValue.defvalue, new).where( - (DefaultValue.parenttype == "User Permission") - & (DefaultValue.defkey == doctype) - & (DefaultValue.defvalue == old) - ).run() - if merge: new_doc.add_comment("Edit", _("merged {0} into {1}").format(frappe.bold(old), frappe.bold(new))) else: From 2866721a782d0db8e1f812199a2f743a45165dcf Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 5 Aug 2022 16:07:43 +0530 Subject: [PATCH 40/57] test: clear `lang_full_dict` from local cache --- .../doctype/translation/test_translation.py | 20 ++++++++++++------- frappe/tests/test_translate.py | 7 +++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/translation/test_translation.py b/frappe/core/doctype/translation/test_translation.py index 581cda83f0..ebed447b00 100644 --- a/frappe/core/doctype/translation/test_translation.py +++ b/frappe/core/doctype/translation/test_translation.py @@ -11,18 +11,19 @@ class TestTranslation(FrappeTestCase): def tearDown(self): frappe.local.lang = "en" - frappe.local.lang_full_dict = None + clear_translation_cache() def test_doctype(self): translation_data = get_translation_data() for key, val in translation_data.items(): frappe.local.lang = key - frappe.local.lang_full_dict = None + + clear_translation_cache() translation = create_translation(key, val) self.assertEqual(_(val[0]), val[1]) frappe.delete_doc("Translation", translation.name) - frappe.local.lang_full_dict = None + clear_translation_cache() self.assertEqual(_(val[0]), val[0]) @@ -38,20 +39,20 @@ class TestTranslation(FrappeTestCase): frappe.local.lang = "es" - frappe.local.lang_full_dict = None + clear_translation_cache() self.assertTrue(_(data[0][0]), data[0][1]) - frappe.local.lang_full_dict = None + clear_translation_cache() self.assertTrue(_(data[1][0]), data[1][1]) frappe.local.lang = "es-MX" # different translation for es-MX - frappe.local.lang_full_dict = None + clear_translation_cache() self.assertTrue(_(data[2][0]), data[2][1]) # from spanish (general) - frappe.local.lang_full_dict = None + clear_translation_cache() self.assertTrue(_(data[1][0]), data[1][1]) def test_html_content_data_translation(self): @@ -109,3 +110,8 @@ def create_translation(key, val): translation.translated_text = val[1] translation.save() return translation + + +def clear_translation_cache(): + frappe.local.lang_full_dict = None + frappe.cache().delete_key("lang_full_dict", shared=True) diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py index a72358847a..e546516ade 100644 --- a/frappe/tests/test_translate.py +++ b/frappe/tests/test_translate.py @@ -8,6 +8,7 @@ from unittest.mock import patch import frappe import frappe.translate from frappe import _ +from frappe.core.doctype.translation.test_translation import clear_translation_cache from frappe.tests.utils import FrappeTestCase from frappe.translate import ( extract_javascript, @@ -37,13 +38,15 @@ class TestTranslate(FrappeTestCase): def setUp(self): if self._testMethodName in self.guest_sessions_required: frappe.set_user("Guest") - frappe.local.lang_full_dict = None # reset cached translations + + clear_translation_cache() def tearDown(self): frappe.form_dict.pop("_lang", None) if self._testMethodName in self.guest_sessions_required: frappe.set_user("Administrator") - frappe.local.lang_full_dict = None # reset cached translations + + clear_translation_cache() def test_extract_message_from_file(self): data = frappe.translate.get_messages_from_file(translation_string_file) From c5face41b3648b4fd4cbdb247da3f4c8a0584903 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 5 Aug 2022 16:08:02 +0530 Subject: [PATCH 41/57] fix: clear `notifications` cache when deleting a Notification --- frappe/email/doctype/notification/notification.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 8d0857ac60..41fdfeeda1 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -387,6 +387,9 @@ def get_context(context): if not is_html(self.message): self.message = frappe.utils.md_to_html(self.message) + def on_trash(self): + frappe.cache().hdel("notifications", self.document_type) + @frappe.whitelist() def get_documents_for_today(notification): From 7d245268ea4a48ad6dba32603bfc814b67411d10 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 19:04:47 +0530 Subject: [PATCH 42/57] style: rating field style changes in view mode color field causing error in view mode (fixed) --- frappe/public/js/frappe/form/controls/color.js | 6 +++--- frappe/public/scss/website/web_form.scss | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/color.js b/frappe/public/js/frappe/form/controls/color.js index 7fcadc6638..e9f88faec5 100644 --- a/frappe/public/js/frappe/form/controls/color.js +++ b/frappe/public/js/frappe/form/controls/color.js @@ -93,11 +93,11 @@ frappe.ui.form.ControlColor = class ControlColor extends frappe.ui.form.ControlD set_formatted_input(value) { super.set_formatted_input(value); - this.$input.val(value); - this.selected_color.css({ + this.$input?.val(value); + this.selected_color?.css({ "background-color": value || "transparent", }); - this.selected_color.toggleClass("no-value", !value); + this.selected_color?.toggleClass("no-value", !value); } get_color() { diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index cc7c16b0e1..194edfeb35 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -105,6 +105,17 @@ .form-column { padding: 0 var(--padding-sm); + .frappe-control[data-fieldtype="Rating"] { + .like-disabled-input { + background-color: unset; + padding-left: 0px; + + .rating { + cursor: default; + } + } + } + &:first-child { padding-left: 0; } From 2b57e77f29e29a6d074cff9e04895084a8790d88 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Aug 2022 19:40:51 +0530 Subject: [PATCH 43/57] feat: added not saved indicator if form is dirty --- frappe/public/js/frappe/web_form/web_form.js | 9 +++++++-- frappe/public/scss/website/web_form.scss | 4 ++++ frappe/website/doctype/web_form/templates/web_form.html | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js index c75d3cac79..b8f7c327f6 100644 --- a/frappe/public/js/frappe/web_form/web_form.js +++ b/frappe/public/js/frappe/web_form/web_form.js @@ -44,7 +44,7 @@ export default class WebForm extends frappe.ui.FieldGroup { let field = this.fields_dict[fieldname]; field.df.change = () => { handler(field, field.value); - frappe.form_dirty = true; + this.make_form_dirty(); }; } @@ -53,12 +53,17 @@ export default class WebForm extends frappe.ui.FieldGroup { this.fields.forEach((field) => { if (!field.change) { field.change = () => { - frappe.form_dirty = true; + this.make_form_dirty(); }; } }); } + make_form_dirty() { + frappe.form_dirty = true; + $(".indicator-pill.orange").removeClass("hide"); + } + set_page_breaks() { this.page_breaks = $(".page-break"); diff --git a/frappe/public/scss/website/web_form.scss b/frappe/public/scss/website/web_form.scss index 194edfeb35..e5a0093307 100644 --- a/frappe/public/scss/website/web_form.scss +++ b/frappe/public/scss/website/web_form.scss @@ -57,6 +57,10 @@ margin-bottom: 0; } + .indicator-pill { + margin-top: 7px; + } + .web-form-actions { display: flex; align-items: center; diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 7fa7f4fef9..94dcb226c4 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -64,6 +64,7 @@

{{ _(title) }}

{% endif %}
+ Not Saved
{{ header_buttons() }}
From 141693a75bf1ed3568d34a5cca892cc1dadc63b3 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 23 Aug 2022 00:36:53 +0530 Subject: [PATCH 44/57] fix: use debounced frappe.call on domain selection Also removed tag from domain and service labels in email account doctype --- frappe/email/doctype/email_account/email_account.js | 5 +++-- frappe/email/doctype/email_account/email_account.json | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.js b/frappe/email/doctype/email_account/email_account.js index 673afc5618..98160e5f46 100644 --- a/frappe/email/doctype/email_account/email_account.js +++ b/frappe/email/doctype/email_account/email_account.js @@ -210,10 +210,11 @@ frappe.ui.form.on("Email Account", { oauth_access(frm); }, - domain: function (frm) { + domain: frappe.utils.debounce((frm) => { if (frm.doc.domain) { frappe.call({ method: "get_domain_values", + doc: frm.doc, args: { domain: frm.doc.domain, }, @@ -226,7 +227,7 @@ frappe.ui.form.on("Email Account", { }, }); } - }, + }), email_sync_option: function (frm) { // confirm if the ALL sync option is selected diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json index 6ca5e289df..da88ac680c 100644 --- a/frappe/email/doctype/email_account/email_account.json +++ b/frappe/email/doctype/email_account/email_account.json @@ -145,7 +145,7 @@ "hide_seconds": 1, "in_list_view": 1, "in_standard_filter": 1, - "label": "Domain (optional)", + "label": "Domain", "options": "Email Domain" }, { @@ -154,7 +154,7 @@ "fieldtype": "Select", "hide_days": 1, "hide_seconds": 1, - "label": "Service (optional)", + "label": "Service", "options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail" }, { @@ -615,7 +615,7 @@ "icon": "fa fa-inbox", "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-16 13:05:45.445572", + "modified": "2022-08-23 00:31:05.305462", "modified_by": "Administrator", "module": "Email", "name": "Email Account", @@ -639,4 +639,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} +} \ No newline at end of file From 5ca89e0dadabf739c93b0f989f33863f0b745be8 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 23 Aug 2022 16:33:14 +0530 Subject: [PATCH 45/57] revert: use meta fields in standard webforms --- frappe/website/doctype/web_form/web_form.py | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 08e0c39ccd..53192b05cf 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -8,7 +8,6 @@ import frappe from frappe import _, scrub from frappe.core.api.file import get_max_file_size from frappe.core.doctype.file import remove_file_by_url -from frappe.custom.doctype.customize_form.customize_form import docfield_properties from frappe.desk.form.meta import get_code_files_via_hooks from frappe.modules.utils import export_module_json, get_doc_module from frappe.rate_limiter import rate_limit @@ -22,8 +21,6 @@ class WebForm(WebsiteGenerator): def onload(self): super().onload() - if self.is_standard and not frappe.conf.developer_mode: - self.use_meta_fields() def validate(self): super().validate() @@ -67,31 +64,6 @@ class WebForm(WebsiteGenerator): for df in self.web_form_fields: df.parent = self.doc_type - def use_meta_fields(self): - """Override default properties for standard web forms""" - meta = frappe.get_meta(self.doc_type) - - for df in self.web_form_fields: - meta_df = meta.get_field(df.fieldname) - - if not meta_df: - continue - - for prop in docfield_properties: - if df.fieldtype == meta_df.fieldtype and prop not in ( - "idx", - "reqd", - "default", - "description", - "options", - "hidden", - "read_only", - "label", - ): - df.set(prop, meta_df.get(prop)) - - # TODO translate options of Select fields like Country - # export def on_update(self): """ @@ -196,9 +168,6 @@ def get_context(context): self.reset_field_parent() - if self.is_standard: - self.use_meta_fields() - # add keys from form_dict to context context.update(dict_with_keys(frappe.form_dict, ["is_list", "is_new", "is_edit", "is_read"])) From 78ab3d8429f41d28d594ea202cb1b0d30e8879b9 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 23 Aug 2022 16:39:14 +0530 Subject: [PATCH 46/57] chore: removed unused code --- frappe/website/doctype/web_form/web_form.py | 67 --------------------- 1 file changed, 67 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 53192b05cf..e0ae91fef7 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -312,62 +312,6 @@ def get_context(context): context.style = style - def get_layout(self): - layout = [] - - def add_page(df=None): - new_page = {"sections": []} - layout.append(new_page) - if df and df.fieldtype == "Page Break": - new_page.update(df.as_dict()) - - return new_page - - def add_section(df=None): - new_section = {"columns": []} - if layout: - layout[-1]["sections"].append(new_section) - if df and df.fieldtype == "Section Break": - new_section.update(df.as_dict()) - - return new_section - - def add_column(df=None): - new_col = [] - if layout: - layout[-1]["sections"][-1]["columns"].append(new_col) - - return new_col - - page, section, column = None, None, None - for df in self.web_form_fields: - - # breaks - if df.fieldtype == "Page Break": - page = add_page(df) - section, column = None, None - - if df.fieldtype == "Section Break": - section = add_section(df) - column = None - - if df.fieldtype == "Column Break": - column = add_column(df) - - # input - if df.fieldtype not in ("Section Break", "Column Break", "Page Break"): - if not page: - page = add_page() - section, column = None, None - if not section: - section = add_section() - column = None - if column is None: - column = add_column() - column.append(df) - - return layout - def get_parents(self, context): parents = None @@ -568,17 +512,6 @@ def get_web_form_filters(web_form_name): return [field for field in web_form.web_form_fields if field.show_in_filter] -def make_route_string(parameters): - route_string = "" - delimeter = "?" - if isinstance(parameters, dict): - for key in parameters: - if key != "web_form_name": - route_string += route_string + delimeter + key + "=" + cstr(parameters[key]) - delimeter = "&" - return (route_string, delimeter) - - @frappe.whitelist(allow_guest=True) def get_form_data(doctype, docname=None, web_form_name=None): web_form = frappe.get_doc("Web Form", web_form_name) From d17fd039584952d0f08c0e477b6eb3f3784f48fa Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 23 Aug 2022 17:00:27 +0530 Subject: [PATCH 47/57] chore: moved include script from html to web_form.bundle.js --- frappe/public/js/web_form.bundle.js | 3 +++ frappe/website/doctype/web_form/templates/web_form.html | 3 --- frappe/website/doctype/web_form/templates/web_list.html | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/web_form.bundle.js b/frappe/public/js/web_form.bundle.js index ffb7b824bd..a4e9415401 100644 --- a/frappe/public/js/web_form.bundle.js +++ b/frappe/public/js/web_form.bundle.js @@ -1,3 +1,6 @@ +import "./controls.bundle.js"; +import "./dialog.bundle.js"; import "./lib/moment.js"; import "./frappe/utils/datetime.js"; import "./frappe/web_form/webform_script.js"; +import "./bootstrap-4-web.bundle.js"; diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 94dcb226c4..0dbbe4f13e 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -167,10 +167,7 @@ Vue.prototype.frappe = window.frappe; - {{ include_script("controls.bundle.js") }} - {{ include_script("dialog.bundle.js") }} {{ include_script("web_form.bundle.js") }} - {{ include_script("bootstrap-4-web.bundle.js") }} - {{ include_script("controls.bundle.js") }} - {{ include_script("dialog.bundle.js") }} {{ include_script("web_form.bundle.js") }} - {{ include_script("bootstrap-4-web.bundle.js") }} {% endblock script %} {% block style %} From cdb0732646a2d9151b5d121de750d491e2a980ac Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Aug 2022 10:30:51 +0530 Subject: [PATCH 48/57] perf: avoid coalescing wherever possible (#17920) --- frappe/model/db_query.py | 3 +++ frappe/tests/test_db_query.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 97956a7818..a2f597f7bd 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -612,6 +612,9 @@ class DatabaseQuery: ) elif f.operator.lower() in ("in", "not in"): + # if values contain '' or falsy values then only coalesce column + can_be_null = not f.value or any(v is None or v == "" for v in f.value) + values = f.value or "" if isinstance(values, str): values = values.split(",") diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 331b8eb8b8..1e660f8a5d 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -831,6 +831,11 @@ class TestReportview(FrappeTestCase): self.assertTrue(dashboard_settings) + def test_coalesce_with_in_ops(self): + self.assertNotIn("ifnull", frappe.get_all("User", {"name": ("in", ["a", "b"])}, run=0)) + self.assertIn("ifnull", frappe.get_all("User", {"name": ("in", ["a", None])}, run=0)) + self.assertIn("ifnull", frappe.get_all("User", {"name": ("in", ["a", ""])}, run=0)) + def add_child_table_to_blog_post(): child_table = frappe.get_doc( From 9358655ae2d251218586b5ae32dafde6441b6e96 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 24 Aug 2022 10:39:20 +0530 Subject: [PATCH 49/57] fix: 'TopBarItem' object does not support item assignment #17926 TypeError: 'TopBarItem' object does not support item assignment --- frappe/website/doctype/website_settings/website_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index be9b155314..744a3c90a3 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -218,7 +218,7 @@ def modify_header_footer_items(items: list): continue if not top_bar_item.get("child_items"): - top_bar_item["child_items"] = [] + top_bar_item.child_items = [] top_bar_item.child_items.append(item) break From 2623b7e63c5e4f311970a5bcafb20fb076ce1790 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Aug 2022 11:03:53 +0530 Subject: [PATCH 50/57] test: top bar for website --- frappe/tests/utils.py | 1 + .../website_settings/test_website_settings.py | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frappe/tests/utils.py b/frappe/tests/utils.py index 79dfd76238..69db9a01b3 100644 --- a/frappe/tests/utils.py +++ b/frappe/tests/utils.py @@ -89,6 +89,7 @@ def _restore_thread_locals(flags): frappe.local.cache = {} frappe.local.lang = "en" frappe.local.lang_full_dict = None + frappe.local.preload_assets = {"style": [], "script": []} @contextmanager diff --git a/frappe/website/doctype/website_settings/test_website_settings.py b/frappe/website/doctype/website_settings/test_website_settings.py index 1e8410fb6e..c95d380a34 100644 --- a/frappe/website/doctype/website_settings/test_website_settings.py +++ b/frappe/website/doctype/website_settings/test_website_settings.py @@ -1,8 +1,29 @@ # Copyright (c) 2020, Frappe Technologies and Contributors # License: MIT. See LICENSE -# import frappe + +import frappe from frappe.tests.utils import FrappeTestCase +from frappe.website.doctype.website_settings.website_settings import get_website_settings class TestWebsiteSettings(FrappeTestCase): - pass + def test_child_items_in_top_bar(self): + ws = frappe.get_doc("Website Settings") + ws.append( + "top_bar_items", + {"label": "Parent Item"}, + ) + ws.append( + "top_bar_items", + {"parent_label": "Parent Item", "label": "Child Item"}, + ) + ws.save() + + context = get_website_settings() + + for item in context.top_bar_items: + if item.label == "Parent Item": + self.assertEqual(item.child_items[0].label, "Child Item") + break + else: + self.fail("Child items not found") From 63a60c6457cada56f9bdffa9ffd96d02289e2f57 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 24 Aug 2022 08:07:41 +0200 Subject: [PATCH 51/57] fix: translate import warnings (#17924) --- frappe/core/doctype/data_import/importer.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index 378044c219..7ff25118b1 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -990,10 +990,11 @@ class Column: not_exists = list(set(values) - set(exists)) if not_exists: missing_values = ", ".join(not_exists) + message = _("The following values do not exist for {0}: {1}") self.warnings.append( { "col": self.column_number, - "message": (f"The following values do not exist for {self.df.options}: {missing_values}"), + "message": message.format(self.df.options, missing_values), "type": "warning", } ) @@ -1003,17 +1004,18 @@ class Column: if not self.date_format: if self.df.fieldtype == "Time": self.date_format = "%H:%M:%S" - format = "HH:mm:ss" + date_format = "HH:mm:ss" else: self.date_format = "%Y-%m-%d" - format = "yyyy-mm-dd" + date_format = "yyyy-mm-dd" + message = _( + "{0} format could not be determined from the values in this column. Defaulting to {1}." + ) self.warnings.append( { "col": self.column_number, - "message": _( - "{0} format could not be determined from the values in this column. Defaulting to {1}." - ).format(self.df.fieldtype, format), + "message": message.format(self.df.fieldtype, date_format), "type": "info", } ) @@ -1025,13 +1027,11 @@ class Column: if invalid: valid_values = ", ".join(frappe.bold(o) for o in options) invalid_values = ", ".join(frappe.bold(i) for i in invalid) + message = _("The following values are invalid: {0}. Values must be one of {1}") self.warnings.append( { "col": self.column_number, - "message": ( - "The following values are invalid: {}. Values must be" - " one of {}".format(invalid_values, valid_values) - ), + "message": message.format(invalid_values, valid_values), } ) From 4e6a5b6cd9d43e085fe2e5d27f8804856694ec06 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Aug 2022 11:50:23 +0530 Subject: [PATCH 52/57] ci: enable codecov annotations (#17929) --- codecov.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/codecov.yml b/codecov.yml index ad60b8a6d9..125a7ef014 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,17 +2,16 @@ codecov: require_ci_to_pass: yes coverage: + range: 60..90 status: project: - default: false - server-mariadb: + default: target: auto threshold: 0.5% flags: - server-mariadb patch: - default: false - server-mariadb: + default: target: 85% threshold: 0% only_pulls: true From 80d264a7b41e5a14a5c6c2dba400b2f0f2a5f4d3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 19 Aug 2022 20:17:01 +0530 Subject: [PATCH 53/57] fix: permissions dont refresh when switching doc Grid permissions aren't refreshed when switching docs. Setup permissions like this: 1. Normal perm - read 2. If owner - read + write + others 3. Open a normal doc -> is read only so grid is read only. 4. Now go back and open a created doc -> grid will be read only or empty (if no rows) fix: make grid.perm a computed property that refers to form instead of duplicating this. ref: ISS-22-23-01733 --- frappe/public/js/frappe/form/controls/table.js | 1 - frappe/public/js/frappe/form/form.js | 13 ++++++++++--- frappe/public/js/frappe/form/grid.js | 8 ++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/table.js b/frappe/public/js/frappe/form/controls/table.js index 39aba3a234..777705634c 100644 --- a/frappe/public/js/frappe/form/controls/table.js +++ b/frappe/public/js/frappe/form/controls/table.js @@ -8,7 +8,6 @@ frappe.ui.form.ControlTable = class ControlTable extends frappe.ui.form.Control this.grid = new Grid({ frm: this.frm, df: this.df, - perm: this.perm || (this.frm && this.frm.perm) || this.df.perm, parent: this.wrapper, control: this, }); diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index feadc08ead..db121f1c24 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -403,11 +403,17 @@ frappe.ui.form.Form = class FrappeForm { this.doc = frappe.get_doc(this.doctype, this.docname); // check permissions + this.fetch_permissions(); if (!this.has_read_permission()) { frappe.show_not_permitted(__(this.doctype) + " " + __(cstr(this.docname))); return; } + // update grids with new permissions + this.grids.forEach((table) => { + table.grid.refresh(); + }); + // read only (workflow) this.read_only = frappe.workflow.is_read_only(this.doctype, this.docname); if (this.read_only) this.set_read_only(true); @@ -1157,11 +1163,12 @@ frappe.ui.form.Form = class FrappeForm { .attr("target", "_blank"); } - has_read_permission() { - // get perm - var dt = this.parent_doctype ? this.parent_doctype : this.doctype; + fetch_permissions() { + let dt = this.parent_doctype ? this.parent_doctype : this.doctype; this.perm = frappe.perm.get_perm(dt, this.doc); + } + has_read_permission() { if (!this.perm[0].read) { return 0; } diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index fa582a848f..02646a6687 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -43,6 +43,14 @@ export default class Grid { this.debounced_refresh = frappe.utils.debounce(this.debounced_refresh, 100); } + get perm() { + return this.control?.perm || this.frm?.perm || this.df.perm; + } + + set perm(_perm) { + console.error("Setting perm on grid isn't supported, update form's perm instead"); + } + allow_on_grid_editing() { if (frappe.utils.is_xs()) { return false; From 722d57637f2a95bfa25409191f507de58b827436 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Aug 2022 12:26:45 +0530 Subject: [PATCH 54/57] refactor: convert control.perm to a property --- frappe/public/js/frappe/form/controls/base_control.js | 8 ++++++++ frappe/public/js/frappe/form/layout.js | 11 ----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index aed3f9ff3d..07a2c13a9a 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -33,6 +33,14 @@ frappe.ui.form.Control = class BaseControl { this.refresh(); } + get perm() { + return this.frm?.perm; + } + + set perm(_perm) { + console.error("Setting perm on controls isn't supported, update form's perm instead"); + } + // returns "Read", "Write" or "None" // as strings based on permissions get_status(explain) { diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 3bd9e451db..508d35b462 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -194,9 +194,6 @@ frappe.ui.form.Layout = class Layout { this.fields_dict[fieldname].$wrapper.remove(); this.fields_list.splice(this.fields_dict[fieldname], 1, fieldobj); this.fields_dict[fieldname] = fieldobj; - if (this.frm) { - fieldobj.perm = this.frm.perm; - } this.section.fields_list.splice(this.section.fields_dict[fieldname], 1, fieldobj); this.section.fields_dict[fieldname] = fieldobj; this.refresh_fields([df]); @@ -210,9 +207,6 @@ frappe.ui.form.Layout = class Layout { const fieldobj = this.init_field(df, render); this.fields_list.push(fieldobj); this.fields_dict[df.fieldname] = fieldobj; - if (this.frm) { - fieldobj.perm = this.frm.perm; - } this.section.add_field(fieldobj); this.column.add_field(fieldobj); @@ -465,11 +459,6 @@ frappe.ui.form.Layout = class Layout { fieldobj.df = frappe.meta.get_docfield(me.doc.doctype, fieldobj.df.fieldname, me.doc.name) || fieldobj.df; - - // on form change, permissions can change - if (me.frm) { - fieldobj.perm = me.frm.perm; - } } refresh && fieldobj.df && fieldobj.refresh && fieldobj.refresh(); } From 2e91e92227926805450b3b3caefec28e183b63b5 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 24 Aug 2022 14:04:17 +0530 Subject: [PATCH 55/57] fix: Set last_value for check - Revert https://github.com/frappe/frappe/pull/17390 since it used to set this.value before triggering change events... due to this this.last_value & this.value was having same value in change events of some control (eg. link field) - set_input has the responsibility to set this.value and this.last_value properly. --- frappe/public/js/frappe/form/controls/attach.js | 1 + frappe/public/js/frappe/form/controls/base_control.js | 3 --- frappe/public/js/frappe/form/controls/check.js | 3 ++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/attach.js b/frappe/public/js/frappe/form/controls/attach.js index 8b0e19ad64..0c854f6a0a 100644 --- a/frappe/public/js/frappe/form/controls/attach.js +++ b/frappe/public/js/frappe/form/controls/attach.js @@ -89,6 +89,7 @@ frappe.ui.form.ControlAttach = class ControlAttach extends frappe.ui.form.Contro } set_input(value, dataurl) { + this.last_value = this.value; this.value = value; if (this.value) { this.$input.toggle(false); diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index a315abc7e0..9480a04238 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -270,9 +270,6 @@ frappe.ui.form.Control = class BaseControl { } else { if (this.doc) { this.doc[this.df.fieldname] = value; - } else { - // case where input is rendered on dialog where doc is not maintained - this.value = value; } this.set_input(value); return Promise.resolve(); diff --git a/frappe/public/js/frappe/form/controls/check.js b/frappe/public/js/frappe/form/controls/check.js index 3d191c0520..91409ce4e0 100644 --- a/frappe/public/js/frappe/form/controls/check.js +++ b/frappe/public/js/frappe/form/controls/check.js @@ -31,11 +31,12 @@ frappe.ui.form.ControlCheck = class ControlCheck extends frappe.ui.form.ControlD return cint(value); } set_input(value) { + this.last_value = this.value; value = cint(value); + this.value = value; if (this.input) { this.input.checked = value ? 1 : 0; } - this.last_value = value; this.set_mandatory(value); this.set_disp_area(value); } From 40f54d04b79c2fb72dc8cc99fff338c479128609 Mon Sep 17 00:00:00 2001 From: Alaa Alsalehi Date: Sun, 7 Aug 2022 14:33:40 +0300 Subject: [PATCH 56/57] feat(bench): add new bench command for add user --- frappe/commands/site.py | 56 +++++++++++++++++++++++++++++++++++ frappe/tests/test_commands.py | 17 +++++++++++ 2 files changed, 73 insertions(+) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index ab599be121..eb5a732f04 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -496,6 +496,32 @@ def add_system_manager(context, email, first_name, last_name, send_welcome_email raise SiteNotSpecifiedError +@click.command("add-user") +@click.argument("email") +@click.option("--first-name") +@click.option("--last-name") +@click.option("--password") +@click.option("--user-type") +@click.option("--add-role", multiple=True) +@click.option("--send-welcome-email", default=False, is_flag=True) +@pass_context +def add_user_for_sites( + context, email, first_name, last_name, user_type, send_welcome_email, password, add_role +): + "Add user to a site" + import frappe.utils.user + + for site in context.sites: + frappe.connect(site=site) + try: + add_new_user(email, first_name, last_name, user_type, send_welcome_email, password, add_role) + frappe.db.commit() + finally: + frappe.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + @click.command("disable-user") @click.argument("email") @pass_context @@ -1275,8 +1301,38 @@ def handle_data(data: dict, format="json"): render_table(data) +def add_new_user( + email, + first_name=None, + last_name=None, + user_type="System User", + send_welcome_email=False, + password=None, + role=None, +): + user = frappe.new_doc("User") + user.update( + { + "name": email, + "email": email, + "enabled": 1, + "first_name": first_name or email, + "last_name": last_name, + "user_type": user_type, + "send_welcome_email": 1 if send_welcome_email else 0, + } + ) + user.insert() + user.add_roles(*role) + if password: + from frappe.utils.password import update_password + + update_password(user=user.name, pwd=password) + + commands = [ add_system_manager, + add_user_for_sites, backup, drop_site, install_app, diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index f97b2ee754..8c4099b2df 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -691,6 +691,23 @@ class TestSiteMigration(BaseTestCommands): self.assertEqual(result.exception, None) +class TestAddNewUser(BaseTestCommands): + def test_create_user(self): + self.execute( + f"bench --site {TEST_SITE} add-user test@gmail.com --first-name test --last-name test --password 123 --user-type 'System User' --add-role 'Accounts User' --add-role 'Sales User'" + ) + self.assertEqual(self.returncode, 0) + roles = [] + user = frappe.get_doc("User", "test@gmail.com") + for i in user.roles: + role = frappe.get_doc("Has Role", i.name) + roles.append(role.role) + self.assertEqual(user.name, "test@gmail.com") + self.assertIn("Accounts User", roles) + self.assertIn("Sales User", roles) + self.assertTrue(len(roles) == 2) + + class TestBenchBuild(BaseTestCommands): def test_build_assets_size_check(self): with cli(frappe.commands.utils.build, "--force --production") as result: From 6ec5c05e4e25416115e462d90f53d23e82a7d422 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Aug 2022 14:24:10 +0530 Subject: [PATCH 57/57] test: simplify user addition test --- frappe/tests/test_commands.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 8c4099b2df..9fd505a9c3 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -694,18 +694,12 @@ class TestSiteMigration(BaseTestCommands): class TestAddNewUser(BaseTestCommands): def test_create_user(self): self.execute( - f"bench --site {TEST_SITE} add-user test@gmail.com --first-name test --last-name test --password 123 --user-type 'System User' --add-role 'Accounts User' --add-role 'Sales User'" + "bench --site {site} add-user test@gmail.com --first-name test --last-name test --password 123 --user-type 'System User' --add-role 'Accounts User' --add-role 'Sales User'" ) self.assertEqual(self.returncode, 0) - roles = [] user = frappe.get_doc("User", "test@gmail.com") - for i in user.roles: - role = frappe.get_doc("Has Role", i.name) - roles.append(role.role) - self.assertEqual(user.name, "test@gmail.com") - self.assertIn("Accounts User", roles) - self.assertIn("Sales User", roles) - self.assertTrue(len(roles) == 2) + roles = {r.role for r in user.roles} + self.assertEqual({"Accounts User", "Sales User"}, roles) class TestBenchBuild(BaseTestCommands):