From aac89f13d4d442e04eb27f905d45f7ec65a856c2 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 13 Aug 2022 12:57:03 +0530 Subject: [PATCH 01/21] fix: dont mutate `df` when creating custom fields (cherry picked from commit e3d581afdf1e8143be1df583827120a17a09210e) --- frappe/custom/doctype/custom_field/custom_field.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 8a2a2663de..4fbee3d691 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -186,10 +186,13 @@ def create_custom_fields(custom_fields, ignore_validate=False, update=True): field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) if not field: try: + df = df.copy() df["owner"] = "Administrator" create_custom_field(doctype, df, ignore_validate=ignore_validate) + except frappe.exceptions.DuplicateEntryError: pass + elif update: custom_field = frappe.get_doc("Custom Field", field) custom_field.flags.ignore_validate = ignore_validate From 2834ce1b63d14894fc2a0cf192c5277ef6e49877 Mon Sep 17 00:00:00 2001 From: hrwx Date: Fri, 5 Aug 2022 21:33:33 +0100 Subject: [PATCH 02/21] refactor: translatable doctypes (cherry picked from commit b01929405d500155266db632acb42a01c764a339) # Conflicts: # frappe/contacts/doctype/gender/gender.json # frappe/contacts/doctype/salutation/salutation.json --- cypress/integration/control_link.js | 273 ++++++------------ frappe/boot.py | 21 +- frappe/contacts/doctype/gender/gender.json | 19 ++ .../doctype/salutation/salutation.json | 20 ++ frappe/core/doctype/doctype/doctype.json | 8 +- frappe/core/doctype/role/role.json | 5 +- .../customize_form/customize_form.json | 6 +- .../doctype/customize_form/customize_form.py | 2 +- frappe/database/mariadb/framework_mariadb.sql | 2 +- .../database/postgres/framework_postgres.sql | 2 +- frappe/desk/search.py | 38 ++- frappe/geo/doctype/country/country.json | 6 +- frappe/hooks.py | 2 - frappe/public/js/frappe/form/controls/link.js | 18 +- frappe/sessions.py | 1 - frappe/tests/ui_test_helpers.py | 34 +++ frappe/translate.py | 14 +- frappe/utils/boilerplate.py | 255 ++++++++-------- 18 files changed, 350 insertions(+), 376 deletions(-) diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index b34414e5ca..b4984dbc87 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -26,48 +26,31 @@ context("Control Link", () => { }); } - function get_dialog_with_user_link() { + function get_dialog_with_gender_link() { return cy.dialog({ title: "Link", fields: [ { - label: "Select User", - fieldname: "link", - fieldtype: "Link", - options: "User", - }, - ], + 'label': 'Select Gender', + 'fieldname': 'link', + 'fieldtype': 'Link', + 'options': 'Gender', + } + ] }); } it("should set the valid value", () => { get_dialog_with_link().as("dialog"); - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "User", - property: "translate_link_fields", - property_type: "Check", - doctype_or_field: "DocType", - value: "0", - }, - true - ); - - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "ToDo", - property: "show_title_field_in_link", - property_type: "Check", - doctype_or_field: "DocType", - value: "0", - }, - true - ); + cy.insert_doc("Property Setter", { + "doctype": "Property Setter", + "doc_type": "ToDo", + "property": "show_title_field_in_link", + "property_type": "Check", + "doctype_or_field": "DocType", + "value": "0" + }, true); cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); @@ -132,32 +115,15 @@ context("Control Link", () => { }); }); - it("show title field in link", () => { - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "User", - property: "translate_link_fields", - property_type: "Check", - doctype_or_field: "DocType", - value: "0", - }, - true - ); - - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "ToDo", - property: "show_title_field_in_link", - property_type: "Check", - doctype_or_field: "DocType", - value: "1", - }, - true - ); + it('show title field in link', () => { + cy.insert_doc("Property Setter", { + "doctype": "Property Setter", + "doc_type": "ToDo", + "property": "show_title_field_in_link", + "property_type": "Check", + "doctype_or_field": "DocType", + "value": "1" + }, true); cy.clear_cache(); cy.wait(500); @@ -275,153 +241,92 @@ context("Control Link", () => { ); }); - it("show translated text for link with show_title_field_in_link enabled", () => { - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "ToDo", - property: "translate_link_fields", - property_type: "Check", - doctype_or_field: "DocType", - value: "1", - }, - true - ); - - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "ToDo", - property: "show_title_field_in_link", - property_type: "Check", - doctype_or_field: "DocType", - value: "1", - }, - true - ); - - cy.window() - .its("frappe") - .then((frappe) => { - cy.insert_doc("Translation", { - doctype: "Translation", - language: frappe.boot.lang, - source_text: "this is a test todo for link", - translated_text: "this is a translated test todo for link", - }); + it('show translated text for Gender link field with language de with input in de', () => { + cy.call('frappe.tests.ui_test_helpers.insert_translations').then(() => { + cy.window().its('frappe').then(frappe => { + cy.set_value('User', frappe.user.name, {language: 'de'}); }); - cy.clear_cache(); - cy.wait(500); + cy.clear_cache(); + cy.wait(500); + + get_dialog_with_gender_link().as('dialog'); + cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); + + cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); + cy.wait('@search_link'); + cy.get('@input').type('Sonstiges', { delay: 100 }); + cy.wait('@search_link'); + cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); + cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=link] input').blur(); + cy.get('@dialog').then(dialog => { + let field = dialog.get_field('link'); + let value = field.get_value(); + let label = field.get_label_value(); - cy.window() - .its("frappe") - .then((frappe) => { - if (!frappe.boot) { - frappe.boot = { - link_title_doctypes: ["ToDo"], - translatable_doctypes: ["ToDo"], - }; - } else { - frappe.boot.link_title_doctypes = ["ToDo"]; - frappe.boot.translatable_doctypes = ["ToDo"]; - } + expect(value).to.eq('Other'); + expect(label).to.eq('Sonstiges'); }); + }); + }); - get_dialog_with_link().as("dialog"); - cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); + it('show translated text for Gender link field with language de with input in en', () => { + cy.call('frappe.tests.ui_test_helpers.insert_translations').then(() => { + cy.window().its('frappe').then(frappe => { + cy.set_value('User', frappe.user.name, {language: 'de'}); + }); - cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); - cy.wait("@search_link"); - cy.get("@input").type("todo for link", { delay: 100 }); - cy.wait("@search_link"); - cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); - cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); - cy.get(".frappe-control[data-fieldname=link] input").blur(); - cy.get("@dialog").then((dialog) => { - cy.get("@todos").then((todos) => { - let field = dialog.get_field("link"); + cy.clear_cache(); + cy.wait(500); + + get_dialog_with_gender_link().as('dialog'); + cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); + + cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); + cy.wait('@search_link'); + cy.get('@input').type('Other', { delay: 100 }); + cy.wait('@search_link'); + cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); + cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=link] input').blur(); + cy.get('@dialog').then(dialog => { + let field = dialog.get_field('link'); let value = field.get_value(); let label = field.get_label_value(); - expect(value).to.eq(todos[0]); - expect(label).to.eq("this is a translated test todo for link"); + expect(value).to.eq('Other'); + expect(label).to.eq('Sonstiges'); }); }); }); - it("show translated text for link with show_title_field_in_link disabled", () => { - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "User", - property: "translate_link_fields", - property_type: "Check", - doctype_or_field: "DocType", - value: "1", - }, - true - ); - cy.insert_doc( - "Property Setter", - { - doctype: "Property Setter", - doc_type: "ToDo", - property: "show_title_field_in_link", - property_type: "Check", - doctype_or_field: "DocType", - value: "0", - }, - true - ); - - cy.window() - .its("frappe") - .then((frappe) => { - cy.insert_doc("Translation", { - doctype: "Translation", - language: frappe.boot.lang, - source_text: "test@erpnext.com", - translated_text: "translatedtest@erpnext.com", - }); - }); + it('show text for Gender link field with language en', () => { + cy.window().its('frappe').then(frappe => { + cy.set_value('User', frappe.user.name, {language: 'en'}); + }); cy.clear_cache(); cy.wait(500); - cy.window() - .its("frappe") - .then((frappe) => { - if (!frappe.boot) { - frappe.boot = { - translatable_doctypes: ["User"], - }; - } else { - frappe.boot.translatable_doctypes = ["User"]; - } - }); - - get_dialog_with_user_link().as("dialog"); - cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); - - cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); - cy.wait("@search_link"); - cy.get("@input").type("test@erpnext.com", { delay: 100 }); - cy.wait("@search_link"); - cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); - cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); - cy.get(".frappe-control[data-fieldname=link] input").blur(); - cy.get("@dialog").then((dialog) => { - let field = dialog.get_field("link"); + get_dialog_with_gender_link().as('dialog'); + cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); + + cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); + cy.wait('@search_link'); + cy.get('@input').type('Non-Conforming', { delay: 100 }); + cy.wait('@search_link'); + cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); + cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=link] input').blur(); + cy.get('@dialog').then(dialog => { + let field = dialog.get_field('link'); let value = field.get_value(); let label = field.get_label_value(); - expect(value).to.eq("test@erpnext.com"); - expect(label).to.eq("translatedtest@erpnext.com"); + expect(value).to.eq('Non-Conforming'); + expect(label).to.eq('Non-Conforming'); }); }); }); diff --git a/frappe/boot.py b/frappe/boot.py index 98d1e59a57..a22e188bdb 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -19,7 +19,7 @@ from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_p from frappe.social.doctype.energy_point_settings.energy_point_settings import ( is_energy_point_enabled, ) -from frappe.translate import get_lang_dict, get_messages_for_boot +from frappe.translate import get_lang_dict, get_messages_for_boot, get_translated_doctypes from frappe.utils import add_user_info, cstr, get_time_zone from frappe.utils.change_log import get_versions from frappe.website.doctype.web_page_view.web_page_view import is_tracking_enabled @@ -100,7 +100,7 @@ def get_bootinfo(): bootinfo.desk_settings = get_desk_settings() bootinfo.app_logo_url = get_app_logo() bootinfo.link_title_doctypes = get_link_title_doctypes() - bootinfo.translatable_doctypes = get_translatable_doctypes() + bootinfo.translated_doctypes = get_translated_doctypes() return bootinfo @@ -173,7 +173,10 @@ def get_user_pages_or_reports(parent, cache=False): .from_(hasRole) .from_(parentTable) .select( - customRole[parent.lower()].as_("name"), customRole.modified, customRole.ref_doctype, *columns + customRole[parent.lower()].as_("name"), + customRole.modified, + customRole.ref_doctype, + *columns ) .where( (hasRole.parent == customRole.name) @@ -336,7 +339,9 @@ def get_success_action(): def get_link_preview_doctypes(): from frappe.utils import cint - link_preview_doctypes = [d.name for d in frappe.db.get_all("DocType", {"show_preview_popup": 1})] + link_preview_doctypes = [ + d.name for d in frappe.db.get_all("DocType", {"show_preview_popup": 1}) + ] customizations = frappe.get_all( "Property Setter", fields=["doc_type", "value"], filters={"property": "show_preview_popup"} ) @@ -399,14 +404,6 @@ def set_time_zone(bootinfo): } -def get_translatable_doctypes(): - dts = frappe.get_all("DocType", {"translate_link_fields": 1}, pluck="name") - custom_dts = frappe.get_all( - "Property Setter", {"property": "translate_link_fields", "value": "1"}, pluck="doc_type" - ) - return dts + custom_dts - - def load_country_doc(bootinfo): country = frappe.db.get_default("country") if not country: diff --git a/frappe/contacts/doctype/gender/gender.json b/frappe/contacts/doctype/gender/gender.json index 86a066cf0f..285debf66c 100644 --- a/frappe/contacts/doctype/gender/gender.json +++ b/frappe/contacts/doctype/gender/gender.json @@ -43,6 +43,7 @@ "set_only_once": 0, "unique": 0 } +<<<<<<< HEAD ], "has_web_view": 0, "hide_heading": 0, @@ -60,6 +61,15 @@ "name": "Gender", "name_case": "", "owner": "Administrator", +======= + ], + "links": [], + "modified": "2022-08-05 18:33:28.043370", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Gender", + "owner": "Administrator", +>>>>>>> b01929405d (refactor: translatable doctypes) "permissions": [ { "amend": 0, @@ -101,6 +111,7 @@ "submit": 0, "write": 0 } +<<<<<<< HEAD ], "quick_entry": 0, "read_only": 0, @@ -110,4 +121,12 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 0 +======= + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +>>>>>>> b01929405d (refactor: translatable doctypes) } \ No newline at end of file diff --git a/frappe/contacts/doctype/salutation/salutation.json b/frappe/contacts/doctype/salutation/salutation.json index 579f176aa7..12d2ee2db6 100644 --- a/frappe/contacts/doctype/salutation/salutation.json +++ b/frappe/contacts/doctype/salutation/salutation.json @@ -42,6 +42,7 @@ "set_only_once": 0, "unique": 0 } +<<<<<<< HEAD ], "has_web_view": 0, "hide_heading": 0, @@ -59,6 +60,15 @@ "name": "Salutation", "name_case": "", "owner": "Administrator", +======= + ], + "links": [], + "modified": "2022-08-05 18:33:28.196387", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Salutation", + "owner": "Administrator", +>>>>>>> b01929405d (refactor: translatable doctypes) "permissions": [ { "amend": 0, @@ -120,6 +130,7 @@ "submit": 0, "write": 1 } +<<<<<<< HEAD ], "quick_entry": 0, "read_only": 0, @@ -130,3 +141,12 @@ "track_changes": 1, "track_seen": 0 } +======= + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} +>>>>>>> b01929405d (refactor: translatable doctypes) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 4e110202d2..b7b93a8f37 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -47,7 +47,7 @@ "view_settings", "title_field", "show_title_field_in_link", - "translate_link_fields", + "translated_doctype", "search_fields", "default_print_format", "sort_field", @@ -595,7 +595,7 @@ }, { "default": "0", - "fieldname": "translate_link_fields", + "fieldname": "translated_doctype", "fieldtype": "Check", "label": "Translate Link Fields" } @@ -680,7 +680,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2022-02-28 21:56:52.116915", + "modified": "2022-08-05 18:33:27.315351", "modified_by": "Administrator", "module": "Core", "name": "DocType", @@ -716,5 +716,5 @@ "sort_order": "DESC", "states": [], "track_changes": 1, - "translate_link_fields": 1 + "translated_doctype": 1 } \ No newline at end of file diff --git a/frappe/core/doctype/role/role.json b/frappe/core/doctype/role/role.json index e370082fb5..2039d3889d 100644 --- a/frappe/core/doctype/role/role.json +++ b/frappe/core/doctype/role/role.json @@ -148,7 +148,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-01-12 20:18:18.496230", + "modified": "2022-08-05 18:33:27.694065", "modified_by": "Administrator", "module": "Core", "name": "Role", @@ -171,5 +171,6 @@ "sort_field": "modified", "sort_order": "ASC", "states": [], - "track_changes": 1 + "track_changes": 1, + "translated_doctype": 1 } \ No newline at end of file diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 0011f51af4..d66d779963 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -29,7 +29,7 @@ "view_settings_section", "title_field", "show_title_field_in_link", - "translate_link_fields", + "translated_doctype", "image_field", "default_print_format", "column_break_29", @@ -315,7 +315,7 @@ }, { "default": "0", - "fieldname": "translate_link_fields", + "fieldname": "translated_doctype", "fieldtype": "Check", "label": "Translate Link Fields" } @@ -326,7 +326,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-05-13 15:36:16.772277", + "modified": "2022-08-04 15:36:16.772277", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index dc625d1a58..32f0f165dc 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -585,7 +585,7 @@ doctype_properties = { "naming_rule": "Data", "autoname": "Data", "show_title_field_in_link": "Check", - "translate_link_fields": "Check", + "translated_doctype": "Check", } docfield_properties = { diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index dc91873a82..83b6f6b171 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -226,7 +226,7 @@ CREATE TABLE `tabDocType` ( `sender_field` varchar(255) DEFAULT NULL, `show_title_field_in_link` int(1) NOT NULL DEFAULT 0, `migration_hash` varchar(255) DEFAULT NULL, - `translate_link_fields` int(1) NOT NULL DEFAULT 0, + `translated_doctype` int(1) NOT NULL DEFAULT 0, PRIMARY KEY (`name`) ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index 99e94a226f..bc39449113 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -231,7 +231,7 @@ CREATE TABLE "tabDocType" ( "sender_field" varchar(255) DEFAULT NULL, "show_title_field_in_link" smallint NOT NULL DEFAULT 0, "migration_hash" varchar(255) DEFAULT NULL, - "translate_link_fields" smallint NOT NULL DEFAULT 0, + "translated_doctype" smallint NOT NULL DEFAULT 0, PRIMARY KEY ("name") ) ; diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 8ae635093c..4995357512 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -8,6 +8,7 @@ import re import frappe from frappe import _, is_whitelisted from frappe.permissions import has_permission +from frappe.translate import get_translated_doctypes from frappe.utils import cint, cstr, unique @@ -115,7 +116,10 @@ def search_widget( raise e else: frappe.respond_as_web_page( - title="Invalid Method", html="Method not found", indicator_color="red", http_status_code=404 + title="Invalid Method", + html="Method not found", + indicator_color="red", + http_status_code=404, ) return except Exception as e: @@ -146,9 +150,22 @@ def search_widget( filters = [] or_filters = [] - translated_search_doctypes = frappe.get_hooks("translated_search_doctypes") + translated_doctypes = frappe.cache().hget( + "translated_doctypes", "doctypes", get_translated_doctypes + ) + # build from doctype if txt: + field_types = [ + "Data", + "Text", + "Small Text", + "Long Text", + "Link", + "Select", + "Read Only", + "Text Editor", + ] search_fields = ["name"] if meta.title_field: search_fields.append(meta.title_field) @@ -158,13 +175,8 @@ def search_widget( for f in search_fields: fmeta = meta.get_field(f.strip()) - if (doctype not in translated_search_doctypes) and ( - f == "name" - or ( - fmeta - and fmeta.fieldtype - in ["Data", "Text", "Small Text", "Long Text", "Link", "Select", "Read Only", "Text Editor"] - ) + if (doctype not in translated_doctypes) and ( + f == "name" or (fmeta and fmeta.fieldtype in field_types) ): or_filters.append([doctype, f.strip(), "like", f"%{txt}%"]) @@ -188,7 +200,8 @@ def search_widget( # find relevance as location of search term from the beginning of string `name`. used for sorting results. formatted_fields.append( """locate({_txt}, `tab{doctype}`.`name`) as `_relevance`""".format( - _txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), doctype=doctype + _txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), + doctype=doctype, ) ) @@ -206,7 +219,7 @@ def search_widget( else (cint(ignore_user_permissions) and has_permission(doctype, ptype=ptype)) ) - if doctype in translated_search_doctypes: + if doctype in translated_doctypes: page_length = None values = frappe.get_list( @@ -223,12 +236,13 @@ def search_widget( strict=False, ) - if doctype in translated_search_doctypes: + if doctype in translated_doctypes: # Filtering the values array so that query is included in very element values = ( v for v in values if re.search(f"{re.escape(txt)}.*", _(v.name if as_dict else v[0]), re.IGNORECASE) + or re.search(f"{_(re.escape(txt))}.*", _(v.name if as_dict else v[0]), re.IGNORECASE) ) # Sorting the values array so that relevant results always come first diff --git a/frappe/geo/doctype/country/country.json b/frappe/geo/doctype/country/country.json index 8ac3741865..2cbd0a296f 100644 --- a/frappe/geo/doctype/country/country.json +++ b/frappe/geo/doctype/country/country.json @@ -54,7 +54,7 @@ "icon": "fa fa-globe", "idx": 1, "links": [], - "modified": "2020-02-24 15:44:31.837133", + "modified": "2022-08-05 18:33:27.880783", "modified_by": "Administrator", "module": "Geo", "name": "Country", @@ -84,5 +84,7 @@ "quick_entry": 1, "sort_field": "country_name", "sort_order": "ASC", - "track_changes": 1 + "states": [], + "track_changes": 1, + "translated_doctype": 1 } \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index 14e76adc22..85aeea7418 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -373,5 +373,3 @@ override_whitelisted_methods = { "frappe.core.doctype.file.file.move_file": "frappe.core.api.file.move_file", "frappe.core.doctype.file.file.zip_files": "frappe.core.api.file.zip_files", } - -translated_search_doctypes = ["DocType", "Role", "Country", "Gender", "Salutation"] diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 5b5e3a7542..5b1bb4faeb 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -87,7 +87,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat return this.is_translatable() ? __(value) : value; } is_translatable() { - return in_list(frappe.boot?.translatable_doctypes || [], this.get_options()); + return in_list(frappe.boot?.translated_doctypes || [], this.get_options()); } set_link_title(value) { let doctype = this.get_options(); @@ -382,22 +382,6 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat me.$input.val(""); } }); - - this.$input.on("focus", function () { - if (!frappe.boot.translated_search_doctypes.includes(me.df.options)) { - me.show_untranslated(); - } - }); - - this.$input.keydown((e) => { - let BACKSPACE = 8; - if ( - e.keyCode === BACKSPACE && - !frappe.boot.translated_search_doctypes.includes(me.df.options) - ) { - me.show_untranslated(); - } - }); } show_untranslated() { diff --git a/frappe/sessions.py b/frappe/sessions.py index f7c4b34470..acc09dc03c 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -184,7 +184,6 @@ def get(): frappe.get_attr(hook)(bootinfo=bootinfo) bootinfo["lang"] = frappe.translate.get_user_lang() - bootinfo["translated_search_doctypes"] = frappe.get_hooks("translated_search_doctypes") bootinfo["disable_async"] = frappe.conf.disable_async bootinfo["setup_complete"] = cint(frappe.get_system_settings("setup_complete")) diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index b3501e7f09..fa525558a1 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -333,3 +333,37 @@ def insert_doctype_with_child_table_record(name): insert_child(doc, "Drag", "08189DIHAA2981", 0, 0.7, 342628, "2022-05-04") doc.insert() + + +@frappe.whitelist() +def insert_translations(): + translation = [ + { + "doctype": "Translation", + "language": "de", + "source_text": "Other", + "translated_text": "Sonstiges", + }, + { + "doctype": "Translation", + "language": "de", + "source_text": "Genderqueer", + "translated_text": "Geschlechtsspezifisch", + }, + { + "doctype": "Translation", + "language": "de", + "source_text": "Non-Conforming", + "translated_text": "Nicht konform", + }, + { + "doctype": "Translation", + "language": "de", + "source_text": "Prefer not to say", + "translated_text": "Mache lieber keine Angabe", + }, + ] + + for doc in translation: + if not frappe.db.exists("doc"): + frappe.get_doc(doc).insert() diff --git a/frappe/translate.py b/frappe/translate.py index 06176e28ba..1192bd2d49 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -23,7 +23,7 @@ from pypika.terms import PseudoColumn import frappe from frappe.model.utils import InvalidIncludePath, render_include from frappe.query_builder import DocType, Field -from frappe.utils import cstr, get_bench_path, is_html, strip, strip_html_tags +from frappe.utils import cstr, get_bench_path, is_html, strip, strip_html_tags, unique TRANSLATE_PATTERN = re.compile( r"_\(\s*" # starts with literal `_(`, ignore following whitespace/newlines @@ -108,8 +108,8 @@ def get_parent_language(lang: str) -> str: """If the passed language is a variant, return its parent Eg: - 1. zh-TW -> zh - 2. sr-BA -> sr + 1. zh-TW -> zh + 2. sr-BA -> sr """ is_language_variant = "-" in lang if is_language_variant: @@ -1294,3 +1294,11 @@ def set_preferred_language_cookie(preferred_language): def get_preferred_language_cookie(): return frappe.request.cookies.get("preferred_language") + + +def get_translated_doctypes(): + dts = frappe.get_all("DocType", {"translated_doctype": 1}, pluck="name") + custom_dts = frappe.get_all( + "Property Setter", {"property": "translated_doctype", "value": "1"}, pluck="doc_type" + ) + return unique(dts + custom_dts) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index c34ab2d591..b84e7cfc74 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -79,7 +79,8 @@ def is_valid_title(title) -> bool: def _create_app_boilerplate(dest, hooks, no_git=False): frappe.create_folder( - os.path.join(dest, hooks.app_name, hooks.app_name, frappe.scrub(hooks.app_title)), with_init=True + os.path.join(dest, hooks.app_name, hooks.app_name, frappe.scrub(hooks.app_title)), + with_init=True, ) frappe.create_folder( os.path.join(dest, hooks.app_name, hooks.app_name, "templates"), with_init=True @@ -249,8 +250,8 @@ app_license = "{app_license}" # add methods and filters to jinja environment # jinja = {{ -# "methods": "{app_name}.utils.jinja_methods", -# "filters": "{app_name}.utils.jinja_filters" +# "methods": "{app_name}.utils.jinja_methods", +# "filters": "{app_name}.utils.jinja_filters" # }} # Installation @@ -276,11 +277,11 @@ app_license = "{app_license}" # Permissions evaluated in scripted ways # permission_query_conditions = {{ -# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", +# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", # }} # # has_permission = {{ -# "Event": "frappe.desk.doctype.event.event.has_permission", +# "Event": "frappe.desk.doctype.event.event.has_permission", # }} # DocType Class @@ -288,7 +289,7 @@ app_license = "{app_license}" # Override standard doctype classes # override_doctype_class = {{ -# "ToDo": "custom_app.overrides.CustomToDo" +# "ToDo": "custom_app.overrides.CustomToDo" # }} # Document Events @@ -296,10 +297,10 @@ app_license = "{app_license}" # Hook on document methods and events # doc_events = {{ -# "*": {{ -# "on_update": "method", -# "on_cancel": "method", -# "on_trash": "method" +# "*": {{ +# "on_update": "method", +# "on_cancel": "method", +# "on_trash": "method" # }} # }} @@ -307,21 +308,21 @@ app_license = "{app_license}" # --------------- # scheduler_events = {{ -# "all": [ -# "{app_name}.tasks.all" -# ], -# "daily": [ -# "{app_name}.tasks.daily" -# ], -# "hourly": [ -# "{app_name}.tasks.hourly" -# ], -# "weekly": [ -# "{app_name}.tasks.weekly" -# ], -# "monthly": [ -# "{app_name}.tasks.monthly" -# ], +# "all": [ +# "{app_name}.tasks.all" +# ], +# "daily": [ +# "{app_name}.tasks.daily" +# ], +# "hourly": [ +# "{app_name}.tasks.hourly" +# ], +# "weekly": [ +# "{app_name}.tasks.weekly" +# ], +# "monthly": [ +# "{app_name}.tasks.monthly" +# ], # }} # Testing @@ -333,14 +334,14 @@ app_license = "{app_license}" # ------------------------------ # # override_whitelisted_methods = {{ -# "frappe.desk.doctype.event.event.get_events": "{app_name}.event.get_events" +# "frappe.desk.doctype.event.event.get_events": "{app_name}.event.get_events" # }} # # each overriding function accepts a `data` argument; # generated from the base implementation of the doctype dashboard, # along with any modifications made in other Frappe apps # override_doctype_dashboards = {{ -# "Task": "{app_name}.task.get_dashboard_data" +# "Task": "{app_name}.task.get_dashboard_data" # }} # exempt linked doctypes from being automatically cancelled @@ -352,40 +353,32 @@ app_license = "{app_license}" # -------------------- # user_data_fields = [ -# {{ -# "doctype": "{{doctype_1}}", -# "filter_by": "{{filter_by}}", -# "redact_fields": ["{{field_1}}", "{{field_2}}"], -# "partial": 1, -# }}, -# {{ -# "doctype": "{{doctype_2}}", -# "filter_by": "{{filter_by}}", -# "partial": 1, -# }}, -# {{ -# "doctype": "{{doctype_3}}", -# "strict": False, -# }}, -# {{ -# "doctype": "{{doctype_4}}" -# }} +# {{ +# "doctype": "{{doctype_1}}", +# "filter_by": "{{filter_by}}", +# "redact_fields": ["{{field_1}}", "{{field_2}}"], +# "partial": 1, +# }}, +# {{ +# "doctype": "{{doctype_2}}", +# "filter_by": "{{filter_by}}", +# "partial": 1, +# }}, +# {{ +# "doctype": "{{doctype_3}}", +# "strict": False, +# }}, +# {{ +# "doctype": "{{doctype_4}}" +# }} # ] # Authentication and authorization # -------------------------------- # auth_hooks = [ -# "{app_name}.auth.validate" +# "{app_name}.auth.validate" # ] - -# Translation -# -------------------------------- - -# Make link fields search translated document names for these DocTypes -# Recommended only for DocTypes which have limited documents with untranslated names -# For example: Role, Gender, etc. -# translated_search_doctypes = [] """ desktop_template = """from frappe import _ @@ -447,8 +440,8 @@ name: CI on: push: - branches: - - develop + branches: + - develop pull_request: concurrency: @@ -457,79 +450,79 @@ concurrency: jobs: tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - name: Server - - services: - mariadb: - image: mariadb:10.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Setup Node - uses: actions/setup-node@v2 - with: - node-version: 14 - check-latest: true - - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{{{ runner.os }}}}-pip-${{{{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}}} - restore-keys: | - ${{{{ runner.os }}}}-pip- - ${{{{ runner.os }}}}- - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: 'echo "::set-output name=dir::$(yarn cache dir)"' - - - uses: actions/cache@v2 - id: yarn-cache - with: - path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}} - key: ${{{{ runner.os }}}}-yarn-${{{{ hashFiles('**/yarn.lock') }}}} - restore-keys: | - ${{{{ runner.os }}}}-yarn- - - - name: Setup - run: | - pip install frappe-bench - bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench - mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" - mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" - - - name: Install - working-directory: /home/runner/frappe-bench - run: | - bench get-app {app_name} $GITHUB_WORKSPACE - bench setup requirements --dev - bench new-site --db-root-password root --admin-password admin test_site - bench --site test_site install-app {app_name} - bench build - env: - CI: 'Yes' - - - name: Run Tests - working-directory: /home/runner/frappe-bench - run: | - bench --site test_site set-config allow_tests true - bench --site test_site run-tests --app {app_name} - env: - TYPE: server + runs-on: ubuntu-latest + strategy: + fail-fast: false + name: Server + + services: + mariadb: + image: mariadb:10.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{{{ runner.os }}}}-pip-${{{{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}}} + restore-keys: | + ${{{{ runner.os }}}}-pip- + ${{{{ runner.os }}}}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: 'echo "::set-output name=dir::$(yarn cache dir)"' + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}} + key: ${{{{ runner.os }}}}-yarn-${{{{ hashFiles('**/yarn.lock') }}}} + restore-keys: | + ${{{{ runner.os }}}}-yarn- + + - name: Setup + run: | + pip install frappe-bench + bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench + mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" + mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + + - name: Install + working-directory: /home/runner/frappe-bench + run: | + bench get-app {app_name} $GITHUB_WORKSPACE + bench setup requirements --dev + bench new-site --db-root-password root --admin-password admin test_site + bench --site test_site install-app {app_name} + bench build + env: + CI: 'Yes' + + - name: Run Tests + working-directory: /home/runner/frappe-bench + run: | + bench --site test_site set-config allow_tests true + bench --site test_site run-tests --app {app_name} + env: + TYPE: server """ From 1b1886f054c604e225a871e23bb17861490a24ec Mon Sep 17 00:00:00 2001 From: hrwx Date: Sat, 6 Aug 2022 00:22:35 +0100 Subject: [PATCH 03/21] chore: linter fixes (cherry picked from commit db2e898397f6a55bd996800c5d94c58d1b12a757) --- cypress/integration/control_link.js | 173 +++++++++++++++------------- frappe/boot.py | 9 +- 2 files changed, 95 insertions(+), 87 deletions(-) diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index b4984dbc87..147a838eab 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -31,26 +31,30 @@ context("Control Link", () => { title: "Link", fields: [ { - 'label': 'Select Gender', - 'fieldname': 'link', - 'fieldtype': 'Link', - 'options': 'Gender', - } - ] + label: "Select Gender", + fieldname: "link", + fieldtype: "Link", + options: "Gender", + }, + ], }); } it("should set the valid value", () => { get_dialog_with_link().as("dialog"); - cy.insert_doc("Property Setter", { - "doctype": "Property Setter", - "doc_type": "ToDo", - "property": "show_title_field_in_link", - "property_type": "Check", - "doctype_or_field": "DocType", - "value": "0" - }, true); + cy.insert_doc( + "Property Setter", + { + doctype: "Property Setter", + doc_type: "ToDo", + property: "show_title_field_in_link", + property_type: "Check", + doctype_or_field: "DocType", + value: "0", + }, + true + ); cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); @@ -115,15 +119,19 @@ context("Control Link", () => { }); }); - it('show title field in link', () => { - cy.insert_doc("Property Setter", { - "doctype": "Property Setter", - "doc_type": "ToDo", - "property": "show_title_field_in_link", - "property_type": "Check", - "doctype_or_field": "DocType", - "value": "1" - }, true); + it("show title field in link", () => { + cy.insert_doc( + "Property Setter", + { + doctype: "Property Setter", + doc_type: "ToDo", + property: "show_title_field_in_link", + property_type: "Check", + doctype_or_field: "DocType", + value: "1", + }, + true + ); cy.clear_cache(); cy.wait(500); @@ -241,92 +249,97 @@ context("Control Link", () => { ); }); - it('show translated text for Gender link field with language de with input in de', () => { - cy.call('frappe.tests.ui_test_helpers.insert_translations').then(() => { - cy.window().its('frappe').then(frappe => { - cy.set_value('User', frappe.user.name, {language: 'de'}); - }); + it("show translated text for Gender link field with language de with input in de", () => { + cy.call("frappe.tests.ui_test_helpers.insert_translations").then(() => { + cy.window() + .its("frappe") + .then((frappe) => { + cy.set_value("User", frappe.user.name, { language: "de" }); + }); cy.clear_cache(); cy.wait(500); - get_dialog_with_gender_link().as('dialog'); - cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); - - cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); - cy.wait('@search_link'); - cy.get('@input').type('Sonstiges', { delay: 100 }); - cy.wait('@search_link'); - cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); - cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); - cy.get('.frappe-control[data-fieldname=link] input').blur(); - cy.get('@dialog').then(dialog => { - let field = dialog.get_field('link'); + get_dialog_with_gender_link().as("dialog"); + cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); + + cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("Sonstiges", { delay: 100 }); + cy.wait("@search_link"); + cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".frappe-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + let field = dialog.get_field("link"); let value = field.get_value(); let label = field.get_label_value(); - expect(value).to.eq('Other'); - expect(label).to.eq('Sonstiges'); + expect(value).to.eq("Other"); + expect(label).to.eq("Sonstiges"); }); }); }); - it('show translated text for Gender link field with language de with input in en', () => { - cy.call('frappe.tests.ui_test_helpers.insert_translations').then(() => { - cy.window().its('frappe').then(frappe => { - cy.set_value('User', frappe.user.name, {language: 'de'}); - }); + it("show translated text for Gender link field with language de with input in en", () => { + cy.call("frappe.tests.ui_test_helpers.insert_translations").then(() => { + cy.window() + .its("frappe") + .then((frappe) => { + cy.set_value("User", frappe.user.name, { language: "de" }); + }); cy.clear_cache(); cy.wait(500); - get_dialog_with_gender_link().as('dialog'); - cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); - - cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); - cy.wait('@search_link'); - cy.get('@input').type('Other', { delay: 100 }); - cy.wait('@search_link'); - cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); - cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); - cy.get('.frappe-control[data-fieldname=link] input').blur(); - cy.get('@dialog').then(dialog => { - let field = dialog.get_field('link'); + get_dialog_with_gender_link().as("dialog"); + cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); + + cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("Other", { delay: 100 }); + cy.wait("@search_link"); + cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".frappe-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + let field = dialog.get_field("link"); let value = field.get_value(); let label = field.get_label_value(); - expect(value).to.eq('Other'); - expect(label).to.eq('Sonstiges'); + expect(value).to.eq("Other"); + expect(label).to.eq("Sonstiges"); }); }); }); - - it('show text for Gender link field with language en', () => { - cy.window().its('frappe').then(frappe => { - cy.set_value('User', frappe.user.name, {language: 'en'}); - }); + it("show text for Gender link field with language en", () => { + cy.window() + .its("frappe") + .then((frappe) => { + cy.set_value("User", frappe.user.name, { language: "en" }); + }); cy.clear_cache(); cy.wait(500); - get_dialog_with_gender_link().as('dialog'); - cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); - - cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); - cy.wait('@search_link'); - cy.get('@input').type('Non-Conforming', { delay: 100 }); - cy.wait('@search_link'); - cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); - cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); - cy.get('.frappe-control[data-fieldname=link] input').blur(); - cy.get('@dialog').then(dialog => { - let field = dialog.get_field('link'); + get_dialog_with_gender_link().as("dialog"); + cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); + + cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("Non-Conforming", { delay: 100 }); + cy.wait("@search_link"); + cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".frappe-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + let field = dialog.get_field("link"); let value = field.get_value(); let label = field.get_label_value(); - expect(value).to.eq('Non-Conforming'); - expect(label).to.eq('Non-Conforming'); + expect(value).to.eq("Non-Conforming"); + expect(label).to.eq("Non-Conforming"); }); }); }); diff --git a/frappe/boot.py b/frappe/boot.py index a22e188bdb..5121215106 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -173,10 +173,7 @@ def get_user_pages_or_reports(parent, cache=False): .from_(hasRole) .from_(parentTable) .select( - customRole[parent.lower()].as_("name"), - customRole.modified, - customRole.ref_doctype, - *columns + customRole[parent.lower()].as_("name"), customRole.modified, customRole.ref_doctype, *columns ) .where( (hasRole.parent == customRole.name) @@ -339,9 +336,7 @@ def get_success_action(): def get_link_preview_doctypes(): from frappe.utils import cint - link_preview_doctypes = [ - d.name for d in frappe.db.get_all("DocType", {"show_preview_popup": 1}) - ] + link_preview_doctypes = [d.name for d in frappe.db.get_all("DocType", {"show_preview_popup": 1})] customizations = frappe.get_all( "Property Setter", fields=["doc_type", "value"], filters={"property": "show_preview_popup"} ) From 527cc9c9a4051cdcce0f155104ca8dea8b0e0c61 Mon Sep 17 00:00:00 2001 From: hrwx Date: Sat, 6 Aug 2022 10:52:19 +0100 Subject: [PATCH 04/21] chore: revert changes to github workflow template (cherry picked from commit 476b628a4e66a98093e79a1fc847997a8d413e7e) --- frappe/utils/boilerplate.py | 154 ++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index b84e7cfc74..f0f3173425 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -440,8 +440,8 @@ name: CI on: push: - branches: - - develop + branches: + - develop pull_request: concurrency: @@ -450,79 +450,79 @@ concurrency: jobs: tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - name: Server - - services: - mariadb: - image: mariadb:10.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Setup Node - uses: actions/setup-node@v2 - with: - node-version: 14 - check-latest: true - - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{{{ runner.os }}}}-pip-${{{{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}}} - restore-keys: | - ${{{{ runner.os }}}}-pip- - ${{{{ runner.os }}}}- - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: 'echo "::set-output name=dir::$(yarn cache dir)"' - - - uses: actions/cache@v2 - id: yarn-cache - with: - path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}} - key: ${{{{ runner.os }}}}-yarn-${{{{ hashFiles('**/yarn.lock') }}}} - restore-keys: | - ${{{{ runner.os }}}}-yarn- - - - name: Setup - run: | - pip install frappe-bench - bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench - mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" - mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" - - - name: Install - working-directory: /home/runner/frappe-bench - run: | - bench get-app {app_name} $GITHUB_WORKSPACE - bench setup requirements --dev - bench new-site --db-root-password root --admin-password admin test_site - bench --site test_site install-app {app_name} - bench build - env: - CI: 'Yes' - - - name: Run Tests - working-directory: /home/runner/frappe-bench - run: | - bench --site test_site set-config allow_tests true - bench --site test_site run-tests --app {app_name} - env: - TYPE: server + runs-on: ubuntu-latest + strategy: + fail-fast: false + name: Server + + services: + mariadb: + image: mariadb:10.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{{{ runner.os }}}}-pip-${{{{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}}} + restore-keys: | + ${{{{ runner.os }}}}-pip- + ${{{{ runner.os }}}}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: 'echo "::set-output name=dir::$(yarn cache dir)"' + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}} + key: ${{{{ runner.os }}}}-yarn-${{{{ hashFiles('**/yarn.lock') }}}} + restore-keys: | + ${{{{ runner.os }}}}-yarn- + + - name: Setup + run: | + pip install frappe-bench + bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench + mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" + mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + + - name: Install + working-directory: /home/runner/frappe-bench + run: | + bench get-app {app_name} $GITHUB_WORKSPACE + bench setup requirements --dev + bench new-site --db-root-password root --admin-password admin test_site + bench --site test_site install-app {app_name} + bench build + env: + CI: 'Yes' + + - name: Run Tests + working-directory: /home/runner/frappe-bench + run: | + bench --site test_site set-config allow_tests true + bench --site test_site run-tests --app {app_name} + env: + TYPE: server """ From a8b2ecb0296fffd5dca57cc7e65b4d0cf419f3e0 Mon Sep 17 00:00:00 2001 From: hrwx Date: Mon, 8 Aug 2022 22:06:59 +0100 Subject: [PATCH 05/21] chore: set middle name as _Test User for admin (cherry picked from commit d9c3c03522584b86a8c2a3da0185cf55ac931ede) --- cypress/integration/web_form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js index 4c99451146..9173bfaeb3 100644 --- a/cypress/integration/web_form.js +++ b/cypress/integration/web_form.js @@ -237,7 +237,7 @@ context("Web Form", () => { cy.get(".web-form-actions a").contains("Edit").click(); - cy.fill_field("last_name", "_Test User"); + cy.fill_field("middle_name", "_Test User"); cy.get(".web-form-actions .btn-primary").click(); cy.url().should("include", "/me"); @@ -249,7 +249,7 @@ context("Web Form", () => { cy.get(".web-form-actions a").contains("Edit").click(); - cy.fill_field("last_name", "_Test User"); + cy.fill_field("middle_name", "_Test User"); cy.get(".btn-next").should("be.visible"); cy.get(".btn-next").click(); From d9073e469343e833e13f76cf2c4681eb05e3386c Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 9 Aug 2022 09:49:21 +0100 Subject: [PATCH 06/21] chore: linter fixes (cherry picked from commit b6281ffef6edc0ce89f431e1eb28519e20ee1038) --- frappe/translate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 1192bd2d49..5cee36342a 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -108,8 +108,8 @@ def get_parent_language(lang: str) -> str: """If the passed language is a variant, return its parent Eg: - 1. zh-TW -> zh - 2. sr-BA -> sr + 1. zh-TW -> zh + 2. sr-BA -> sr """ is_language_variant = "-" in lang if is_language_variant: From 00bc70d330a84ec159350ea11dd4b238c202af13 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 9 Aug 2022 11:53:21 +0100 Subject: [PATCH 07/21] chore: linter fixes (cherry picked from commit 192aa7a9ab147e07d86336ec3c43e727ba58cb89) --- frappe/translate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 5cee36342a..c644636332 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -108,8 +108,8 @@ def get_parent_language(lang: str) -> str: """If the passed language is a variant, return its parent Eg: - 1. zh-TW -> zh - 2. sr-BA -> sr + 1. zh-TW -> zh + 2. sr-BA -> sr """ is_language_variant = "-" in lang if is_language_variant: From 578bacee08c05faa83d6535eae40edd89daa8ab1 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 11 Aug 2022 13:47:21 +0100 Subject: [PATCH 08/21] chore: set correct translation for ui tests Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> (cherry picked from commit 2aa140d4c34401d00c813b91b4b359e461a3104f) --- frappe/tests/ui_test_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index fa525558a1..b0ef472cbd 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -348,7 +348,7 @@ def insert_translations(): "doctype": "Translation", "language": "de", "source_text": "Genderqueer", - "translated_text": "Geschlechtsspezifisch", + "translated_text": "Nichtbinär", }, { "doctype": "Translation", @@ -360,7 +360,7 @@ def insert_translations(): "doctype": "Translation", "language": "de", "source_text": "Prefer not to say", - "translated_text": "Mache lieber keine Angabe", + "translated_text": "Keine Angabe", }, ] From d50db2e0c65032897ec10f0499f3be375e62e521 Mon Sep 17 00:00:00 2001 From: hrwx Date: Fri, 12 Aug 2022 17:04:06 +0100 Subject: [PATCH 09/21] chore: remove untranslated matching (cherry picked from commit 42a3fe0fa2e8953423fb2d077be0388adab46ece) --- cypress/integration/control_link.js | 32 ----------------------------- frappe/desk/search.py | 1 - 2 files changed, 33 deletions(-) diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index 147a838eab..44b78e731b 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -281,38 +281,6 @@ context("Control Link", () => { }); }); - it("show translated text for Gender link field with language de with input in en", () => { - cy.call("frappe.tests.ui_test_helpers.insert_translations").then(() => { - cy.window() - .its("frappe") - .then((frappe) => { - cy.set_value("User", frappe.user.name, { language: "de" }); - }); - - cy.clear_cache(); - cy.wait(500); - - get_dialog_with_gender_link().as("dialog"); - cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link"); - - cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); - cy.wait("@search_link"); - cy.get("@input").type("Other", { delay: 100 }); - cy.wait("@search_link"); - cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible"); - cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); - cy.get(".frappe-control[data-fieldname=link] input").blur(); - cy.get("@dialog").then((dialog) => { - let field = dialog.get_field("link"); - let value = field.get_value(); - let label = field.get_label_value(); - - expect(value).to.eq("Other"); - expect(label).to.eq("Sonstiges"); - }); - }); - }); - it("show text for Gender link field with language en", () => { cy.window() .its("frappe") diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 4995357512..53a8c39ee5 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -242,7 +242,6 @@ def search_widget( v for v in values if re.search(f"{re.escape(txt)}.*", _(v.name if as_dict else v[0]), re.IGNORECASE) - or re.search(f"{_(re.escape(txt))}.*", _(v.name if as_dict else v[0]), re.IGNORECASE) ) # Sorting the values array so that relevant results always come first From 7e0d1eca5852d9f6005f16f7ed2fcedf6fa368cd Mon Sep 17 00:00:00 2001 From: hrwx Date: Sat, 13 Aug 2022 18:22:41 +0100 Subject: [PATCH 10/21] chore: resolve conflicts --- frappe/contacts/doctype/gender/gender.json | 134 +++----------- .../doctype/salutation/salutation.json | 165 ++++-------------- 2 files changed, 62 insertions(+), 237 deletions(-) diff --git a/frappe/contacts/doctype/gender/gender.json b/frappe/contacts/doctype/gender/gender.json index 285debf66c..34e1ddaee6 100644 --- a/frappe/contacts/doctype/gender/gender.json +++ b/frappe/contacts/doctype/gender/gender.json @@ -1,67 +1,20 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:gender", - "beta": 0, - "creation": "2017-04-10 12:11:36.526508", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:gender", + "creation": "2017-04-10 12:11:36.526508", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "gender" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gender", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gender", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "gender", + "fieldtype": "Data", + "label": "Gender", + "unique": 1 } -<<<<<<< HEAD - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-04-10 12:17:04.848338", - "modified_by": "Administrator", - "module": "Contacts", - "name": "Gender", - "name_case": "", - "owner": "Administrator", -======= ], "links": [], "modified": "2022-08-05 18:33:28.043370", @@ -69,64 +22,27 @@ "module": "Contacts", "name": "Gender", "owner": "Administrator", ->>>>>>> b01929405d (refactor: translatable doctypes) "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "All" } -<<<<<<< HEAD - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -======= ], "sort_field": "modified", "sort_order": "DESC", "states": [], "track_changes": 1, "translated_doctype": 1 ->>>>>>> b01929405d (refactor: translatable doctypes) } \ No newline at end of file diff --git a/frappe/contacts/doctype/salutation/salutation.json b/frappe/contacts/doctype/salutation/salutation.json index 12d2ee2db6..98ed082e69 100644 --- a/frappe/contacts/doctype/salutation/salutation.json +++ b/frappe/contacts/doctype/salutation/salutation.json @@ -1,66 +1,21 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:salutation", - "beta": 0, - "creation": "2017-04-10 12:17:58.071915", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:salutation", + "creation": "2017-04-10 12:17:58.071915", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salutation" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salutation", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salutation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "salutation", + "fieldtype": "Data", + "label": "Salutation", + "unique": 1 } -<<<<<<< HEAD - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-14 12:55:18.855578", - "modified_by": "Administrator", - "module": "Contacts", - "name": "Salutation", - "name_case": "", - "owner": "Administrator", -======= ], "links": [], "modified": "2022-08-05 18:33:28.196387", @@ -68,85 +23,39 @@ "module": "Contacts", "name": "Salutation", "owner": "Administrator", ->>>>>>> b01929405d (refactor: translatable doctypes) "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "All" + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, "write": 1 } -<<<<<<< HEAD - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} -======= ], "sort_field": "modified", "sort_order": "DESC", "states": [], "track_changes": 1, "translated_doctype": 1 -} ->>>>>>> b01929405d (refactor: translatable doctypes) +} \ No newline at end of file From 9107fbbcd58caa91bba6857ac62b019bea8e92c2 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 13 Aug 2022 14:53:41 +0530 Subject: [PATCH 11/21] fix: default `filters` to `undefined` instead of `null` (cherry picked from commit 7545bf2219144bfc2316e278577c4acf911fe2c6) --- frappe/public/js/frappe/db.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index 7e9dd1e875..661772fffb 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -71,7 +71,7 @@ frappe.db = { }, }); }, - get_doc: function (doctype, name, filters = null) { + get_doc: function (doctype, name, filters) { return new Promise((resolve, reject) => { frappe .call({ From cc03176f50bfe023a4468da26d682928d3e237b6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 14 Aug 2022 13:42:24 +0530 Subject: [PATCH 12/21] ci: disable coverge on stable branch Coverage has ~30% overhead, not required on stable branch --- .github/helper/install.sh | 3 -- .github/workflows/server-mariadb-tests.yml | 12 +------ .github/workflows/server-postgres-tests.yml | 12 +------ .github/workflows/ui-tests.yml | 36 ++------------------- 4 files changed, 4 insertions(+), 59 deletions(-) diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 1a2c62c973..3468917cc9 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -52,9 +52,6 @@ if [ "$TYPE" == "server" ]; then sed -i 's/^socketio:/# socketio:/g' Procfile; sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile; fi -if [ "$TYPE" == "ui" ]; then - sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile; -fi echo "Starting Bench..." diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml index c8ccfa7862..6f2d9dc7ef 100644 --- a/.github/workflows/server-mariadb-tests.yml +++ b/.github/workflows/server-mariadb-tests.yml @@ -122,17 +122,7 @@ jobs: - name: Run Tests if: ${{ steps.check-build.outputs.build == 'strawberry' }} - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator env: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - - name: Upload coverage data - if: ${{ steps.check-build.outputs.build == 'strawberry' }} - uses: codecov/codecov-action@v3 - with: - name: MariaDB - fail_ci_if_error: true - files: /home/runner/frappe-bench/sites/coverage.xml - verbose: true - flags: server diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml index 9760067197..659fd00472 100644 --- a/.github/workflows/server-postgres-tests.yml +++ b/.github/workflows/server-postgres-tests.yml @@ -125,17 +125,7 @@ jobs: - name: Run Tests if: ${{ steps.check-build.outputs.build == 'strawberry' }} - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator env: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - - name: Upload coverage data - if: ${{ steps.check-build.outputs.build == 'strawberry' }} - uses: codecov/codecov-action@v3 - with: - name: Postgres - fail_ci_if_error: true - files: /home/runner/frappe-bench/sites/coverage.xml - verbose: true - flags: server diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 115b8c2b1b..f6293db18f 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -144,42 +144,10 @@ jobs: - name: UI Tests if: ${{ steps.check-build.outputs.build == 'strawberry' }} - run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --with-coverage --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT + run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT env: CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb - - name: Stop server - if: ${{ steps.check-build.outputs.build-server == 'strawberry' }} - run: | - ps -ef | grep "frappe serve" | awk '{print $2}' | xargs kill -s SIGINT 2> /dev/null || true - sleep 5 - - - name: Check If Coverage Report Exists - id: check_coverage - uses: andstor/file-existence-action@v1 - with: - files: "/home/runner/frappe-bench/apps/frappe/.cypress-coverage/clover.xml" - - - name: Upload Coverage Data - if: ${{ steps.check-build.outputs.build == 'strawberry' && steps.check_coverage.outputs.files_exists == 'true' }} - uses: codecov/codecov-action@v3 - with: - name: Cypress - fail_ci_if_error: true - directory: /home/runner/frappe-bench/apps/frappe/.cypress-coverage/ - verbose: true - flags: ui-tests - - - name: Upload Server Coverage Data - if: ${{ steps.check-build.outputs.build-server == 'strawberry' }} - uses: codecov/codecov-action@v3 - with: - name: MariaDB - fail_ci_if_error: true - files: /home/runner/frappe-bench/sites/coverage.xml - verbose: true - flags: server - - name: Show bench console if tests failed if: ${{ failure() }} - run: cat ~/frappe-bench/bench_start.log \ No newline at end of file + run: cat ~/frappe-bench/bench_start.log From 79950501f75d647d03ba09bf65b61cbc6076fc94 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 14 Aug 2022 18:55:30 +0200 Subject: [PATCH 13/21] feat(Language): show title in link field (cherry picked from commit 5c3d86209d135ca59fbcf3035bedcaff4be83e5d) --- frappe/core/doctype/language/language.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/language/language.json b/frappe/core/doctype/language/language.json index 9ab8f55f6b..7e9bbb1038 100644 --- a/frappe/core/doctype/language/language.json +++ b/frappe/core/doctype/language/language.json @@ -51,7 +51,7 @@ "icon": "fa fa-globe", "in_create": 1, "links": [], - "modified": "2021-10-18 14:02:06.818219", + "modified": "2022-08-14 18:54:03.490836", "modified_by": "Administrator", "module": "Core", "name": "Language", @@ -76,8 +76,10 @@ } ], "search_fields": "language_name", + "show_title_field_in_link": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "title_field": "language_name", "track_changes": 1 } \ No newline at end of file From 10faee0a04aeb0caf9f67dee764461215df2b5ce Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:31:58 +0530 Subject: [PATCH 14/21] feat: custom __link_options (#17699) (#17838) * feat: custom __link_optins * chore: linter fixes * test: add ui test for custom link option (cherry picked from commit 1aa3f75d4c75221fea5815b297b7cd3803289925) Co-authored-by: Himanshu --- cypress/integration/control_link.js | 26 +++++++++++++++++++ frappe/public/js/frappe/form/controls/link.js | 11 +++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index 44b78e731b..240515be45 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -310,4 +310,30 @@ context("Control Link", () => { expect(label).to.eq("Non-Conforming"); }); }); + + it("show custom link option", () => { + cy.window() + .its("frappe") + .then((frappe) => { + frappe.ui.form.ControlLink.link_options = (link) => { + return [ + { + html: + "" + + " " + + "Custom Link Option" + + "", + label: "Custom Link Option", + value: "custom__link_option", + action: () => {}, + }, + ]; + }; + + get_dialog_with_link().as("dialog"); + cy.get(".frappe-control[data-fieldname=link] input").focus().as("input"); + cy.get("@input").type("custom", { delay: 100 }); + cy.get(".custom-link-option").should("be.visible"); + }); + }); }); diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 5b1bb4faeb..e34fd01008 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -288,8 +288,17 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat action: me.new_doc, }); } - // advanced search + //custom link actions + let custom__link_options = + frappe.ui.form.ControlLink.link_options && + frappe.ui.form.ControlLink.link_options(me); + + if (custom__link_options) { + r.results = r.results.concat(custom__link_options); + } + + // advanced search if (locals && locals["DocType"]) { // not applicable in web forms r.results.push({ From c8d4fbd6dd929e84bd1c4d2d5d320957d48dc2d5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:39:34 +0530 Subject: [PATCH 15/21] fix: appropriate password hint (backport #17623) (#17839) * fix: appropriate password hint (cherry picked from commit 4a04e9f9dcf385dd417a37657bbbc012d684080a) * refactor: handle_password_test_fail (cherry picked from commit 244ffb4e233b7de3fc5f3cc7fdb25a9f848f9316) * refactor: pass feedback (cherry picked from commit 3e351fe4fa30c13c407145aee744b0c5771968f9) * test: password strength test and error (cherry picked from commit a65f8637cf47eabfc1f2979d9e5835af8a660176) * test: don't check for specific exception title This would be testing other libraries. Not our job. (cherry picked from commit c8eab1ef7514d2f42d652162444853f7c7d08bcb) * test: merge password validation test cases (cherry picked from commit 2cf45366ecbd929058ecb953af76c930b89cba16) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- frappe/core/doctype/user/test_user.py | 9 ++++++++- frappe/core/doctype/user/user.py | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index 7582954175..d4536c6355 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -8,6 +8,7 @@ from unittest.mock import patch import frappe import frappe.exceptions from frappe.core.doctype.user.user import ( + handle_password_test_fail, reset_password, sign_up, test_password_strength, @@ -191,6 +192,12 @@ class TestUser(unittest.TestCase): # Score 1; should now fail result = test_password_strength("bee2ve") self.assertEqual(result["feedback"]["password_policy_validation_passed"], False) + self.assertRaises( + frappe.exceptions.ValidationError, handle_password_test_fail, result["feedback"] + ) + self.assertRaises( + frappe.exceptions.ValidationError, handle_password_test_fail, result + ) # test backwards compatibility # Score 4; should pass result = test_password_strength("Eastern_43A1W") @@ -200,7 +207,7 @@ class TestUser(unittest.TestCase): user = frappe.get_doc("User", "test@example.com") frappe.flags.in_test = False user.new_password = "password" - self.assertRaisesRegex(frappe.exceptions.ValidationError, "Invalid Password", user.save) + self.assertRaises(frappe.exceptions.ValidationError, user.save) user.reload() user.new_password = "Eastern_43A1W" user.save() diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 232e915435..a982403935 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -540,7 +540,7 @@ class User(Document): feedback = result.get("feedback", None) if feedback and not feedback.get("password_policy_validation_passed", False): - handle_password_test_fail(result) + handle_password_test_fail(feedback) def suggest_username(self): def _check_suggestion(suggestion): @@ -686,7 +686,7 @@ def update_password(new_password, logout_all_sessions=0, key=None, old_password= feedback = result.get("feedback", None) if feedback and not feedback.get("password_policy_validation_passed", False): - handle_password_test_fail(result) + handle_password_test_fail(feedback) res = _get_user_for_update_password(key, old_password) if res.get("message"): @@ -1042,13 +1042,15 @@ def notify_admin_access_to_system_manager(login_manager=None): ) -def handle_password_test_fail(result): - suggestions = result["feedback"]["suggestions"][0] if result["feedback"]["suggestions"] else "" - warning = result["feedback"]["warning"] if "warning" in result["feedback"] else "" - suggestions += ( - "
" + _("Hint: Include symbols, numbers and capital letters in the password") + "
" - ) - frappe.throw(" ".join([_("Invalid Password:"), warning, suggestions])) +def handle_password_test_fail(feedback: dict): + # Backward compatibility + if "feedback" in feedback: + feedback = feedback["feedback"] + + suggestions = feedback.get("suggestions", []) + warning = feedback.get("warning", "") + + frappe.throw(msg=" ".join([warning] + suggestions), title=_("Invalid Password")) def update_gravatar(name): From e0efa6fde2faeb571f4dfc669cc2ad951f48dfdf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:48:19 +0530 Subject: [PATCH 16/21] perf: use cached docs for system settings (#17842) (cherry picked from commit d61705f528da80c5ea657d38eb08ea57dd631a2d) Co-authored-by: Ankush Menat --- frappe/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index ec98d1920e..343ea8ced5 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -2284,14 +2284,22 @@ def safe_eval(code, eval_globals=None, eval_locals=None): def get_website_settings(key): if not hasattr(local, "website_settings"): - local.website_settings = db.get_singles_dict("Website Settings", cast=True) + try: + local.website_settings = get_cached_doc("Website Settings") + except DoesNotExistError: + clear_last_message() + return return local.website_settings.get(key) def get_system_settings(key): if not hasattr(local, "system_settings"): - local.system_settings = db.get_singles_dict("System Settings", cast=True) + try: + local.system_settings = get_cached_doc("System Settings") + except DoesNotExistError: # possible during new install + clear_last_message() + return return local.system_settings.get(key) From d3608fb0bb0bde7a28cf431e0bf471c95216bd2f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 20:59:01 +0530 Subject: [PATCH 17/21] fix: incorrect attachment limit message (#17848) (#17849) (cherry picked from commit a01967c182b351f65acfbc4ad0f49dc0dc1ef3f7) Co-authored-by: Ankush Menat --- frappe/public/js/frappe/form/sidebar/attachments.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/sidebar/attachments.js b/frappe/public/js/frappe/form/sidebar/attachments.js index a987d0d334..471b2db2ae 100644 --- a/frappe/public/js/frappe/form/sidebar/attachments.js +++ b/frappe/public/js/frappe/form/sidebar/attachments.js @@ -160,6 +160,12 @@ frappe.ui.form.Attachments = class Attachments { this.dialog.$wrapper.remove(); } + const restrictions = {}; + if (this.frm.meta.max_attachments) { + restrictions.max_number_of_files = + this.frm.meta.max_attachments - this.frm.attachments.get_attachments().length; + } + new frappe.ui.FileUploader({ doctype: this.frm.doctype, docname: this.frm.docname, @@ -168,10 +174,7 @@ frappe.ui.form.Attachments = class Attachments { on_success: (file_doc) => { this.attachment_uploaded(file_doc); }, - restrictions: { - max_number_of_files: - this.frm.meta.max_attachments - this.frm.attachments.get_attachments().length, - }, + restrictions, }); } get_args() { From 3e31275a85afc3cf2bcb006392bc0514a4e7ab0f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 Aug 2022 13:46:57 +0530 Subject: [PATCH 18/21] fix: undefined "self" This is JS not python xD (cherry picked from commit 5062b9b26a5efe04512f909b58a0c5e435cb6b31) --- frappe/public/js/frappe/file_uploader/FileUploader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 9cf30453f4..11b7ef2a5a 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -228,7 +228,7 @@ export default { }); } if (this.restrictions.max_number_of_files == null && this.doctype) { - this.restrictions.max_number_of_files = frappe.get_meta(self.doctype).max_attachments; + this.restrictions.max_number_of_files = frappe.get_meta(this.doctype)?.max_attachments; } }, watch: { From b510037b213cfff8de9574ff836b83caf76171c9 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 Aug 2022 19:30:10 +0530 Subject: [PATCH 19/21] fix(UX): better indicator for "is private" uploads (cherry picked from commit 040a7ba021114ed20b98139d774af2cd0199a962) --- .../js/frappe/file_uploader/FilePreview.vue | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frappe/public/js/frappe/file_uploader/FilePreview.vue b/frappe/public/js/frappe/file_uploader/FilePreview.vue index 5972a975f2..8cce557dcf 100644 --- a/frappe/public/js/frappe/file_uploader/FilePreview.vue +++ b/frappe/public/js/frappe/file_uploader/FilePreview.vue @@ -13,14 +13,8 @@
{{ file.name | file_name }} -
- - {{ file.name | file_name }} - - + {{ file.name | file_name }}
@@ -28,7 +22,11 @@ {{ file.file_obj.size | file_size }}
- + +
+ + +
{{ file.error_message }} @@ -87,9 +85,6 @@ export default { } }, computed: { - private_icon() { - return frappe.utils.icon(this.is_private ? 'lock' : 'unlock'); - }, is_private() { return this.file.doc ? this.file.doc.is_private : this.file.private; }, @@ -206,7 +201,7 @@ export default { opacity: 1; } -.optimize-checkbox { +.frappe-checkbox { font-size: var(--text-sm); color: var(--text-light); display: flex; @@ -214,6 +209,10 @@ export default { padding-top: 0.25rem; } +.config-area { + gap: 0.5rem; +} + .file-error { font-size: var(--text-sm); font-weight: var(--text-bold); From bd69c67f42b3848c44c0eabe8e3181e1de8fac15 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 17 Aug 2022 14:40:40 +0530 Subject: [PATCH 20/21] fix: has_content check for fieldtype TextEditor When a TextEditor field contains only an image, while checking for content, HTML tags are stripped off including the only image. This change adds a loose but explicit check for img tag. (cherry picked from commit d11692c52ff113f1024a7b82f373b07e1ed3fdbe) --- frappe/model/base_document.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 1162ceacd3..5b6caf4f6e 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -671,10 +671,19 @@ class BaseDocument: return _("Error: Value missing for {0}: {1}").format(_(df.parent), _(df.label)) + def has_content(df): + value = cstr(self.get(df.fieldname)) + has_text_content = strip_html(value).strip() + has_img_tag = "