From 745d956678483fe7184c4659583dc71544c30e15 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Thu, 20 Oct 2022 22:32:10 -0600 Subject: [PATCH 01/14] fix: Improve Translation on To Do list view (#18484) --- frappe/desk/doctype/todo/todo_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/todo/todo_list.js b/frappe/desk/doctype/todo/todo_list.js index 2e4534e05c..bf62088f1d 100644 --- a/frappe/desk/doctype/todo/todo_list.js +++ b/frappe/desk/doctype/todo/todo_list.js @@ -17,10 +17,10 @@ frappe.listview_settings["ToDo"] = { return doc.reference_name; }, get_label: function () { - return __("Open"); + return __("Open", null, "Access"); }, get_description: function (doc) { - return __("Open {0}", [`${doc.reference_type} ${doc.reference_name}`]); + return __("Open {0}", [`${__(doc.reference_type)}: ${doc.reference_name}`]); }, action: function (doc) { frappe.set_route("Form", doc.reference_type, doc.reference_name); From 8fd497a5941d856bad3429987cdcc1b7577d9f51 Mon Sep 17 00:00:00 2001 From: Komal-Saraf0609 <81952590+Komal-Saraf0609@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:01:06 +0530 Subject: [PATCH 02/14] fix: map view doesnt open (#18487) Co-authored-by: Komal Saraf --- frappe/public/js/frappe/router.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index c453fcad6e..315adab7d0 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -88,6 +88,7 @@ frappe.router = { "dashboard", "image", "inbox", + "map", ], list_views_route: { list: "List", @@ -100,6 +101,7 @@ frappe.router = { image: "Image", inbox: "Inbox", file: "Home", + map: "Map", }, layout_mapped: {}, From dad080842a034f8e4982e76da89b7c4d2102b33a Mon Sep 17 00:00:00 2001 From: Ritwik Puri Date: Sat, 22 Oct 2022 21:55:12 +0530 Subject: [PATCH 03/14] fix: don't print multiple print statements when database settings dont match with expected settings (#18492) Also, fetch mariadb variables only once when checking mariadb variables --- frappe/database/mariadb/setup_db.py | 35 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/frappe/database/mariadb/setup_db.py b/frappe/database/mariadb/setup_db.py index 392421bc7c..6c68a7692c 100644 --- a/frappe/database/mariadb/setup_db.py +++ b/frappe/database/mariadb/setup_db.py @@ -17,15 +17,16 @@ expected_settings_10_3_later = { } -def get_mariadb_versions(): +def get_mariadb_variables(): + return frappe._dict(frappe.db.sql("show variables")) + + +def get_mariadb_version(version_string: str = ""): # MariaDB classifies their versions as Major (1st and 2nd number), and Minor (3rd number) # Example: Version 10.3.13 is Major Version = 10.3, Minor Version = 13 - mariadb_variables = frappe._dict(frappe.db.sql("""show variables""")) - version_string = mariadb_variables.get("version").split("-")[0] - versions = {} - versions["major"] = version_string.split(".")[0] + "." + version_string.split(".")[1] - versions["minor"] = version_string.split(".")[2] - return versions + version_string = version_string or get_mariadb_variables().get("version") + version = version_string.split("-")[0] + return version.rsplit(".", 1) def setup_database(force, source_sql, verbose, no_mariadb_socket=False): @@ -108,13 +109,13 @@ def import_db_from_sql(source_sql=None, verbose=False): def check_database_settings(): - versions = get_mariadb_versions() - if versions["major"] <= "10.2": + mariadb_variables = get_mariadb_variables() + versions = get_mariadb_version(mariadb_variables.get("version")) + if versions[0] <= "10.2": expected_variables = expected_settings_10_2_earlier else: expected_variables = expected_settings_10_3_later - mariadb_variables = frappe._dict(frappe.db.sql("show variables")) # Check each expected value vs. actuals: result = True for key, expected_value in expected_variables.items(): @@ -124,18 +125,16 @@ def check_database_settings(): % (key, expected_value, mariadb_variables.get(key)) ) result = False + if not result: print( ( - "=" * 80 + "\n" - "Creation of your site - {x} failed because MariaDB is not properly {sep}" + "{sep2}Creation of your site - {site} failed because MariaDB is not properly {sep}" "configured. If using version 10.2.x or earlier, make sure you use the {sep}" - "the Barracuda storage engine. {sep}{sep}" - "Please verify the settings above in MariaDB's my.cnf. Restart MariaDB. And {sep}" - "then run `bench new-site {x}` again.{sep2}" - "" - "=" * 80 - ).format(x=frappe.local.site, sep2="\n" * 2, sep="\n") + "the Barracuda storage engine.{sep2}" + "Please verify the above settings in MariaDB's my.cnf. Restart MariaDB.{sep}" + "And then run `bench new-site {site}` again.{sep2}" + ).format(site=frappe.local.site, sep2="\n\n", sep="\n") ) return result From 3491d0dcb049223d2b5d05e1fdd5c6ff42c85836 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 Oct 2022 11:20:17 +0530 Subject: [PATCH 04/14] refactor: dynamically load web form module (#18486) --- frappe/website/doctype/web_form/web_form.py | 25 +++++++++------------ frappe/www/list.py | 3 ++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index e0ae91fef7..03ba7c0880 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -98,7 +98,6 @@ def get_context(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: context.template = "website/doctype/web_form/templates/web_list.html" @@ -284,13 +283,14 @@ def get_context(context): def add_custom_context_and_script(self, context): """Update context from module if standard and append script""" - if self.web_form_module: - new_context = self.web_form_module.get_context(context) + if self.is_standard: + web_form_module = get_web_form_module(self) + new_context = web_form_module.get_context(context) if new_context: context.update(new_context) - js_path = os.path.join(os.path.dirname(self.web_form_module.__file__), scrub(self.name) + ".js") + js_path = os.path.join(os.path.dirname(web_form_module.__file__), scrub(self.name) + ".js") if os.path.exists(js_path): script = frappe.render_template(open(js_path).read(), context) @@ -300,9 +300,7 @@ def get_context(context): context.script = script - css_path = os.path.join( - os.path.dirname(self.web_form_module.__file__), scrub(self.name) + ".css" - ) + css_path = os.path.join(os.path.dirname(web_form_module.__file__), scrub(self.name) + ".css") if os.path.exists(css_path): style = open(css_path).read() @@ -322,14 +320,6 @@ def get_context(context): return parents - def set_web_form_module(self): - """Get custom web form module if exists""" - self.web_form_module = self.get_web_form_module() - - def get_web_form_module(self): - if self.is_standard: - return get_doc_module(self.module, self.doctype, self.name) - def validate_mandatory(self, doc): """Validate mandatory web form fields""" missing = [] @@ -368,6 +358,11 @@ def get_context(context): return False +def get_web_form_module(doc): + if doc.is_standard: + return get_doc_module(doc.module, doc.doctype, doc.name) + + @frappe.whitelist(allow_guest=True) @rate_limit(key="web_form", limit=5, seconds=60, methods=["POST"]) def accept(web_form, data, docname=None): diff --git a/frappe/www/list.py b/frappe/www/list.py index 06a2ea48aa..9b704d5d44 100644 --- a/frappe/www/list.py +++ b/frappe/www/list.py @@ -163,6 +163,7 @@ def prepare_filters(doctype, controller, kwargs): def get_list_context(context, doctype, web_form_name=None): from frappe.modules import load_doctype_module + from frappe.website.doctype.web_form.web_form import get_web_form_module list_context = context or frappe._dict() meta = frappe.get_meta(doctype) @@ -193,7 +194,7 @@ def get_list_context(context, doctype, web_form_name=None): # get context from web form module if web_form_name: web_form = frappe.get_doc("Web Form", web_form_name) - list_context = update_context_from_module(web_form.get_web_form_module(), list_context) + list_context = update_context_from_module(get_web_form_module(web_form), list_context) # get path from '/templates/' folder of the doctype if not meta.custom and not list_context.row_template: From 8222a605467464c70b414dbda62c15a11adc0b1d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 Oct 2022 15:56:23 +0530 Subject: [PATCH 05/14] chore: bump pillow (#18496) security fixes: https://pillow.readthedocs.io/en/stable/releasenotes/9.2.0.html --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2f5d0cc2b3..e70a38215c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "Click~=7.1.2", "GitPython~=3.1.14", "Jinja2~=3.1.2", - "Pillow~=9.1.1", + "Pillow~=9.2.0", "PyJWT~=2.4.0", "PyMySQL~=1.0.2", "PyPDF2~=2.1.0", From 01fe2da368239a859d478ee0f3dc24758e12aeec Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Sun, 23 Oct 2022 20:07:49 +0100 Subject: [PATCH 06/14] fix(email): generate random filename when attachment filenames are same upon inbound email (#18483) Fix issue when an email has multiple inline images with the same filename. Currently the cid_map is overwritten; instead, attach the files with different names. Emails where every inline image has the filename 'image.png', only one of the images ends up correctly linked. --- frappe/email/receive.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 351c14481d..0cf01083f7 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -604,6 +604,9 @@ class Email: fname = get_random_filename(content_type=content_type) else: fname = get_random_filename(content_type=content_type) + # Don't clobber existing filename + while fname in self.cid_map: + fname = get_random_filename(content_type=content_type) self.attachments.append( { From ce9570de569c0625ab8164e5c59f3c78d3193062 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:30:08 +0530 Subject: [PATCH 07/14] build(deps): bump bruceadams/get-release from 1.2.3 to 1.3.1 (#18535) Bumps [bruceadams/get-release](https://github.com/bruceadams/get-release) from 1.2.3 to 1.3.1. - [Release notes](https://github.com/bruceadams/get-release/releases) - [Commits](https://github.com/bruceadams/get-release/compare/v1.2.3...v1.3.1) --- updated-dependencies: - dependency-name: bruceadams/get-release dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/on_release.yml b/.github/workflows/on_release.yml index 59e14a8c4d..851b5b1d6a 100644 --- a/.github/workflows/on_release.yml +++ b/.github/workflows/on_release.yml @@ -41,7 +41,7 @@ jobs: - name: Get release id: get_release - uses: bruceadams/get-release@v1.2.3 + uses: bruceadams/get-release@v1.3.1 - name: Upload built Assets to Release uses: actions/upload-release-asset@v1.0.2 From 66fbd14c3a330117749878f4c270197ac5557b66 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Tue, 25 Oct 2022 08:36:49 +0200 Subject: [PATCH 08/14] chore: fix DE translations added missing translation [skip ci] --- frappe/translations/de.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index ed7110467b..78fdcb7925 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -2593,6 +2593,7 @@ Tree,Baum, Trigger Method,Trigger-Methode, Trigger Name,Name des Auslösers, "Trigger on valid methods like ""before_insert"", ""after_update"", etc (will depend on the DocType selected)","Trigger auf gültige Methoden wie "before_insert", "after_update" usw. (hängt von der DocType ausgewählt)", +Try a naming Series, Nummernkreis testen, Try to avoid repeated words and characters,"Versuchen Sie, wiederholte Wörter und Zeichen zu vermeiden", Try to use a longer keyboard pattern with more turns,"Versuchen Sie, eine längere Tastaturmuster mit mehr Windungen zu verwenden", Two Factor Authentication,Zwei-Faktor-Authentifizierung, From 5c2b917f524d546056ba362501f286e1afe27468 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 26 Oct 2022 00:15:47 +0530 Subject: [PATCH 09/14] refactor(minor): Tab layout in Website Theme form (#18570) --- .../website/doctype/website_theme/website_theme.js | 1 + .../doctype/website_theme/website_theme.json | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frappe/website/doctype/website_theme/website_theme.js b/frappe/website/doctype/website_theme/website_theme.js index 5c0524f357..c22bee1bc8 100644 --- a/frappe/website/doctype/website_theme/website_theme.js +++ b/frappe/website/doctype/website_theme/website_theme.js @@ -19,6 +19,7 @@ frappe.ui.form.on("Website Theme", { } else { frm.enable_save(); } + frm.set_df_property("custom_scss", "max_lines", 45); }, set_default_theme_button_and_indicator(frm) { diff --git a/frappe/website/doctype/website_theme/website_theme.json b/frappe/website/doctype/website_theme/website_theme.json index ee4b33d854..0faa621f68 100644 --- a/frappe/website/doctype/website_theme/website_theme.json +++ b/frappe/website/doctype/website_theme/website_theme.json @@ -7,10 +7,10 @@ "document_type": "Setup", "engine": "InnoDB", "field_order": [ + "bootstrap_theme_section", "theme", "module", "custom", - "bootstrap_theme_section", "google_font", "font_size", "font_properties", @@ -73,8 +73,8 @@ { "collapsible": 1, "fieldname": "custom_js_section", - "fieldtype": "Section Break", - "label": "Custom JS" + "fieldtype": "Tab Break", + "label": "Script" }, { "fieldname": "js", @@ -84,7 +84,7 @@ }, { "fieldname": "bootstrap_theme_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Theme Configuration" }, { @@ -123,7 +123,7 @@ }, { "fieldname": "stylesheet_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Stylesheet" }, { @@ -181,10 +181,11 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-01-18 17:43:39.804765", + "modified": "2022-10-25 22:15:53.601571", "modified_by": "Administrator", "module": "Website", "name": "Website Theme", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -206,5 +207,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From c5c91bdbd3b3c6f97b324dabf498ef072e021059 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 27 Oct 2022 11:52:47 +0530 Subject: [PATCH 10/14] fix(UX): warn about Etc/* timezones behaviour (#18587) --- frappe/core/doctype/user/user.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index 24f9eb2cea..18e8651819 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -17,6 +17,16 @@ frappe.ui.form.on("User", { } }, + time_zone: function (frm) { + if (frm.doc.time_zone && frm.doc.time_zone.startsWith("Etc")) { + frm.set_df_property( + "time_zone", + "description", + __("Note: Etc timezones have their signs reversed.") + ); + } + }, + role_profile_name: function (frm) { if (frm.doc.role_profile_name) { frappe.call({ @@ -259,6 +269,7 @@ frappe.ui.form.on("User", { } frm.dirty(); } + frm.trigger("time_zone"); }, validate: function (frm) { if (frm.roles_editor) { From 8cdda2e721e9635abb038f3d560c41b83ad19674 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 27 Oct 2022 13:00:50 +0530 Subject: [PATCH 11/14] fix(router-js): handle case when link is not of same host --- frappe/public/js/frappe/router.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 315adab7d0..1149f4a297 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -38,16 +38,17 @@ $("body").on("click", "a", function (e) { return false; }; - const href = e.currentTarget.getAttribute("href"); + const targetElement = e.currentTarget; + const href = targetElement.getAttribute("href"); + const isOfSameHost = targetElement.hostname === window.location.hostname; // click handled, but not by href if ( - e.currentTarget.getAttribute("onclick") || // has a handler + targetElement.getAttribute("onclick") || // has a handler e.ctrlKey || e.metaKey || // open in a new tab - href === "#" + href === "#" // hash is home ) { - // hash is home return; } @@ -57,20 +58,20 @@ $("body").on("click", "a", function (e) { if (href && href.startsWith("#")) { // target startswith "#", this is a v1 style route, so remake it. - return override(e.currentTarget.hash); + return override(targetElement.hash); } - if (frappe.router.is_app_route(e.currentTarget.pathname)) { + if (isOfSameHost && frappe.router.is_app_route(targetElement.pathname)) { // target has "/app, this is a v2 style route. - if (e.currentTarget.search) { + if (targetElement.search) { frappe.route_options = {}; - let params = new URLSearchParams(e.currentTarget.search); + let params = new URLSearchParams(targetElement.search); for (const [key, value] of params) { frappe.route_options[key] = value; } } - return override(e.currentTarget.pathname + e.currentTarget.hash); + return override(targetElement.pathname + targetElement.hash); } }); From 22990ff3434dc6cd38e8bde5b6c139a537b73f9c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Thu, 27 Oct 2022 13:59:08 +0530 Subject: [PATCH 12/14] fix: use `async...await` when parsing route --- frappe/public/js/frappe/recorder/RecorderRoot.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/recorder/RecorderRoot.vue b/frappe/public/js/frappe/recorder/RecorderRoot.vue index 0aa5a42469..7c802ccec1 100644 --- a/frappe/public/js/frappe/recorder/RecorderRoot.vue +++ b/frappe/public/js/frappe/recorder/RecorderRoot.vue @@ -12,8 +12,8 @@ import { useRoute } from "vue-router" let route = useRoute(); -watch(route, () => { - frappe.router.current_route = frappe.router.parse(); +watch(route, async () => { + frappe.router.current_route = await frappe.router.parse(); frappe.breadcrumbs.update(); frappe.recorder.route = route; }); From 3ddac5fe925ade001e67e3517bc1b857f3c13d17 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 27 Oct 2022 16:28:02 +0530 Subject: [PATCH 13/14] refactor: accurate translation caching (#18595) * refactor: rename poorly named functions * refactor: getting translation from apps - use generator - use sane name for cache key - avoid manual handling of frappe.local state just use cache() interface --- frappe/__init__.py | 10 +- .../doctype/translation/test_translation.py | 13 ++- .../core/doctype/translation/translation.py | 5 +- frappe/tests/test_search.py | 1 - frappe/tests/utils.py | 1 - frappe/translate.py | 103 ++++++++++-------- 6 files changed, 71 insertions(+), 62 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index f8e4de34d1..ad07b11caf 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -89,7 +89,7 @@ def _(msg: str, lang: str | None = None, context: str | None = None) -> str: _('Change') _('Change', context='Coins') """ - from frappe.translate import get_full_dict + from frappe.translate import get_all_translations from frappe.utils import is_html, strip_html_tags if not hasattr(local, "lang"): @@ -107,14 +107,15 @@ def _(msg: str, lang: str | None = None, context: str | None = None) -> str: msg = as_unicode(msg).strip() translated_string = "" + + all_translations = get_all_translations(lang) if context: string_key = f"{msg}:{context}" - translated_string = get_full_dict(lang).get(string_key) + translated_string = all_translations.get(string_key) if not translated_string: - translated_string = get_full_dict(lang).get(msg) + translated_string = all_translations.get(msg) - # return lang_full_dict according to lang passed parameter return translated_string or non_translated_string @@ -222,7 +223,6 @@ def init(site: str, sites_path: str = ".", new_site: bool = False) -> None: local.conf = _dict(get_site_config()) local.lang = local.conf.lang or "en" - local.lang_full_dict = None local.module_app = None local.app_modules = None diff --git a/frappe/core/doctype/translation/test_translation.py b/frappe/core/doctype/translation/test_translation.py index ebed447b00..5602fa2c2d 100644 --- a/frappe/core/doctype/translation/test_translation.py +++ b/frappe/core/doctype/translation/test_translation.py @@ -3,6 +3,7 @@ import frappe from frappe import _ from frappe.tests.utils import FrappeTestCase +from frappe.translate import clear_cache class TestTranslation(FrappeTestCase): @@ -11,20 +12,17 @@ class TestTranslation(FrappeTestCase): def tearDown(self): frappe.local.lang = "en" - clear_translation_cache() + clear_cache() def test_doctype(self): translation_data = get_translation_data() for key, val in translation_data.items(): frappe.local.lang = key - clear_translation_cache() translation = create_translation(key, val) self.assertEqual(_(val[0]), val[1]) frappe.delete_doc("Translation", translation.name) - clear_translation_cache() - self.assertEqual(_(val[0]), val[0]) def test_parent_language(self): @@ -55,6 +53,10 @@ class TestTranslation(FrappeTestCase): clear_translation_cache() self.assertTrue(_(data[1][0]), data[1][1]) + def test_multi_language_translations(self): + source = "User" + self.assertNotEqual(_(source, lang="de"), _(source, lang="es")) + def test_html_content_data_translation(self): source = """ str: """Set `frappe.local.lang` from HTTP headers at beginning of request @@ -215,7 +221,7 @@ def get_dict(fortype: str, name: str | None = None) -> dict[str, str]: def get_messages_for_boot(): """Return all message translations that are required on boot.""" - messages = get_full_dict(frappe.local.lang) + messages = get_all_translations(frappe.local.lang) messages.update(get_dict_from_hooks("boot", None)) return messages @@ -241,9 +247,9 @@ def make_dict_from_messages(messages, full_dict=None, load_user_translation=True out = {} if full_dict is None: if load_user_translation: - full_dict = get_full_dict(frappe.local.lang) + full_dict = get_all_translations(frappe.local.lang) else: - full_dict = load_lang(frappe.local.lang) + full_dict = get_translations_from_apps(frappe.local.lang) for m in messages: if m[1] in full_dict: @@ -266,31 +272,29 @@ def get_lang_js(fortype: str, name: str) -> str: return f"\n\n$.extend(frappe._messages, {json.dumps(get_dict(fortype, name))})" -def get_full_dict(lang: str) -> dict[str, str]: - """Load and return the entire translations dictionary for a language from :meth:`frape.cache` +def get_all_translations(lang: str) -> dict[str, str]: + """Load and return the entire translations dictionary for a language from apps + user translations. :param lang: Language Code, e.g. `hi` """ if not lang: return {} - # found in local, return! - if getattr(frappe.local, "lang_full_dict", None) is not None: - return frappe.local.lang_full_dict + def _merge_translations(): + all_translations = get_translations_from_apps(lang).copy() + try: + # get user specific translation data + user_translations = get_user_translations(lang) + all_translations.update(user_translations) + except Exception: + pass - frappe.local.lang_full_dict = load_lang(lang) + return all_translations - try: - # get user specific translation data - user_translations = get_user_translations(lang) - frappe.local.lang_full_dict.update(user_translations) - except Exception: - pass - - return frappe.local.lang_full_dict + return frappe.cache().hget(MERGED_TRANSLATION_KEY, lang, generator=_merge_translations) -def load_lang(lang, apps=None): +def get_translations_from_apps(lang, apps=None): """Combine all translations from `.csv` files in all `apps`. For derivative languages (es-GT), take translations from the base language (es) and then update translations from the child (es-GT)""" @@ -298,22 +302,20 @@ def load_lang(lang, apps=None): if lang == "en": return {} - out = frappe.cache().hget("lang_full_dict", lang, shared=True) - if not out: - out = {} + def _get_from_disk(): + translations = {} for app in apps or frappe.get_all_apps(True): path = os.path.join(frappe.get_pymodule_path(app), "translations", lang + ".csv") - out.update(get_translation_dict_from_file(path, lang, app) or {}) - + translations.update(get_translation_dict_from_file(path, lang, app) or {}) if "-" in lang: parent = lang.split("-")[0] - parent_out = load_lang(parent) - parent_out.update(out) - out = parent_out + parent_translations = get_translations_from_apps(parent) + parent_translations.update(translations) + return parent_translations - frappe.cache().hset("lang_full_dict", lang, out, shared=True) + return translations - return out or {} + return frappe.cache().hget(APP_TRANSLATION_KEY, lang, shared=True, generator=_get_from_disk) def get_translation_dict_from_file(path, lang, app, throw=False) -> dict[str, str]: @@ -342,23 +344,22 @@ def get_translation_dict_from_file(path, lang, app, throw=False) -> dict[str, st def get_user_translations(lang): if not frappe.db: frappe.connect() - out = frappe.cache().hget("lang_user_translations", lang) - if out is None: - out = {} - user_translations = frappe.get_all( + + def _read_from_db(): + user_translations = {} + translations = frappe.get_all( "Translation", fields=["source_text", "translated_text", "context"], filters={"language": lang} ) - for translation in user_translations: - key = translation.source_text - value = translation.translated_text - if translation.context: - key += ":" + translation.context - out[key] = value + for t in translations: + key = t.source_text + value = t.translated_text + if t.context: + key += ":" + t.context + user_translations[key] = value + return user_translations - frappe.cache().hset("lang_user_translations", lang, out) - - return out + return frappe.cache().hget(USER_TRANSLATION_KEY, lang, generator=_read_from_db) def clear_cache(): @@ -368,9 +369,10 @@ def clear_cache(): # clear translations saved in boot cache cache.delete_key("bootinfo") - cache.delete_key("lang_full_dict", shared=True) cache.delete_key("translation_assets", shared=True) - cache.delete_key("lang_user_translations") + cache.delete_key(APP_TRANSLATION_KEY, shared=True) + cache.delete_key(USER_TRANSLATION_KEY) + cache.delete_key(MERGED_TRANSLATION_KEY) def get_messages_for_app(app, deduplicate=True): @@ -1050,7 +1052,7 @@ def get_untranslated(lang, untranslated_file, get_all=False, app="_ALL_APPS"): # replace \n with ||| so that internal linebreaks don't get split f.write((escape_newlines(m[1]) + os.linesep).encode("utf-8")) else: - full_dict = get_full_dict(lang) + full_dict = get_all_translations(lang) for m in messages: if not full_dict.get(m[1]): @@ -1073,7 +1075,7 @@ def update_translations(lang, untranslated_file, translated_file, app="_ALL_APPS :param untranslated_file: File path with the messages in English. :param translated_file: File path with messages in language to be updated.""" clear_cache() - full_dict = get_full_dict(lang) + full_dict = get_all_translations(lang) def restore_newlines(s): return ( @@ -1110,7 +1112,7 @@ def update_translations(lang, untranslated_file, translated_file, app="_ALL_APPS def import_translations(lang, path): """Import translations from file in standard format""" clear_cache() - full_dict = get_full_dict(lang) + full_dict = get_all_translations(lang) full_dict.update(get_translation_dict_from_file(path, lang, "import")) for app in frappe.get_all_apps(True): @@ -1140,7 +1142,9 @@ def write_translations_file(app, lang, full_dict=None, app_messages=None): tpath = frappe.get_pymodule_path(app, "translations") frappe.create_folder(tpath) - write_csv_file(os.path.join(tpath, lang + ".csv"), app_messages, full_dict or get_full_dict(lang)) + write_csv_file( + os.path.join(tpath, lang + ".csv"), app_messages, full_dict or get_all_translations(lang) + ) def send_translations(translation_dict): @@ -1302,3 +1306,8 @@ def get_translated_doctypes(): "Property Setter", {"property": "translated_doctype", "value": "1"}, pluck="doc_type" ) return unique(dts + custom_dts) + + +# Backward compatibility +get_full_dict = get_all_translations +load_lang = get_translations_from_apps From ccbd6ffab371d01dbb1ef09b2634b801a2b322bd Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 27 Oct 2022 16:30:28 +0530 Subject: [PATCH 14/14] fix(formatting): use snake case for variable names --- frappe/public/js/frappe/router.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 1149f4a297..2005a46cfe 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -38,13 +38,13 @@ $("body").on("click", "a", function (e) { return false; }; - const targetElement = e.currentTarget; - const href = targetElement.getAttribute("href"); - const isOfSameHost = targetElement.hostname === window.location.hostname; + const target_element = e.currentTarget; + const href = target_element.getAttribute("href"); + const is_on_same_host = target_element.hostname === window.location.hostname; // click handled, but not by href if ( - targetElement.getAttribute("onclick") || // has a handler + target_element.getAttribute("onclick") || // has a handler e.ctrlKey || e.metaKey || // open in a new tab href === "#" // hash is home @@ -58,20 +58,20 @@ $("body").on("click", "a", function (e) { if (href && href.startsWith("#")) { // target startswith "#", this is a v1 style route, so remake it. - return override(targetElement.hash); + return override(target_element.hash); } - if (isOfSameHost && frappe.router.is_app_route(targetElement.pathname)) { + if (is_on_same_host && frappe.router.is_app_route(target_element.pathname)) { // target has "/app, this is a v2 style route. - if (targetElement.search) { + if (target_element.search) { frappe.route_options = {}; - let params = new URLSearchParams(targetElement.search); + let params = new URLSearchParams(target_element.search); for (const [key, value] of params) { frappe.route_options[key] = value; } } - return override(targetElement.pathname + targetElement.hash); + return override(target_element.pathname + target_element.hash); } });