From d7026b8a262e4e2360313ff7d123db938fd97c02 Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:40:12 +0100 Subject: [PATCH 01/95] fix(meta)!: Allow level 0 fields when a doc has been shared with user --- frappe/model/meta.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 0e80a0957c..99e6d2f740 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -595,6 +595,10 @@ class Meta(Document): self.get_permlevel_access(permission_type=permission_type, parenttype=parenttype, user=user) ) + if 0 not in permlevel_access and permission_type in ("read", "select"): + if frappe.share.get_shared(self.name, user, rights=[permission_type], limit=1): + permlevel_access.add(0) + permitted_fieldnames.extend( df.fieldname for df in self.get_fieldnames_with_value( From 06bd8b9e9c436697d98324cf1c30ed13608dce4b Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:06:54 +0100 Subject: [PATCH 02/95] test: Add test for get_list of shared document --- frappe/core/doctype/docshare/test_docshare.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frappe/core/doctype/docshare/test_docshare.py b/frappe/core/doctype/docshare/test_docshare.py index 125e829d9b..3b6ec0c396 100644 --- a/frappe/core/doctype/docshare/test_docshare.py +++ b/frappe/core/doctype/docshare/test_docshare.py @@ -56,6 +56,24 @@ class TestDocShare(FrappeTestCase): with self.assertRowsRead(1): self.assertTrue(self.event.has_permission()) + def test_list_permission(self): + frappe.set_user(self.user) + with self.assertRaises(frappe.PermissionError): + frappe.get_list("Web Page") + + frappe.set_user("Administrator") + doc = frappe.new_doc("Web Page") + doc.update({"title": "test document for docshare permissions"}) + doc.insert() + frappe.share.add("Web Page", doc.name, self.user) + + frappe.set_user(self.user) + self.assertEqual(len(frappe.get_list("Web Page")), 1) + + doc.delete(ignore_permissions=True) + with self.assertRaises(frappe.PermissionError): + frappe.get_list("Web Page") + def test_share_permission(self): frappe.share.add("Event", self.event.name, self.user, write=1, share=1) From fc0ba518945102b836794fc539ea6bd42af60cc1 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 Dec 2023 16:15:56 +0100 Subject: [PATCH 03/95] fix: Disappearing letterhead header in pdf - fix: Make sure 'header-html' is extracted from document after it is rendered as html and loaded in wkhtmltopdf options, else it goes missing --- frappe/utils/pdf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 24b6896620..b7a8557b48 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -227,7 +227,7 @@ def read_options_from_html(html): return str(soup), options -def prepare_header_footer(soup): +def prepare_header_footer(soup: BeautifulSoup): options = {} head = soup.find("head").contents @@ -240,10 +240,6 @@ def prepare_header_footer(soup): for html_id in ("header-html", "footer-html"): content = soup.find(id=html_id) if content: - # there could be multiple instances of header-html/footer-html - for tag in soup.find_all(id=html_id): - tag.extract() - toggle_visible_pdf(content) id_map = {"header-html": "pdf_header_html", "footer-html": "pdf_footer_html"} hook_func = frappe.get_hooks(id_map.get(html_id)) @@ -256,6 +252,10 @@ def prepare_header_footer(soup): css=css, ) + # there could be multiple instances of header-html/footer-html + for tag in soup.find_all(id=html_id): + tag.extract() + # create temp file fname = os.path.join("/tmp", f"frappe-pdf-{frappe.generate_hash()}.html") with open(fname, "wb") as f: From 29e761671dc73edcc3b0a9745d825b8851a34e34 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 Dec 2023 17:25:19 +0100 Subject: [PATCH 04/95] feat: Letterhead scripts - feat: Allow script injection into header/footer.html to allow manipulation of styles using page numbers/args received by wkhtmltopdf - misc: also validate letterhead scripts - Include scripts in printview as well as pdf - Add helper instructions & re-arrange fields --- frappe/__init__.py | 6 ++- .../doctype/letter_head/letter_head.js | 51 +++++++++++++++++++ .../doctype/letter_head/letter_head.json | 35 ++++++++++++- .../print_formats/pdf_header_footer.html | 16 +++++- frappe/utils/pdf.py | 26 ++++++---- frappe/www/printview.html | 16 +++--- frappe/www/printview.py | 33 ++++++++++-- 7 files changed, 157 insertions(+), 26 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 3d31adbcfa..7c41c21b51 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -2116,7 +2116,9 @@ def get_print( pdf_options["password"] = password html = get_response_content("printview") - return get_pdf(html, options=pdf_options, output=output) if as_pdf else html + return ( + get_pdf(html, options=pdf_options, output=output, letterhead=letterhead) if as_pdf else html + ) def attach_print( @@ -2155,7 +2157,7 @@ def attach_print( ext = ".pdf" kwargs["as_pdf"] = True content = ( - get_pdf(html, options={"password": password} if password else None) + get_pdf(html, options={"password": password} if password else None, letterhead=letterhead) if html else get_print(doctype, name, **kwargs) ) diff --git a/frappe/printing/doctype/letter_head/letter_head.js b/frappe/printing/doctype/letter_head/letter_head.js index 55d97cf37f..a1ef08bce9 100644 --- a/frappe/printing/doctype/letter_head/letter_head.js +++ b/frappe/printing/doctype/letter_head/letter_head.js @@ -2,7 +2,58 @@ // For license information, please see license.txt frappe.ui.form.on("Letter Head", { + setup(frm) { + frm.get_field("instructions").html(INSTRUCTIONS); + }, + refresh: function (frm) { frm.flag_public_attachments = true; }, + + validate: (frm) => { + ["header_script", "footer_script"].forEach((field) => { + if (!frm.doc[field]) return; + + try { + eval(frm.doc[field]); + } catch (e) { + frappe.throw({ + title: __("Error in Header/Footer Script"), + indicator: "orange", + message: '
' + e.stack + "
", + }); + } + }); + }, }); + +const INSTRUCTIONS = `

Letter Head Scripts

+

Header/Footer scripts can be used to add dynamic behaviours.

+
+
+// The following Header Script will add the current date to an element in 'Header HTML' with class "header-content"
+var el = document.getElementsByClassName("header-content");
+if (el.length > 0) {
+	el[0].textContent += " " + new Date().toGMTString();
+}
+
+
+

You can also access wkhtmltopdf variables (valid only in PDF print):

+
+
+// Get Header and Footer wkhtmltopdf variables
+// Snippet and more variables: https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
+var vars = {};
+var query_strings_from_url = document.location.search.substring(1).split('&');
+for (var query_string in query_strings_from_url) {
+	if (query_strings_from_url.hasOwnProperty(query_string)) {
+		var temp_var = query_strings_from_url[query_string].split('=', 2);
+		vars[temp_var[0]] = decodeURI(temp_var[1]);
+	}
+}
+var el = document.getElementsByClassName("header-content");
+if (el.length > 0 && vars["page"] == 1) {
+	el[0].textContent += " : " + vars["date"];
+}
+
+
`; diff --git a/frappe/printing/doctype/letter_head/letter_head.json b/frappe/printing/doctype/letter_head/letter_head.json index 021f79ca93..4ffca134f2 100644 --- a/frappe/printing/doctype/letter_head/letter_head.json +++ b/frappe/printing/doctype/letter_head/letter_head.json @@ -26,7 +26,11 @@ "footer_image", "footer_image_height", "footer_image_width", - "footer_align" + "footer_align", + "scripts_section", + "header_script", + "footer_script", + "instructions" ], "fields": [ { @@ -162,13 +166,40 @@ "fieldtype": "Select", "label": "Footer Based On", "options": "Image\nHTML" + }, + { + "depends_on": "eval:!doc.__islocal && doc.source==='HTML'", + "fieldname": "header_script", + "fieldtype": "Code", + "label": "Header Script", + "options": "Javascript" + }, + { + "depends_on": "eval:!doc.__islocal && doc.footer_source==='HTML'", + "fieldname": "footer_script", + "fieldtype": "Code", + "label": "Footer Script", + "options": "Javascript" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.header_script || doc.footer_script", + "fieldname": "scripts_section", + "fieldtype": "Section Break", + "label": "Scripts" + }, + { + "fieldname": "instructions", + "fieldtype": "HTML", + "label": "Instructions", + "read_only": 1 } ], "icon": "fa fa-font", "idx": 1, "links": [], "max_attachments": 3, - "modified": "2023-12-08 15:52:37.525003", + "modified": "2023-12-21 16:19:37.525003", "modified_by": "Administrator", "module": "Printing", "name": "Letter Head", diff --git a/frappe/templates/print_formats/pdf_header_footer.html b/frappe/templates/print_formats/pdf_header_footer.html index 189cf43cd9..91973efb70 100644 --- a/frappe/templates/print_formats/pdf_header_footer.html +++ b/frappe/templates/print_formats/pdf_header_footer.html @@ -56,14 +56,28 @@ } } } + + function custom_load() { + if (window.custom_header_footer_script) { + window.custom_header_footer_script(); + } + } + {% if custom_header_footer_script -%} + + {%- endif %} + {% for tag in styles -%} {{ tag | string }} {%- endfor %} - + " #. Description of the 'Footer HTML' (HTML Editor) field in DocType 'Letter #. Head' @@ -12644,14 +12790,16 @@ msgstr "Zum Beispiel: {} Öffnen" msgctxt "Customize Form Field" msgid "For Links, enter the DocType as range.\n" "For Select, enter list of Options, each on a new line." -msgstr "" +msgstr "Für Links geben Sie den DocType als Bereich ein.\n" +"Für Auswahl geben Sie eine Liste von Optionen ein, jede in einer neuen Zeile." #. Description of the 'Options' (Small Text) field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "For Links, enter the DocType as range.\n" "For Select, enter list of Options, each on a new line." -msgstr "" +msgstr "Für Links geben Sie den DocType als Bereich ein.\n" +"Für Auswahl geben Sie eine Liste von Optionen ein, jede in einer neuen Zeile." #: core/doctype/user_permission/user_permission_list.js:10 #: core/doctype/user_permission/user_permission_list.js:148 @@ -12719,7 +12867,7 @@ msgstr "Weitere Informationen erhalten Sie bei {0}." #: email/doctype/auto_email_report/auto_email_report.json msgctxt "Auto Email Report" msgid "For multiple addresses, enter the address on different line. e.g. test@test.com ⏎ test1@test.com" -msgstr "" +msgstr "Bei mehreren Adressen geben Sie bitte jede Adresse in einer neuen Zeile ein. z.B. test@test.com ⏎ test1@test.com" #: core/doctype/data_export/exporter.py:199 msgid "For updating, you can update only selective columns." @@ -12746,13 +12894,13 @@ msgstr "Erzwingen" #: custom/doctype/customize_form/customize_form.json msgctxt "Customize Form" msgid "Force Re-route to Default View" -msgstr "" +msgstr "Umleitung zur Standardansicht erzwingen" #. Label of a Check field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Force Re-route to Default View" -msgstr "" +msgstr "Umleitung zur Standardansicht erzwingen" #. Label of a Check field in DocType 'Desktop Icon' #: desk/doctype/desktop_icon/desktop_icon.json @@ -12762,7 +12910,7 @@ msgstr "Anzeige erzwingen" #: core/doctype/rq_job/rq_job.js:13 msgid "Force Stop job" -msgstr "" +msgstr "Job stoppen erzwingen" #. Label of a Int field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json @@ -12774,7 +12922,7 @@ msgstr "Benutzer zum Zurücksetzen des Kennworts zwingen" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Force Web Capture Mode for Uploads" -msgstr "" +msgstr "Web-Capture-Modus für Uploads erzwingen" #: www/login.html:35 msgid "Forgot Password?" @@ -12818,13 +12966,13 @@ msgstr "Formular" #: custom/doctype/customize_form/customize_form.json msgctxt "Customize Form" msgid "Form Builder" -msgstr "" +msgstr "Formular-Generator" #. Label of a HTML field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Form Builder" -msgstr "" +msgstr "Formular-Generator" #. Label of a Code field in DocType 'Recorder' #: core/doctype/recorder/recorder.json @@ -12933,7 +13081,7 @@ msgstr "Frappe-Framework" #: public/js/frappe/ui/theme_switcher.js:59 msgid "Frappe Light" -msgstr "" +msgstr "Frappe Hell" #. Label of a standard help item #. Type: Action @@ -13057,7 +13205,7 @@ msgstr "Vom Benutzer" #: public/js/frappe/utils/diffview.js:30 msgid "From version" -msgstr "" +msgstr "Von Version" #. Option for the 'Width' (Select) field in DocType 'Dashboard Chart Link' #: desk/doctype/dashboard_chart_link/dashboard_chart_link.json @@ -13126,7 +13274,7 @@ msgstr "Funktion basiert auf" #: __init__.py:835 msgid "Function {0} is not whitelisted." -msgstr "" +msgstr "Funktion {0} ist nicht freigegeben." #: public/js/frappe/views/treeview.js:402 msgid "Further nodes can be only created under 'Group' type nodes" @@ -13241,7 +13389,7 @@ msgstr "Alarme für heute" #: desk/page/backups/backups.js:19 msgid "Get Backup Encryption Key" -msgstr "" +msgstr "Backup-Verschlüsselungsschlüssel abrufen" #. Label of a Button field in DocType 'Auto Repeat' #: automation/doctype/auto_repeat/auto_repeat.json @@ -13259,7 +13407,7 @@ msgstr "Artikel aufrufen" #: integrations/doctype/connected_app/connected_app.js:6 msgid "Get OpenID Configuration" -msgstr "" +msgstr "OpenID-Konfiguration abrufen" #: www/printview.html:22 msgid "Get PDF" @@ -13270,14 +13418,14 @@ msgstr "PDF herunterladen" #: core/doctype/document_naming_settings/document_naming_settings.json msgctxt "Document Naming Settings" msgid "Get a preview of generated names with a series." -msgstr "" +msgstr "Erhalten Sie eine Vorschau der generierten Namen eines Nummernkreises." #. Description of the 'Email Threads on Assigned Document' (Check) field in #. DocType 'Notification Settings' #: desk/doctype/notification_settings/notification_settings.json msgctxt "Notification Settings" msgid "Get notified when an email is received on any of the documents assigned to you." -msgstr "" +msgstr "Benachrichtigen, wenn eine E-Mail auf einem der Ihnen zugewiesenen Dokumente empfangen wird." #. Description of the 'User Image' (Attach Image) field in DocType 'User' #: core/doctype/user/user.json @@ -13289,7 +13437,7 @@ msgstr "Allgemein wiedererkennbaren Avatar von Gravatar.com abrufen" #: core/doctype/installed_application/installed_application.json msgctxt "Installed Application" msgid "Git Branch" -msgstr "" +msgstr "Git-Zweig" #. Option for the 'Social Login Provider' (Select) field in DocType 'Social #. Login Key' @@ -13371,7 +13519,7 @@ msgstr "Gehe zum Dokument" #: website/doctype/web_form/web_form.json msgctxt "Web Form" msgid "Go to this URL after completing the form" -msgstr "" +msgstr "Nach dem Ausfüllen des Formulars diese URL aufrufen" #: core/doctype/doctype/doctype.js:54 #: custom/doctype/client_script/client_script.js:10 @@ -13435,7 +13583,7 @@ msgstr "Google-Kalender" #: integrations/doctype/google_calendar/google_calendar.py:781 msgid "Google Calendar - Contact / email not found. Did not add attendee for -
{0}" -msgstr "" +msgstr "Google Kalender - Kontakt / E-Mail nicht gefunden. Teilnehmer wurde nicht hinzugefügt für -
{0}" #: integrations/doctype/google_calendar/google_calendar.py:251 msgid "Google Calendar - Could not create Calendar for {0}, error code {1}." @@ -13696,11 +13844,11 @@ msgstr "Gruppen-Knoten" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "Group Object Class" -msgstr "" +msgstr "Gruppenobjektklasse" #: public/js/frappe/ui/group_by/group_by.js:415 msgid "Grouped by {0}" -msgstr "" +msgstr "Gruppiert nach {0}" #. Option for the 'Method' (Select) field in DocType 'Recorder' #: core/doctype/recorder/recorder.json @@ -13865,7 +14013,7 @@ msgstr "Hat Domain" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Has Next Condition" -msgstr "" +msgstr "Hat nächste Bedingung" #. Name of a DocType #: core/doctype/has_role/has_role.json @@ -13932,13 +14080,13 @@ msgstr "Header, Robots" #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Headers" -msgstr "" +msgstr "Headers" #. Label of a Code field in DocType 'Webhook Request Log' #: integrations/doctype/webhook_request_log/webhook_request_log.json msgctxt "Webhook Request Log" msgid "Headers" -msgstr "" +msgstr "Headers" #: printing/page/print_format_builder/print_format_builder.js:602 msgid "Heading" @@ -14048,7 +14196,7 @@ msgstr "Hilfe zur Suche" #: desk/doctype/note/note.json msgctxt "Note" msgid "Help: To link to another record in the system, use \"/app/note/[Note Name]\" as the Link URL. (don't use \"http://\")" -msgstr "" +msgstr "Hilfe: Um einen Link zu einem anderen Datensatz im System zu erstellen, verwenden Sie \"/app/note/[Name der Notiz]\" als Link URL. (ohne \"http://\" und Domain)" #. Label of a Int field in DocType 'Help Article' #: website/doctype/help_article/help_article.json @@ -14222,13 +14370,13 @@ msgstr "Tage verstecken" #: core/doctype/user_permission/user_permission_list.js:96 msgid "Hide Descendants" -msgstr "" +msgstr "Nachkommen ausblenden" #. Label of a Check field in DocType 'User Permission' #: core/doctype/user_permission/user_permission.json msgctxt "User Permission" msgid "Hide Descendants" -msgstr "" +msgstr "Nachkommen ausblenden" #: www/error.html:41 www/error.html:56 msgid "Hide Error" @@ -14249,7 +14397,7 @@ msgstr "Vorschau verbergen" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Hide Previous, Next and Close button on highlight dialog." -msgstr "" +msgstr "Die Schaltflächen Zurück, Weiter und Schließen ausblenden." #: public/js/frappe/list/list_filter.js:87 msgid "Hide Saved" @@ -14277,7 +14425,7 @@ msgstr "Sekunden ausblenden" #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Hide Sidebar, Menu, and Comments" -msgstr "" +msgstr "Seitenleiste, Menü und Kommentare ausblenden" #. Label of a Check field in DocType 'Portal Settings' #: website/doctype/portal_settings/portal_settings.json @@ -14302,7 +14450,7 @@ msgstr "Arbeitsbereich ausblenden" #: core/doctype/user_permission/user_permission.json msgctxt "User Permission" msgid "Hide descendant records of For Value." -msgstr "" +msgstr "Untergeordnete Datensätze von Für Wert ausblenden." #: public/js/frappe/form/layout.js:260 msgid "Hide details" @@ -14466,7 +14614,7 @@ msgstr "ID (Name) der Einheit, deren Eigenschaft festgelegt werden muss" #: website/doctype/web_page_block/web_page_block.json msgctxt "Web Page Block" msgid "IDs must contain only alphanumeric characters, not contain spaces, and should be unique." -msgstr "" +msgstr "IDs dürfen nur alphanumerische Zeichen enthalten, keine Leerzeichen und sollten eindeutig sein." #. Label of a Section Break field in DocType 'Email Account' #: email/doctype/email_account/email_account.json @@ -14585,7 +14733,7 @@ msgstr "Identitätsdetails" #: desk/doctype/desktop_icon/desktop_icon.json msgctxt "Desktop Icon" msgid "Idx" -msgstr "" +msgstr "Pos" #. Description of the 'Apply Strict User Permissions' (Check) field in DocType #. 'System Settings' @@ -14623,7 +14771,7 @@ msgstr "Wenn aktiviert, werden alle anderen Workflows inaktiv." #: printing/doctype/print_format/print_format.json msgctxt "Print Format" msgid "If checked, negative numeric values of Currency, Quantity or Count would be shown as positive" -msgstr "" +msgstr "Wenn aktiviert, werden negative numerische Werte von Währung, Menge oder Anzahl als positiv angezeigt" #. Description of the 'Skip Authorization' (Check) field in DocType 'OAuth #. Client' @@ -14733,13 +14881,13 @@ msgstr "Wenn nicht gesetzt, hängt die Währungspräzision vom Zahlenformat ab" #: desk/doctype/dashboard_chart/dashboard_chart.json msgctxt "Dashboard Chart" msgid "If set, only user with these roles can access this chart. If not set, DocType or Report permissions will be used." -msgstr "" +msgstr "Wenn diese Option gesetzt ist, können nur Benutzer mit diesen Rollen auf dieses Diagramm zugreifen. Wenn nicht festgelegt, werden die Berechtigungen für den zugehörigen DocType oder Report verwendet." #. Description of the 'Condition' (Code) field in DocType 'Energy Point Rule' #: social/doctype/energy_point_rule/energy_point_rule.json msgctxt "Energy Point Rule" msgid "If the condition is satisfied user will be rewarded with the points. eg. doc.status == 'Closed'\n" -msgstr "" +msgstr "Wenn die Bedingung erfüllt ist, wird der Benutzer mit den Punkten belohnt. z.B. doc.status == 'Closed'\n" #. Description of the 'User Type' (Link) field in DocType 'User' #: core/doctype/user/user.json @@ -14752,21 +14900,21 @@ msgstr "Wenn der Benutzer eine Rolle geprüft hat, wird er ein \"System User\". #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "If unchecked, the value will always be re-fetched on save." -msgstr "" +msgstr "Wenn diese Option deaktiviert ist, wird der Wert beim Speichern immer erneut abgerufen." #. Description of the 'Fetch on Save if Empty' (Check) field in DocType #. 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json msgctxt "Customize Form Field" msgid "If unchecked, the value will always be re-fetched on save." -msgstr "" +msgstr "Wenn diese Option deaktiviert ist, wird der Wert beim Speichern immer erneut abgerufen." #. Description of the 'Fetch on Save if Empty' (Check) field in DocType #. 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "If unchecked, the value will always be re-fetched on save." -msgstr "" +msgstr "Wenn diese Option deaktiviert ist, wird der Wert beim Speichern immer erneut abgerufen." #. Label of a Check field in DocType 'Custom DocPerm' #: core/doctype/custom_docperm/custom_docperm.json @@ -14794,11 +14942,11 @@ msgstr "Wenn neue Datensätze hochgeladen werden, bitte die Spalte \"Bezeichnung #: utils/password.py:200 msgid "If you have recently restored the site you may need to copy the site config contaning original Encryption Key." -msgstr "" +msgstr "Falls Sie diese Instanz kürzlich wiederhergestellt haben, müssen Sie möglicherweise noch den Verschlüsselungsschlüssel des vorherigen Systems in die Site Config übernehmen." #: core/doctype/doctype/doctype.js:80 msgid "If you just want to customize for your site, use {0} instead." -msgstr "" +msgstr "Falls Sie nur für Ihre Instanz anpassen möchten, verwenden Sie stattdessen {0}." #. Description of the 'Parent Label' (Select) field in DocType 'Top Bar Item' #: website/doctype/top_bar_item/top_bar_item.json @@ -14997,7 +15145,7 @@ msgstr "Bilder" #: core/doctype/log_settings/log_settings.py:56 msgid "Implement `clear_old_logs` method to enable auto error clearing." -msgstr "" +msgstr "Implementieren Sie die Methode `clear_old_logs`, um die automatische Fehlerbereinigung zu aktivieren." #. Option for the 'Grant Type' (Select) field in DocType 'OAuth Client' #: integrations/doctype/oauth_client/oauth_client.json @@ -15137,7 +15285,7 @@ msgstr "In Tagen" #. Description of the Onboarding Step 'Setup Limited Access for a User' #: custom/onboarding_step/role_permissions/role_permissions.json msgid "In ERPNext, you can add your Employees as Users, and give them restricted access. Tools like Role Permission and User Permission allow you to define rules which give restricted access to the user to masters and transactions." -msgstr "" +msgstr "In ERPNext können Sie Ihre Mitarbeiter als Benutzer hinzufügen und ihnen eingeschränkten Zugriff gewähren. Mit Werkzeugen wie Rollenberechtigung und Benutzerberechtigung können Sie Regeln definieren, die dem Benutzer einen eingeschränkten Zugang zu Stammdaten und Transaktionen ermöglichen." #. Label of a Check field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json @@ -15248,7 +15396,7 @@ msgstr "Im Standard-Filter" #. Description of the Onboarding Step 'Generate Custom Reports' #: custom/onboarding_step/report_builder/report_builder.json msgid "In each module, you will find a host of single-click reports, ranging from financial statements to sales and purchase analytics and stock tracking reports. If a required new report is not available out-of-the-box, you can create custom reports in ERPNext by pulling values from the same multiple ERPNext tables.\n" -msgstr "" +msgstr "In jedem Modul finden Sie eine Vielzahl von Berichten, die Sie per Mausklick erstellen können – von Finanzberichten über Verkaufs- und Einkaufsanalysen bis hin zu Berichten zur Bestandsverfolgung. Wenn ein benötigter Bericht noch nicht angeboten wird, können Sie in ERPNext benutzerdefinierte Berichte erstellen, in denen Sie Werte aus denselben vielzähligen ERPNext-Tabellen abrufen.\n" #. Description of the 'Font Size' (Float) field in DocType 'Print Settings' #: printing/doctype/print_settings/print_settings.json @@ -15311,7 +15459,7 @@ msgstr "Dokument Web View Link per E-Mail senden" #: public/js/frappe/views/reports/query_report.js:1488 msgid "Include filters" -msgstr "" +msgstr "Filter einbeziehen" #: public/js/frappe/views/reports/query_report.js:1480 msgid "Include indentation" @@ -15325,25 +15473,25 @@ msgstr "Geben Sie Symbole, Zahlen und Großbuchstaben in das Passwort ein" #: email/doctype/email_account/email_account.json msgctxt "Email Account" msgid "Incoming (POP/IMAP) Settings" -msgstr "" +msgstr "Eingehende (POP/IMAP) Einstellungen" #. Label of a Data field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" msgid "Incoming Server" -msgstr "" +msgstr "Eingehender Server" #. Label of a Data field in DocType 'Email Domain' #: email/doctype/email_domain/email_domain.json msgctxt "Email Domain" msgid "Incoming Server" -msgstr "" +msgstr "Eingehender Server" #. Label of a Section Break field in DocType 'Email Domain' #: email/doctype/email_domain/email_domain.json msgctxt "Email Domain" msgid "Incoming Settings" -msgstr "" +msgstr "Eingehende Einstellungen" #: email/doctype/email_domain/email_domain.py:32 msgid "Incoming email account not correct" @@ -15351,7 +15499,7 @@ msgstr "Falsches Konto für eingehende E-Mails" #: model/virtual_doctype.py:81 model/virtual_doctype.py:94 msgid "Incomplete Virtual Doctype Implementation" -msgstr "" +msgstr "Unvollständige Implementierung des virtuellen DocTypes" #: auth.py:236 msgid "Incomplete login details" @@ -15385,25 +15533,25 @@ msgstr "Falscher Wert: {0} muss {1} {2} sein" #: public/js/frappe/model/model.js:114 #: public/js/frappe/views/reports/report_view.js:941 msgid "Index" -msgstr "" +msgstr "Pos" #. Label of a Check field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "Index" -msgstr "" +msgstr "Pos" #. Label of a Check field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Index" -msgstr "" +msgstr "Pos" #. Label of a Int field in DocType 'Recorder Query' #: core/doctype/recorder_query/recorder_query.json msgctxt "Recorder Query" msgid "Index" -msgstr "" +msgstr "Pos" #. Label of a Check field in DocType 'DocType' #: core/doctype/doctype/doctype.json @@ -15520,7 +15668,7 @@ msgstr "Stil einfügen" #: public/js/frappe/ui/toolbar/search_utils.js:646 #: public/js/frappe/ui/toolbar/search_utils.js:647 msgid "Install {0} from Marketplace" -msgstr "" +msgstr "{0} aus Marketplace installieren" #. Name of a DocType #: core/doctype/installed_application/installed_application.json @@ -15560,7 +15708,7 @@ msgstr "Unzureichende Berechtigungen um Bericht zu bearbeiten" #: core/doctype/doctype/doctype.py:447 msgid "Insufficient attachment limit" -msgstr "" +msgstr "Unzureichende Begrenzung für Anhänge" #. Option for the 'Field Type' (Select) field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json @@ -15717,7 +15865,7 @@ msgstr "Ungültiger "abhängiger_on" -Ausdruck im Filter {0}" #: public/js/frappe/form/save.js:206 msgid "Invalid \"mandatory_depends_on\" expression" -msgstr "" +msgstr "Ungültiger Ausdruck in Bedingung für Pflichtfeld" #: utils/nestedset.py:181 msgid "Invalid Action" @@ -15793,7 +15941,7 @@ msgstr "Ungültige Option" #: email/smtp.py:102 msgid "Invalid Outgoing Mail Server or Port: {0}" -msgstr "" +msgstr "Ungültiger Postausgang Server oder Port: {0}" #: email/doctype/auto_email_report/auto_email_report.py:182 msgid "Invalid Output Format" @@ -15840,7 +15988,7 @@ msgstr "Ungültiger Benutzername oder fehlendes Passwort. Bitte Angaben korrigie #: integrations/doctype/webhook/webhook.py:116 msgid "Invalid Webhook Secret" -msgstr "" +msgstr "Ungültiges Webhook Geheimnis" #: desk/reportview.py:150 msgid "Invalid aggregate function" @@ -15889,7 +16037,7 @@ msgstr "Ungültiger JSON in den benutzerdefinierten Optionen hinzugefügt: {0}" #: model/naming.py:445 msgid "Invalid name type (integer) for varchar name column" -msgstr "" +msgstr "Ungültiger Namenstyp (Ganzzahl) für die Varchar-Namensspalte" #: model/naming.py:52 msgid "Invalid naming series {}: dot (.) missing" @@ -15901,11 +16049,11 @@ msgstr "Ungültiger oder beschädigter Inhalt für den Import" #: website/doctype/website_settings/website_settings.py:139 msgid "Invalid redirect regex in row #{}: {}" -msgstr "" +msgstr "Ungültige Weiterleitungs-Regex in Zeile #{}: {}" #: app.py:301 msgid "Invalid request arguments" -msgstr "" +msgstr "Ungültige Anfrageargumente" #: integrations/doctype/connected_app/connected_app.py:172 msgid "Invalid state." @@ -16413,49 +16561,49 @@ msgstr "Jinja" #: core/doctype/prepared_report/prepared_report.json msgctxt "Prepared Report" msgid "Job ID" -msgstr "" +msgstr "Job-ID" #. Label of a Data field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "Job ID" -msgstr "" +msgstr "Job-ID" #. Label of a Link field in DocType 'Submission Queue' #: core/doctype/submission_queue/submission_queue.json msgctxt "Submission Queue" msgid "Job Id" -msgstr "" +msgstr "Job Id" #. Label of a Section Break field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "Job Info" -msgstr "" +msgstr "Jobinfo" #. Label of a Data field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "Job Name" -msgstr "" +msgstr "Jobname" #. Label of a Section Break field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "Job Status" -msgstr "" +msgstr "Jobstatus" #: core/doctype/rq_job/rq_job.js:24 msgid "Job Stopped Successfully" -msgstr "" +msgstr "Job erfolgreich beendet" #: core/doctype/rq_job/rq_job.py:122 msgid "Job is not running." -msgstr "" +msgstr "Job läuft nicht." #: desk/doctype/event/event.js:51 msgid "Join video conference with {0}" -msgstr "" +msgstr "Videokonferenz mit {0} beitreten" #: public/js/frappe/form/toolbar.js:355 public/js/frappe/form/toolbar.js:757 msgid "Jump to field" @@ -16562,7 +16710,7 @@ msgstr "Tastatürkürzel" #: public/js/frappe/utils/number_systems.js:37 msgctxt "Number system" msgid "Kh" -msgstr "" +msgstr "Kh" #. Label of a Card Break in the Website Workspace #: website/doctype/help_article/help_article.py:80 @@ -16584,13 +16732,13 @@ msgstr "Wissensdatenbank Bearbeiter/-in" #: public/js/frappe/utils/number_systems.js:49 msgctxt "Number system" msgid "L" -msgstr "" +msgstr "L" #. Label of a Section Break field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "LDAP Auth" -msgstr "" +msgstr "LDAP-Authentifizierung" #. Label of a Section Break field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -16638,7 +16786,7 @@ msgstr "LDAP-Gruppenzuordnungen" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "LDAP Group Member attribute" -msgstr "" +msgstr "LDAP-Gruppenmitgliedsattribut" #. Label of a Data field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -16656,7 +16804,7 @@ msgstr "LDAP Middle Name-Feld" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "LDAP Mobile Field" -msgstr "" +msgstr "LDAP-Mobilfunkfeld" #: integrations/doctype/ldap_settings/ldap_settings.py:160 msgid "LDAP Not Installed" @@ -16676,13 +16824,13 @@ msgstr "LDAP Suchstring" #: integrations/doctype/ldap_settings/ldap_settings.py:127 msgid "LDAP Search String must be enclosed in '()' and needs to contian the user placeholder {0}, eg sAMAccountName={0}" -msgstr "" +msgstr "Der LDAP-Suchstring muss in '()' eingeschlossen sein und den Benutzerplatzhalter {0} enthalten, z.B. sAMAccountName={0}" #. Label of a Section Break field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "LDAP Search and Paths" -msgstr "" +msgstr "LDAP-Suche und Pfade" #. Label of a Section Break field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -17072,11 +17220,11 @@ msgstr "Zuletzt synchronisiert {0}" #: custom/doctype/customize_form/customize_form.js:186 msgid "Layout Reset" -msgstr "" +msgstr "Layout zurücksetzen" #: custom/doctype/customize_form/customize_form.js:178 msgid "Layout will be reset to standard layout, are you sure you want to do this?" -msgstr "" +msgstr "Das Layout wird auf das Standardlayout zurückgesetzt. Sind Sie sicher, dass Sie dies tun möchten?" #: desk/page/leaderboard/leaderboard.js:15 msgid "Leaderboard" @@ -17085,7 +17233,7 @@ msgstr "Bestenliste" #. Label of an action in the Onboarding Step 'Customize Print Formats' #: custom/onboarding_step/print_format/print_format.json msgid "Learn about Standard and Custom Print Formats" -msgstr "" +msgstr "Erfahren Sie mehr über Standard- und benutzerdefinierte Druckformate" #. Title of an Onboarding Step #: website/onboarding_step/web_page_tour/web_page_tour.json @@ -17104,7 +17252,7 @@ msgstr "Mehr erfahren" #. Label of an action in the Onboarding Step 'Generate Custom Reports' #: custom/onboarding_step/report_builder/report_builder.json msgid "Learn more about Report Builders" -msgstr "" +msgstr "Erfahren Sie mehr über Berichterstellungswerkzeuge" #. Label of an action in the Onboarding Step 'Custom Document Types' #: custom/onboarding_step/custom_doctype/custom_doctype.json @@ -17596,17 +17744,17 @@ msgstr "Link zu" #: desk/doctype/workspace_link/workspace_link.json msgctxt "Workspace Link" msgid "Link Type" -msgstr "" +msgstr "Linktyp" #: website/doctype/about_us_settings/about_us_settings.js:6 msgid "Link for About Us Page is \"/about\"." -msgstr "" +msgstr "Der Link zur Seite „Über uns“ lautet „/about“." #. Description of the 'Home Page' (Data) field in DocType 'Website Settings' #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" msgid "Link that is the website home page. Standard Links (home, login, products, blog, about, contact)" -msgstr "" +msgstr "Pfad, der als Startseite verwendet werden soll. Standardpfade: home, login, products, blog, about, contact" #. Description of the 'URL' (Data) field in DocType 'Top Bar Item' #: website/doctype/top_bar_item/top_bar_item.json @@ -17694,13 +17842,13 @@ msgstr "Liste" #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "List / Search Settings" -msgstr "" +msgstr "Listen-/Sucheinstellungen" #. Label of a Table field in DocType 'Web Form' #: website/doctype/web_form/web_form.json msgctxt "Web Form" msgid "List Columns" -msgstr "" +msgstr "Listenspalten" #. Name of a DocType #: desk/doctype/list_filter/list_filter.json @@ -17759,7 +17907,7 @@ msgstr "Liste als [{ \"label\": _ ( \"Jobs\"), \"route\": \"jobs\"}]" #: public/js/frappe/ui/toolbar/search_utils.js:526 msgid "Lists" -msgstr "" +msgstr "Listen" #. Option for the 'Rule' (Select) field in DocType 'Assignment Rule' #: automation/doctype/assignment_rule/assignment_rule.json @@ -17825,7 +17973,7 @@ msgstr "Anmelden bei {0}" #: core/doctype/data_import_log/data_import_log.json msgctxt "Data Import Log" msgid "Log Index" -msgstr "" +msgstr "Protokollindex" #. Name of a DocType #: core/doctype/log_setting_user/log_setting_user.json @@ -17928,7 +18076,7 @@ msgstr "Anmelden und im Browser anzeigen" #: website/doctype/web_form/web_form.js:358 msgid "Login is required to see web form list view. Enable {0} to see list settings" -msgstr "" +msgstr "Um die Listenansicht des Webformulars zu sehen, ist eine Anmeldung erforderlich. Aktivieren Sie {0}, um die Listeneinstellungen zu sehen" #: auth.py:322 auth.py:325 msgid "Login not allowed at this time" @@ -17968,7 +18116,7 @@ msgstr "Mit E-Mail-Link anmelden" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Login with email link expiry (in minutes)" -msgstr "" +msgstr "Gültigkeitsdauer des E-Mail-Links (in Minuten)" #: auth.py:131 msgid "Login with username and password is not allowed." @@ -18048,7 +18196,7 @@ msgstr "Sieht so aus, als hätten Sie den Wert nicht geändert" #: www/third_party_apps.html:57 msgid "Looks like you haven’t added any third party apps." -msgstr "" +msgstr "Wie es aussieht haben Sie keine Drittanbieter-Apps hinzugefügt." #: public/js/frappe/form/sidebar/assign_to.js:190 msgid "Low" @@ -18128,7 +18276,7 @@ msgstr " Anhänge im Standard als öffentlich markieren" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Make sure to configure a Social Login Key before disabling to prevent lockout" -msgstr "" +msgstr "Stellen Sie sicher, dass Sie vor der Deaktivierung einen Social Login Key konfigurieren, um eine Sperre zu verhindern" #: utils/password_strength.py:94 msgid "Make use of longer keyboard patterns" @@ -18148,7 +18296,7 @@ msgstr "Drittanbieter-Anwendungen verwalten" #: www/me.html:59 msgid "Manage your apps" -msgstr "" +msgstr "Verwalten Sie Ihre Apps" #. Label of a Check field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json @@ -18184,25 +18332,25 @@ msgstr "Zwingend notwendig" #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "Mandatory Depends On" -msgstr "Obligatorisch Hängt von ab" +msgstr "Bedingung für Pflichtfeld" #. Label of a Code field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json msgctxt "Customize Form Field" msgid "Mandatory Depends On" -msgstr "Obligatorisch Hängt von ab" +msgstr "Bedingung für Pflichtfeld" #. Label of a Code field in DocType 'Web Form Field' #: website/doctype/web_form_field/web_form_field.json msgctxt "Web Form Field" msgid "Mandatory Depends On" -msgstr "Obligatorisch Hängt von ab" +msgstr "Bedingung für Pflichtfeld" #. Label of a Code field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Mandatory Depends On (JS)" -msgstr "" +msgstr "Bedingung für Pflichtfeld (JS)" #: website/doctype/web_form/web_form.py:412 msgid "Mandatory Information missing:" @@ -18227,7 +18375,7 @@ msgstr "Für {0} benötigte Pflichtfelder:" #: public/js/frappe/web_form/web_form.js:234 msgctxt "Error message in web form" msgid "Mandatory fields required:" -msgstr "" +msgstr "Pflichtfelder erforderlich:" #: core/doctype/data_export/exporter.py:142 msgid "Mandatory:" @@ -18407,7 +18555,7 @@ msgstr "Maximaler Wert" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Max auto email report per user" -msgstr "" +msgstr "Höchstzahl automatischer E-Mail-Berichte pro Benutzer" #: core/doctype/doctype/doctype.py:1293 msgid "Max width for type Currency is 100px in row {0}" @@ -18421,7 +18569,7 @@ msgstr "Maximal" #: core/doctype/file/file.py:317 msgid "Maximum Attachment Limit of {0} has been reached for {1} {2}." -msgstr "" +msgstr "Die Höchstgrenze für Anhänge von {0} wurde für {1} {2} erreicht." #. Label of a Select field in DocType 'List View Settings' #: desk/doctype/list_view_settings/list_view_settings.json @@ -18437,7 +18585,7 @@ msgstr "Maximale Punkte" #: public/js/frappe/form/sidebar/attachments.js:38 msgid "Maximum attachment limit of {0} has been reached." -msgstr "" +msgstr "Die Höchstgrenze für Anhänge von {0} wurde erreicht." #. Description of the 'Maximum Points' (Int) field in DocType 'Energy Point #. Rule' @@ -18445,7 +18593,8 @@ msgstr "" msgctxt "Energy Point Rule" msgid "Maximum points allowed after multiplying points with the multiplier value\n" "(Note: For no limit leave this field empty or set 0)" -msgstr "" +msgstr "Maximal erlaubte Punkte nach Multiplikation mit dem Multiplikatorwert\n" +"(Hinweis: Für kein Limit lassen Sie dieses Feld leer oder setzen Sie es auf 0)" #: model/rename_doc.py:675 msgid "Maximum {0} rows allowed" @@ -18489,7 +18638,7 @@ msgstr "Treffen" #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Meets Condition?" -msgstr "" +msgstr "Bedingungen erfüllen?" #. Group in Email Group's connections #: email/doctype/email_group/email_group.json @@ -18670,7 +18819,7 @@ msgstr "Nachrichten ID" #: core/doctype/data_import_log/data_import_log.json msgctxt "Data Import Log" msgid "Messages" -msgstr "" +msgstr "Nachrichten" #. Label of a Section Break field in DocType 'Web Form' #: website/doctype/web_form/web_form.json @@ -18868,7 +19017,7 @@ msgstr "Nicht ausgefüllte Felder" #: email/doctype/auto_email_report/auto_email_report.py:123 msgid "Missing Filters Required" -msgstr "" +msgstr "Fehlende Filter erforderlich" #: desk/form/assign_to.py:105 msgid "Missing Permission" @@ -18910,7 +19059,7 @@ msgstr "Mobilfunknummer" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Modal Trigger" -msgstr "" +msgstr "Modal-Auslöser" #. Label of a Card Break in the Build Workspace #: core/workspace/build/build.json @@ -19085,7 +19234,7 @@ msgstr "Modul-Def" #: core/doctype/module_profile/module_profile.json msgctxt "Module Profile" msgid "Module HTML" -msgstr "" +msgstr "Modul HTML" #. Label of a Data field in DocType 'Desktop Icon' #: desk/doctype/desktop_icon/desktop_icon.json @@ -19135,7 +19284,7 @@ msgstr "Modulprofil Name" #: desk/doctype/module_onboarding/module_onboarding.py:68 msgid "Module onboarding progress reset" -msgstr "" +msgstr "Modul Onboarding-Fortschritt zurücksetzen" #: custom/doctype/customize_form/customize_form.js:208 msgid "Module to Export" @@ -19327,7 +19476,7 @@ msgstr "Am Meisten verwendet" #: utils/password.py:65 msgid "Most probably your password is too long." -msgstr "" +msgstr "Wahrscheinlich ist Ihr Passwort zu lang." #: core/doctype/communication/communication.js:86 #: core/doctype/communication/communication.js:194 @@ -19345,19 +19494,19 @@ msgstr "In den Papierkorb verschieben" #: public/js/frappe/form/form.js:176 msgid "Move cursor to above row" -msgstr "" +msgstr "Cursor zur Zeile darüber bewegen" #: public/js/frappe/form/form.js:180 msgid "Move cursor to below row" -msgstr "" +msgstr "Cursor zur Zeile darunter bewegen" #: public/js/frappe/form/form.js:184 msgid "Move cursor to next column" -msgstr "" +msgstr "Cursor zur nächsten Spalte bewegen" #: public/js/frappe/form/form.js:188 msgid "Move cursor to previous column" -msgstr "" +msgstr "Cursor zur vorherigen Spalte bewegen" #: public/js/frappe/form/grid_row.js:165 msgid "Move to Row Number" @@ -19367,14 +19516,14 @@ msgstr "Gehe zu Zeilennummer" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Move to next step when clicked inside highlighted area." -msgstr "" +msgstr "Zum nächsten Schritt bewegen, wenn innerhalb des markierten Bereichs geklickt wird." #. Description of the 'Parent Element Selector' (Data) field in DocType 'Form #. Tour Step' #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Mozilla doesn't support :has() so you can pass parent selector here as workaround" -msgstr "" +msgstr "Mozilla unterstützt :has() nicht, daher können Sie hier den übergeordneten Selektor als Workaround übergeben" #: utils/nestedset.py:334 msgid "Multiple root nodes not allowed." @@ -19398,7 +19547,7 @@ msgstr "Muss eine öffentlich zugängliche Google Sheets-URL sein" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "Must be enclosed in '()' and include '{0}', which is a placeholder for the user/login name. i.e. (&(objectclass=user)(uid={0}))" -msgstr "" +msgstr "Muss in '()' eingeschlossen sein und '{0}' enthalten, was ein Platzhalter für den Benutzer-/Loginnamen ist. d.h. (&(objectclass=user)(uid={0}))" #. Description of the 'Image Field' (Data) field in DocType 'Customize Form' #: custom/doctype/customize_form/customize_form.json @@ -19453,14 +19602,14 @@ msgstr "MyISAM" #: workflow/doctype/workflow/workflow.js:19 msgid "NOTE: If you add states or transitions in the table, it will be reflected in the Workflow Builder but you will have to position them manually. Also Workflow Builder is currently in BETA." -msgstr "" +msgstr "HINWEIS: Wenn Sie in der Tabelle Zustände oder Übergänge hinzufügen, wird dies im Workflow Builder berücksichtigt, aber Sie müssen sie manuell positionieren. Außerdem befindet sich Workflow Builder derzeit noch in der BETA-Phase." #. Description of the 'LDAP Group Field' (Data) field in DocType 'LDAP #. Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "NOTE: This box is due for depreciation. Please re-setup LDAP to work with the newer settings" -msgstr "" +msgstr "HINWEIS: Dieses Feld ist veraltet. Bitte richten Sie LDAP neu ein, um mit den neueren Einstellungen zu arbeiten" #: public/js/frappe/form/layout.js:75 #: public/js/frappe/form/multi_select_dialog.js:239 @@ -19468,31 +19617,31 @@ msgstr "" #: public/js/frappe/views/file/file_view.js:97 #: website/doctype/website_slideshow/website_slideshow.js:25 msgid "Name" -msgstr "" +msgstr "Name" #. Label of a Data field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json msgctxt "Customize Form Field" msgid "Name" -msgstr "" +msgstr "Name" #. Label of a Data field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Name" -msgstr "" +msgstr "Name" #. Label of a Data field in DocType 'Slack Webhook URL' #: integrations/doctype/slack_webhook_url/slack_webhook_url.json msgctxt "Slack Webhook URL" msgid "Name" -msgstr "" +msgstr "Name" #. Label of a Data field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" msgid "Name" -msgstr "" +msgstr "Name" #: desk/utils.py:22 msgid "Name already taken, please set a new name" @@ -19542,7 +19691,9 @@ msgctxt "DocType" msgid "Naming Options:\n" "
  1. field:[fieldname] - By Field
  2. autoincrement - Uses Databases' Auto Increment feature
  3. naming_series: - By Naming Series (field called naming_series must be present)
  4. Prompt - Prompt user for a name
  5. [series] - Series by prefix (separated by a dot); for example PRE.#####
  6. \n" "
  7. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Replace all braced words (fieldnames, date words (DD, MM, YY), series) with their value. Outside braces, any characters can be used.
" -msgstr "" +msgstr "Benennungsoptionen:\n" +"
  1. field:[fieldname] - Nach Feld
  2. autoincrement - Verwendet die Funktion Auto Increment der Datenbanken
  3. naming_series: - Nach Nummernkreis (ein Feld namens naming_series muss vorhanden sein)
  4. Prompt - Fragt den Benutzer nach einem Namen
  5. [series] - Zähler nach Präfix (durch einen Punkt getrennt); zum Beispiel PRE.#####
  6. \n" +"
  7. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Ersetzt alle Wörter in geschweiften Klammern (Feldnamen, Datumskürzel (DD, MM, YY), Zähler) durch ihren Wert. Außerhalb der geschweiften Klammern können beliebige Zeichen verwendet werden.
" #. Description of the 'Auto Name' (Data) field in DocType 'Customize Form' #: custom/doctype/customize_form/customize_form.json @@ -19550,7 +19701,9 @@ msgctxt "Customize Form" msgid "Naming Options:\n" "
  1. field:[fieldname] - By Field
  2. naming_series: - By Naming Series (field called naming_series must be present)
  3. Prompt - Prompt user for a name
  4. [series] - Series by prefix (separated by a dot); for example PRE.#####
  5. \n" "
  6. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Replace all braced words (fieldnames, date words (DD, MM, YY), series) with their value. Outside braces, any characters can be used.
" -msgstr "" +msgstr "Benennungsoptionen:\n" +"
  1. field:[fieldname] - Nach Feld
  2. naming_series: - Nach Nummernkreis (ein Feld namens naming_series muss vorhanden sein)
  3. Prompt - Aufforderung zur Eingabe eines Namens
  4. [series] - Zähler nach Präfix (durch einen Punkt getrennt); zum Beispiel PRE.#####
  5. \n" +"
  6. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Ersetzt alle Wörter in geschweiften Klammern (Feldnamen, Datumskürzel (DD, MM, YY), Zähler) durch ihren Wert. Außerhalb der geschweiften Klammern können beliebige Zeichen verwendet werden.
" #. Label of a Select field in DocType 'Customize Form' #: custom/doctype/customize_form/customize_form.json @@ -19642,11 +19795,11 @@ msgstr "Navigationseinstellungen" #: desk/doctype/workspace/workspace.py:297 msgid "Need Workspace Manager role to edit private workspace of other users" -msgstr "" +msgstr "Sie benötigen die Rolle des Workspace Managers, um den privaten Arbeitsbereich anderer Benutzer zu bearbeiten" #: desk/doctype/workspace/workspace.py:343 msgid "Need Workspace Manager role to hide/unhide public workspaces" -msgstr "" +msgstr "Benötigen Sie die Rolle des Workspace Managers, um öffentliche Arbeitsbereiche ein- und auszublenden" #: model/document.py:607 msgid "Negative Value" @@ -19704,7 +19857,7 @@ msgstr "Neues benutzerdefiniertes Druckformat" #: desk/doctype/form_tour/form_tour.json msgctxt "Form Tour" msgid "New Document Form" -msgstr "" +msgstr "Neues Dokumentenformular" #: desk/doctype/notification_log/notification_log.py:158 msgid "New Document Shared {0}" @@ -19792,7 +19945,7 @@ msgstr "Neue Updates sind verfügbar" #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" msgid "New users will have to be manually registered by system managers." -msgstr "" +msgstr "Neue Benutzer müssen von den Systemmanagern manuell angelegt werden." #. Description of the 'Set Value' (Small Text) field in DocType 'Property #. Setter' @@ -19836,7 +19989,7 @@ msgstr "Neue {} Versionen für die folgenden Apps sind verfügbar" #: core/doctype/user/user.py:764 msgid "Newly created user {0} has no roles enabled." -msgstr "" +msgstr "Der neu erstellte Benutzer {0} hat keine aktivierten Rollen." #. Label of a Card Break in the Tools Workspace #. Name of a DocType @@ -19919,7 +20072,7 @@ msgstr "Nächste Ausführung" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Next Form Tour" -msgstr "" +msgstr "Nächste Formular-Tour" #. Label of a Date field in DocType 'Auto Repeat' #: automation/doctype/auto_repeat/auto_repeat.json @@ -19937,7 +20090,7 @@ msgstr "Nächster Status" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Next Step Condition" -msgstr "" +msgstr "Bedingung für den nächsten Schritt" #. Label of a Password field in DocType 'Google Calendar' #: integrations/doctype/google_calendar/google_calendar.json @@ -19955,7 +20108,7 @@ msgstr "Nächstes Sync-Token" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Next on Click" -msgstr "" +msgstr "Weiter bei Klick" #: integrations/doctype/webhook/webhook.py:137 #: public/js/form_builder/utils.js:341 @@ -20511,7 +20664,7 @@ msgstr "Hinweis: Wenn Sie den Seitennamen ändern, wird die vorherige URL auf di #: core/doctype/user/user.js:25 msgid "Note: Etc timezones have their signs reversed." -msgstr "" +msgstr "Hinweis: Bei \"Etc\"-Zeitzonen sind die Vorzeichen umgekehrt." #. Description of the 'sb0' (Section Break) field in DocType 'Website #. Slideshow' @@ -20529,7 +20682,7 @@ msgstr "Hinweis: Mehrere Sitzungen wird im Falle einer mobilen Gerät erlaubt se #: website/web_form/request_to_delete_data/request_to_delete_data.js:8 msgid "Note: Your request for account deletion will be fulfilled within {0} hours." -msgstr "" +msgstr "Hinweis: Ihr Antrag auf Kontolöschung wird innerhalb von {0} Stunden bearbeitet." #: core/doctype/data_export/exporter.py:183 msgid "Notes:" @@ -20757,7 +20910,7 @@ msgstr "Anzahl der Abfragen" #: core/doctype/doctype/doctype.py:444 public/js/frappe/doctype/index.js:59 msgid "Number of attachment fields are more than {}, limit updated to {}." -msgstr "" +msgstr "Anzahl der Anhangsfelder ist größer als {}, Limit auf {} aktualisiert." #: core/doctype/system_settings/system_settings.py:152 msgid "Number of backups must be greater than zero." @@ -20870,7 +21023,7 @@ msgstr "Name des OTP-Emittenten" #: twofactor.py:468 msgid "OTP Secret Reset - {0}" -msgstr "" +msgstr "OTP Geheimnis zurücksetzen - {0}" #: twofactor.py:487 msgid "OTP Secret has been reset. Re-registration will be required on next login." @@ -20972,11 +21125,11 @@ msgstr "Onboarding-Schritt" #. Name of a DocType #: desk/doctype/onboarding_step_map/onboarding_step_map.json msgid "Onboarding Step Map" -msgstr "" +msgstr "Onboarding-Schritt Zuordnung" #: public/js/frappe/widgets/onboarding_widget.js:269 msgid "Onboarding complete" -msgstr "" +msgstr "Onboarding abgeschlossen" #: core/doctype/doctype/doctype_list.js:28 msgid "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended." @@ -21002,7 +21155,7 @@ msgstr "Einer von" #: public/js/frappe/views/workspace/workspace.js:1312 msgid "One of the child page with name {0} already exist in {1} Section. Please update the name of the child page first before moving" -msgstr "" +msgstr "Eine untergeordnete Seiten mit dem Namen {0} existiert bereits im Bereich {1}. Bitte aktualisieren Sie zuerst den Namen der untergeordneten Seite, bevor Sie sie verschieben." #: client.py:213 msgid "Only 200 inserts allowed in one request" @@ -21042,22 +21195,22 @@ msgstr "Nur Send Records aktualisiert in den letzten X Stunden" #: desk/doctype/workspace/workspace.js:36 msgid "Only Workspace Manager can edit public workspaces" -msgstr "" +msgstr "Nur der Workspace Manager kann öffentliche Arbeitsbereiche bearbeiten" #: public/js/frappe/views/workspace/workspace.js:536 msgid "Only Workspace Manager can sort or edit this page" -msgstr "" +msgstr "Nur der Workspace-Manager kann diese Seite sortieren oder bearbeiten" #: modules/utils.py:66 msgid "Only allowed to export customizations in developer mode" -msgstr "" +msgstr "Anpassungen können nur im Entwicklermodus exportiert werden" #. Description of the 'Endpoint URL' (Data) field in DocType 'S3 Backup #. Settings' #: integrations/doctype/s3_backup_settings/s3_backup_settings.json msgctxt "S3 Backup Settings" msgid "Only change this if you want to use other S3 compatible object storage backends." -msgstr "" +msgstr "Ändern Sie dies nur, wenn Sie andere S3-kompatible Objektspeicher-Backends verwenden möchten." #. Label of a Link field in DocType 'Workspace Link' #: desk/doctype/workspace_link/workspace_link.json @@ -21076,11 +21229,11 @@ msgstr "Es kann nur eine {0} als primäre festgelegt werden." #: desk/reportview.py:319 msgid "Only reports of type Report Builder can be deleted" -msgstr "" +msgstr "Nur Berichte aus dem Berichterstellungswerkzeug können gelöscht werden" #: desk/reportview.py:290 msgid "Only reports of type Report Builder can be edited" -msgstr "" +msgstr "Nur Berichte aus dem Berichterstellungswerkzeug können bearbeitet werden" #: custom/doctype/customize_form/customize_form.py:124 msgid "Only standard DocTypes are allowed to be customized from Customize Form." @@ -21096,7 +21249,7 @@ msgstr "Es werden nur Benutzer aufgelistet, die an dem Dokument beteiligt sind" #: email/doctype/auto_email_report/auto_email_report.py:100 msgid "Only {0} emailed reports are allowed per user." -msgstr "" +msgstr "Pro Benutzer sind nur {0} per E-Mail versandte Berichte erlaubt." #: core/doctype/deleted_document/deleted_document.js:7 msgid "Open" @@ -21207,7 +21360,7 @@ msgstr "{0} öffnen" #: integrations/doctype/connected_app/connected_app.json msgctxt "Connected App" msgid "OpenID Configuration" -msgstr "" +msgstr "OpenID-Konfiguration" #. Option for the 'Directory Server' (Select) field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -21321,7 +21474,7 @@ msgstr "Hilfe zu Optionen" #: core/doctype/doctype/doctype.py:1601 msgid "Options for Rating field can range from 3 to 10" -msgstr "" +msgstr "Optionen für Bewertungsfeld können zwischen 3 und 10 liegen" #: custom/doctype/custom_field/custom_field.js:96 msgid "Options for select. Each option on a new line." @@ -21333,7 +21486,7 @@ msgstr "Optionen für {0} müssen festgelegt werden, bevor der Standardwert fest #: public/js/form_builder/store.js:177 msgid "Options is required for field {0} of type {1}" -msgstr "" +msgstr "Optionen sind erforderlich für Feld {0} des Typs {1}" #: model/base_document.py:767 msgid "Options not set for link field {0}" @@ -21635,7 +21788,7 @@ msgstr "Seitenumbruch" #: website/doctype/web_page/web_page.json msgctxt "Web Page" msgid "Page Builder" -msgstr "" +msgstr "Seitengenerator" #. Label of a Table field in DocType 'Web Page' #: website/doctype/web_page/web_page.json @@ -21760,7 +21913,7 @@ msgstr "Übergeordneter DocType wird benötigt, um Zahlenkarte zu erstellen" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Parent Element Selector" -msgstr "" +msgstr "Selektor für übergeordnetes Element" #. Label of a Select field in DocType 'Form Tour Step' #: desk/doctype/form_tour_step/form_tour_step.json @@ -21804,7 +21957,7 @@ msgstr "Übergeordnete Tabelle" #: desk/doctype/dashboard_chart/dashboard_chart.py:404 msgid "Parent document type is required to create a dashboard chart" -msgstr "" +msgstr "Übergeordneter Dokumententyp wird benötigt, um ein Dashboard-Diagramm zu erstellen" #: core/doctype/data_export/exporter.py:255 msgid "Parent is the name of the document to which the data will get added to." @@ -21812,7 +21965,7 @@ msgstr "Parent ist der Name des Dokuments, zu dem die Daten hinzugefügt werden. #: permissions.py:806 msgid "Parentfield not specified in {0}: {1}" -msgstr "" +msgstr "Das übergeordnete Feld wurde in {0}: {1} nicht angegeben" #: client.py:476 msgid "Parenttype, Parent and Parentfield are required to insert a child record" @@ -21986,7 +22139,7 @@ msgstr "Korrektur-Protokoll" #: modules/patch_handler.py:137 msgid "Patch type {} not found in patches.txt" -msgstr "" +msgstr "Patch-Typ {} nicht in patches.txt gefunden" #: website/report/website_analytics/website_analytics.js:35 msgid "Path" @@ -22038,7 +22191,7 @@ msgstr "Pfad zur privaten Schlüsseldatei" #: core/doctype/data_import/data_import.json msgctxt "Data Import" msgid "Payload Count" -msgstr "" +msgstr "Nutzlast Anzahl" #. Option for the 'Status' (Select) field in DocType 'Data Import' #: core/doctype/data_import/data_import.json @@ -22406,11 +22559,11 @@ msgstr "Bitte zuerst eine Datei anhängen." #: printing/doctype/letter_head/letter_head.py:73 msgid "Please attach an image file to set HTML for Footer." -msgstr "" +msgstr "Bitte fügen Sie eine Bilddatei an, um HTML für die Fußzeile festzulegen." #: printing/doctype/letter_head/letter_head.py:61 msgid "Please attach an image file to set HTML for Letter Head." -msgstr "" +msgstr "Bitte fügen Sie eine Bilddatei an, um HTML für den Briefkopf festzulegen." #: core/doctype/package_import/package_import.py:38 msgid "Please attach the package" @@ -22418,7 +22571,7 @@ msgstr "Bitte das Paket anhängen" #: integrations/doctype/connected_app/connected_app.js:19 msgid "Please check OpenID Configuration URL" -msgstr "" +msgstr "Bitte überprüfen Sie die OpenID-Konfigurations-URL" #: utils/dashboard.py:58 msgid "Please check the filter values set for Dashboard Chart: {}" @@ -22442,7 +22595,7 @@ msgstr "Bitte überprüfen Sie Ihren Posteingang auf weitere Instruktionen.\\nLa #: twofactor.py:291 msgid "Please click on the following link and follow the instructions on the page. {0}" -msgstr "" +msgstr "Bitte klicken Sie auf den folgenden Link und folgen Sie den Anweisungen auf der Seite. {0}" #: templates/emails/password_reset.html:2 msgid "Please click on the following link to set your new password" @@ -22458,7 +22611,7 @@ msgstr "Bitte bestätigen Sie Ihre Aktion für {0} dieses Dokument." #: core/doctype/server_script/server_script.js:33 msgid "Please contact your system administrator to enable this feature." -msgstr "" +msgstr "Bitte kontaktieren Sie Ihren Systemadministrator, um diese Funktion zu aktivieren." #: desk/doctype/number_card/number_card.js:44 msgid "Please create Card first" @@ -22470,7 +22623,7 @@ msgstr "Bitte erstellen Sie zuerst ein Diagramm" #: desk/form/meta.py:209 msgid "Please delete the field from {0} or add the required doctype." -msgstr "" +msgstr "Bitte löschen Sie das Feld von {0} oder fügen Sie den erforderlichen DocType hinzu." #: core/doctype/data_export/exporter.py:184 msgid "Please do not change the template headings." @@ -22482,7 +22635,7 @@ msgstr "Bitte kopieren um Änderungen vorzunehmen" #: core/doctype/system_settings/system_settings.py:145 msgid "Please enable atleast one Social Login Key or LDAP or Login With Email Link before disabling username/password based login." -msgstr "" +msgstr "Bitte aktivieren Sie mindestens eines der Anmeldeverfahren Social Login Key, LDAP oder Anmeldung per E-Mail-Link, bevor Sie die Anmeldung per Benutzernamen und Passwort deaktivieren." #: desk/doctype/notification_log/notification_log.js:45 #: email/doctype/auto_email_report/auto_email_report.js:17 @@ -22543,7 +22696,7 @@ msgstr "Bitte Passwort eingeben" #: public/js/frappe/desk.js:196 msgctxt "Email Account" msgid "Please enter the password for: {0}" -msgstr "" +msgstr "Bitte geben Sie das Passwort ein für: {0}" #: core/doctype/sms_settings/sms_settings.py:42 msgid "Please enter valid mobile nos" @@ -22623,7 +22776,7 @@ msgstr "Bitte wählen Sie Minimum Password Score" #: utils/__init__.py:115 msgid "Please select a country code for field {1}." -msgstr "" +msgstr "Bitte wählen Sie einen Ländercode für das Feld {1} aus." #: utils/file_manager.py:50 msgid "Please select a file or url" @@ -22666,7 +22819,7 @@ msgstr "Bitte {0} auswählen" #: integrations/doctype/dropbox_settings/dropbox_settings.py:306 msgid "Please set Dropbox access keys in site config or doctype" -msgstr "" +msgstr "Bitte setzen Sie die Dropbox-Zugriffsschlüssel in der Site Config oder im DocType" #: contacts/doctype/contact/contact.py:200 msgid "Please set Email Address" @@ -22686,7 +22839,7 @@ msgstr "Bitte setzen Sie Filter Wert in Berichtsfiltertabelle." #: model/naming.py:533 msgid "Please set the document name" -msgstr "" +msgstr "Bitte geben Sie den Dokumentnamen ein" #: desk/doctype/dashboard/dashboard.py:125 msgid "Please set the following documents in this Dashboard as standard first." @@ -22706,11 +22859,11 @@ msgstr "Bitte richten Sie zuerst eine Nachricht ein" #: email/doctype/email_account/email_account.py:389 msgid "Please setup default Email Account from Settings > Email Account" -msgstr "" +msgstr "Bitte richten Sie ein Standard-E-Mail-Konto unter Einstellungen > E-Mail-Konto ein" #: core/doctype/user/user.py:369 msgid "Please setup default outgoing Email Account from Settings > Email Account" -msgstr "" +msgstr "Bitte legen Sie das Standard-E-Mail-Konto unter Einstellungen > E-Mail-Konto fest" #: public/js/frappe/model/model.js:785 msgid "Please specify" @@ -22718,7 +22871,7 @@ msgstr "Bitte angeben" #: permissions.py:782 msgid "Please specify a valid parent DocType for {0}" -msgstr "" +msgstr "Bitte geben Sie einen gültigen übergeordneten DocType für {0} an" #: email/doctype/notification/notification.py:86 msgid "Please specify which date field must be checked" @@ -22775,13 +22928,13 @@ msgstr "Punkte gegeben" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Popover Element" -msgstr "" +msgstr "Popover-Element" #. Label of a HTML Editor field in DocType 'Form Tour Step' #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "Popover or Modal Description" -msgstr "" +msgstr "Popover- oder Modal-Beschreibung" #. Label of a Data field in DocType 'Email Account' #: email/doctype/email_account/email_account.json @@ -22845,7 +22998,7 @@ msgstr "Absenden" #: templates/discussions/reply_section.html:39 msgid "Post it here, our mentors will help you out." -msgstr "" +msgstr "Posten Sie es hier, unsere Mentoren werden Ihnen helfen." #. Option for the 'Address Type' (Select) field in DocType 'Address' #: contacts/doctype/address/address.json @@ -23050,7 +23203,7 @@ msgstr "Vorheriger Hash" #: public/js/frappe/form/form.js:2162 msgid "Previous Submission" -msgstr "" +msgstr "Vorherige Buchungen" #. Option for the 'Style' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -23156,13 +23309,13 @@ msgstr "Programm zum Erstellen von Druckformaten" #. Label of a Link in the Tools Workspace #: automation/workspace/tools/tools.json msgid "Print Format Builder (New)" -msgstr "" +msgstr "Druckformat-Generator (neu)" #. Label of a Check field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json msgctxt "Print Format" msgid "Print Format Builder Beta" -msgstr "" +msgstr "Format-Builder Beta drucken" #: utils/pdf.py:52 msgid "Print Format Error" @@ -23171,7 +23324,7 @@ msgstr "Druckformatfehler" #. Name of a DocType #: printing/doctype/print_format_field_template/print_format_field_template.json msgid "Print Format Field Template" -msgstr "" +msgstr "Druckformat Feldvorlage" #. Label of a HTML field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json @@ -23192,12 +23345,12 @@ msgstr "Druckformat {0} ist deaktiviert" #. Description of the Onboarding Step 'Customize Print Formats' #: custom/onboarding_step/print_format/print_format.json msgid "Print Formats allow you can define looks for documents when printed or converted to PDF. You can also create a custom Print Format using drag-and-drop tools." -msgstr "" +msgstr "Mit Druckformaten können Sie das Aussehen von Dokumenten festlegen, die gedruckt oder in PDF konvertiert werden. Sie können auch ein benutzerdefiniertes Druckformat mit Hilfe von Drag-and-Drop-Tools erstellen." #. Name of a DocType #: printing/doctype/print_heading/print_heading.json msgid "Print Heading" -msgstr "" +msgstr "Druck-Kopfzeile" #. Label of a Link in the Tools Workspace #. Label of a Data field in DocType 'Print Heading' @@ -23205,7 +23358,7 @@ msgstr "" #: printing/doctype/print_heading/print_heading.json msgctxt "Print Heading" msgid "Print Heading" -msgstr "" +msgstr "Druck-Kopfzeile" #. Label of a Check field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json @@ -23495,7 +23648,7 @@ msgstr "Eigenschaftstyp" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Provide a list of allowed file extensions for file uploads. Each line should contain one allowed file type. If unset, all file extensions are allowed. Example:
CSV
JPG
PNG" -msgstr "" +msgstr "Geben Sie eine Liste der zulässigen Dateierweiterungen für Datei-Uploads an. Jede Zeile sollte einen erlaubten Dateityp enthalten. Wenn Sie nichts angeben, sind alle Dateierweiterungen erlaubt. Beispiel:
CSV
JPG
PNG" #. Label of a Data field in DocType 'User Social Login' #: core/doctype/user_social_login/user_social_login.json @@ -23864,7 +24017,7 @@ msgstr "Eingereiht von" #: core/doctype/submission_queue/submission_queue.py:173 msgid "Queued for Submission. You can track the progress over {0}." -msgstr "" +msgstr "In der Warteschlange für die Buchung. Sie können den Fortschritt über {0} verfolgen." #: integrations/doctype/dropbox_settings/dropbox_settings.py:64 #: integrations/doctype/google_drive/google_drive.py:156 @@ -23878,15 +24031,15 @@ msgstr "Warteschlange für Backup. Sie erhalten eine E-Mail mit dem Download-Lin #: email/doctype/newsletter/newsletter.js:95 msgid "Queued {0} emails" -msgstr "" +msgstr "{0} E-Mails in der Warteschlange" #: email/doctype/newsletter/newsletter.js:90 msgid "Queuing emails..." -msgstr "" +msgstr "E-Mails in die Warteschlange stellen..." #: desk/doctype/bulk_update/bulk_update.py:88 msgid "Queuing {0} for Submission" -msgstr "" +msgstr "Einreihen von {0} zur Buchung" #. Label of a Check field in DocType 'Customize Form' #: custom/doctype/customize_form/customize_form.json @@ -23904,18 +24057,18 @@ msgstr "Schnelleingabe" #: desk/doctype/workspace_quick_list/workspace_quick_list.json msgctxt "Workspace Quick List" msgid "Quick List Filter" -msgstr "" +msgstr "Schnelllisten-Filter" #. Label of a Tab Break field in DocType 'Workspace' #. Label of a Table field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" msgid "Quick Lists" -msgstr "" +msgstr "Schnelllisten" #: public/js/frappe/views/reports/report_utils.js:280 msgid "Quoting must be between 0 and 3" -msgstr "" +msgstr "Einstellung für CSV-Anführungszeichen muss zwischen 0 und 3 liegen" #. Label of a Section Break field in DocType 'Access Log' #: core/doctype/access_log/access_log.json @@ -23953,13 +24106,13 @@ msgstr "Bandbreite" #: core/doctype/server_script/server_script.json msgctxt "Server Script" msgid "Rate Limiting" -msgstr "" +msgstr "Anfragen begrenzen" #. Label of a Section Break field in DocType 'Blog Settings' #: website/doctype/blog_settings/blog_settings.json msgctxt "Blog Settings" msgid "Rate Limits" -msgstr "" +msgstr "Anfragen begrenzen" #. Label of a Int field in DocType 'Communication' #: core/doctype/communication/communication.json @@ -24021,7 +24174,7 @@ msgstr "Rohdruck" #: printing/page/print/print.js:165 msgid "Raw Printing Setting" -msgstr "" +msgstr "Einstellung für Rohdruck" #: desk/doctype/console_log/console_log.js:6 msgid "Re-Run in Console" @@ -24136,7 +24289,7 @@ msgstr "Nur lesen hängt von ab" #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Read Only Depends On (JS)" -msgstr "" +msgstr "Bedingungen für Schreibschutz (JS)" #: templates/includes/navbar/navbar_items.html:97 msgid "Read Only Mode" @@ -24166,7 +24319,7 @@ msgstr "Lesemodus" #: utils/safe_exec.py:91 msgid "Read the documentation to know more" -msgstr "" +msgstr "Lesen Sie die Dokumentation, um mehr zu erfahren" #. Label of a Markdown Editor field in DocType 'Package' #: core/doctype/package/package.json @@ -24201,13 +24354,13 @@ msgstr "Baum neu aufbauen" #: utils/nestedset.py:180 msgid "Rebuilding of tree is not supported for {}" -msgstr "" +msgstr "Die Neuberechnung des Baums wird für {} nicht unterstützt" #. Description of the 'Anonymous' (Check) field in DocType 'Web Form' #: website/doctype/web_form/web_form.json msgctxt "Web Form" msgid "Receive anonymous response" -msgstr "" +msgstr "Anonyme Antwort erhalten" #. Option for the 'Sent or Received' (Select) field in DocType 'Communication' #: core/doctype/communication/communication.json @@ -24217,7 +24370,7 @@ msgstr "Empfangen" #: integrations/doctype/token_cache/token_cache.py:49 msgid "Received an invalid token type." -msgstr "" +msgstr "Es wurde ein ungültiger Tokentyp empfangen." #. Label of a Select field in DocType 'Notification Recipient' #: email/doctype/notification_recipient/notification_recipient.json @@ -24243,7 +24396,7 @@ msgstr "Die letzten Jahre sind leicht zu erraten." #: public/js/frappe/ui/toolbar/search_utils.js:516 msgid "Recents" -msgstr "" +msgstr "Kürzlich aufgerufen" #. Label of a Table field in DocType 'Email Queue' #: email/doctype/email_queue/email_queue.json @@ -24320,7 +24473,7 @@ msgstr "Redirect URI Bound To Auth-Code" #: integrations/doctype/oauth_client/oauth_client.json msgctxt "OAuth Client" msgid "Redirect URIs" -msgstr "" +msgstr "Weiterleitungs-URIs" #. Label of a Data field in DocType 'Social Login Key' #: integrations/doctype/social_login_key/social_login_key.json @@ -24338,13 +24491,13 @@ msgstr "Redirect-URL" #: email/doctype/email_group/email_group.json msgctxt "Email Group" msgid "Redirect to this URL after successful confirmation." -msgstr "" +msgstr "Nach erfolgreicher Bestätigung zu dieser URL weiterleiten." #. Label of a Tab Break field in DocType 'Website Settings' #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" msgid "Redirects" -msgstr "" +msgstr "Weiterleitungen" #: sessions.py:148 msgid "Redis cache server not running. Please contact Administrator / Tech support" @@ -24366,7 +24519,7 @@ msgstr "Ref-DocType" #: desk/doctype/form_tour/form_tour.js:38 msgid "Referance Doctype and Dashboard Name both can't be used at the same time." -msgstr "" +msgstr "Referenz-DocType und Dashboard-Name können nicht gleichzeitig verwendet werden." #: core/doctype/user_type/user_type_dashboard.py:5 desk/report/todo/todo.py:42 #: public/js/frappe/views/interaction.js:54 @@ -24494,13 +24647,13 @@ msgstr "Referenzdokument" #: core/doctype/document_share_key/document_share_key.json msgctxt "Document Share Key" msgid "Reference Document Name" -msgstr "" +msgstr "Name des Referenzdokuments" #. Label of a Dynamic Link field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json msgctxt "Integration Request" msgid "Reference Document Name" -msgstr "" +msgstr "Name des Referenzdokuments" #. Label of a Link field in DocType 'Activity Log' #: core/doctype/activity_log/activity_log.json @@ -24757,13 +24910,13 @@ msgstr "Referenz: {0} {1}" #: website/report/website_analytics/website_analytics.js:37 msgid "Referrer" -msgstr "" +msgstr "Referrer" #. Label of a Data field in DocType 'Web Page View' #: website/doctype/web_page_view/web_page_view.json msgctxt "Web Page View" msgid "Referrer" -msgstr "" +msgstr "Referrer" #: printing/page/print/print.js:73 public/js/frappe/desk.js:133 #: public/js/frappe/form/form.js:1174 public/js/frappe/list/base_list.js:65 @@ -24850,7 +25003,7 @@ msgstr "Versionshinweise" #: core/doctype/communication/communication.js:48 #: core/doctype/communication/communication.js:159 msgid "Relink" -msgstr "" +msgstr "Neu verknüpfen" #: core/doctype/communication/communication.js:138 msgid "Relink Communication" @@ -24860,13 +25013,13 @@ msgstr "Relink Kommunikation" #: core/doctype/comment/comment.json msgctxt "Comment" msgid "Relinked" -msgstr "" +msgstr "Neu verknüpft" #. Option for the 'Comment Type' (Select) field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Relinked" -msgstr "" +msgstr "Neu verknüpft" #. Label of a standard navbar item #. Type: Action @@ -24877,7 +25030,7 @@ msgstr "Neu laden" #: public/js/frappe/list/base_list.js:239 msgid "Reload List" -msgstr "" +msgstr "Reload List" #: public/js/frappe/views/reports/query_report.js:99 msgid "Reload Report" @@ -24897,13 +25050,13 @@ msgstr "Zuletzt gewählten Wert merken" #: public/js/frappe/form/reminders.js:33 msgid "Remind At" -msgstr "" +msgstr "Erinnern am" #. Label of a Datetime field in DocType 'Reminder' #: automation/doctype/reminder/reminder.json msgctxt "Reminder" msgid "Remind At" -msgstr "" +msgstr "Erinnern am" #: public/js/frappe/form/toolbar.js:436 msgid "Remind Me" @@ -24911,7 +25064,7 @@ msgstr "Erinnere mich" #: public/js/frappe/form/reminders.js:13 msgid "Remind Me In" -msgstr "" +msgstr "Erinnere mich in" #. Name of a DocType #: automation/doctype/reminder/reminder.json @@ -24928,7 +25081,7 @@ msgstr "" #: core/doctype/rq_job/rq_job_list.js:8 msgid "Remove Failed Jobs" -msgstr "" +msgstr "Fehlgeschlagene Jobs entfernen" #: printing/page/print_format_builder/print_format_builder.js:488 msgid "Remove Field" @@ -25141,19 +25294,19 @@ msgstr "Bericht" #: public/js/frappe/list/list_view_select.js:66 msgid "Report Builder" -msgstr "Berichts-Generator" +msgstr "Berichterstellungswerkzeug" #. Option for the 'Report Type' (Select) field in DocType 'Report' #: core/doctype/report/report.json msgctxt "Report" msgid "Report Builder" -msgstr "Berichts-Generator" +msgstr "Berichterstellungswerkzeug" #. Option for the 'DocType View' (Select) field in DocType 'Workspace Shortcut' #: desk/doctype/workspace_shortcut/workspace_shortcut.json msgctxt "Workspace Shortcut" msgid "Report Builder" -msgstr "Berichts-Generator" +msgstr "Berichterstellungswerkzeug" #. Name of a DocType #: core/doctype/report_column/report_column.json @@ -25247,7 +25400,7 @@ msgstr "Berichtsname" #: desk/doctype/number_card/number_card.py:65 msgid "Report Name, Report Field and Fucntion are required to create a number card" -msgstr "" +msgstr "Zum Erstellen einer Nummernkarte sind Berichtsname, Berichtsfeld und Funktion erforderlich" #. Label of a Data field in DocType 'Onboarding Step' #: desk/doctype/onboarding_step/onboarding_step.json @@ -25289,11 +25442,11 @@ msgstr "Der Bericht enthält keine numerischen Felder. Bitte ändern Sie den Ber #: public/js/frappe/views/reports/query_report.js:935 msgid "Report initiated, click to view status" -msgstr "" +msgstr "Bericht initiiert. Klicken Sie hier, um den Status anzuzeigen" #: email/doctype/auto_email_report/auto_email_report.py:102 msgid "Report limit reached" -msgstr "" +msgstr "Berichtsgrenze erreicht" #: core/doctype/prepared_report/prepared_report.py:202 msgid "Report timed out." @@ -25358,7 +25511,7 @@ msgstr "Kontolöschung anfordern" #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Request Body" -msgstr "" +msgstr "Body anfordern" #. Label of a Code field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json @@ -25370,19 +25523,19 @@ msgstr "Daten anfordern" #: integrations/doctype/integration_request/integration_request.json msgctxt "Integration Request" msgid "Request Description" -msgstr "" +msgstr "Beschreibung der Anfrage" #. Label of a Code field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json msgctxt "Integration Request" msgid "Request Headers" -msgstr "" +msgstr "Anfrage-Kopfzeilen" #. Label of a Code field in DocType 'Recorder' #: core/doctype/recorder/recorder.json msgctxt "Recorder" msgid "Request Headers" -msgstr "" +msgstr "Anfrage-Kopfzeilen" #. Label of a Data field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json @@ -25414,13 +25567,13 @@ msgstr "Zeitüberschreitung der Anfrage" #: public/js/frappe/request.js:241 msgid "Request Timeout" -msgstr "" +msgstr "Zeitüberschreitung der Anfrage" #. Label of a Int field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Request Timeout" -msgstr "" +msgstr "Zeitüberschreitung der Anfrage" #. Label of a Data field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json @@ -25445,14 +25598,14 @@ msgstr "Vertrauenswürdiges Zertifikat anfordern" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "Requires any valid fdn path. i.e. ou=groups,dc=example,dc=com" -msgstr "" +msgstr "Benötigt einen gültigen fdn-Pfad. z.B. ou=groups,dc=example,dc=com" #. Description of the 'LDAP search path for Users' (Data) field in DocType #. 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "Requires any valid fdn path. i.e. ou=users,dc=example,dc=com" -msgstr "" +msgstr "Benötigt einen gültigen fdn-Pfad. z.B. ou=users,dc=example,dc=com" #: core/doctype/communication/communication.js:279 msgid "Res: {0}" @@ -25513,13 +25666,13 @@ msgstr "Passwortschlüssel zurücksetzen" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Reset Password Link Expiry Duration" -msgstr "" +msgstr "Gültigkeitsdauer des Passwort-Links zurücksetzen" #. Label of a Link field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Reset Password Template" -msgstr "" +msgstr "Vorlage Passwort zurücksetzen" #: core/page/permission_manager/permission_manager.js:109 msgid "Reset Permissions for {0}?" @@ -25758,19 +25911,19 @@ msgstr "Gesperrte" #: website/doctype/blog_post/blog_post.json msgctxt "Blog Post" msgid "Rich Text" -msgstr "" +msgstr "Rich-Text" #. Option for the 'Content Type' (Select) field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Rich Text" -msgstr "" +msgstr "Rich-Text" #. Option for the 'Content Type' (Select) field in DocType 'Web Page' #: website/doctype/web_page/web_page.json msgctxt "Web Page" msgid "Rich Text" -msgstr "" +msgstr "Rich-Text" #. Option for the 'Position' (Select) field in DocType 'Form Tour Step' #: desk/doctype/form_tour_step/form_tour_step.json @@ -25970,7 +26123,7 @@ msgstr "Rolle und Ebene" #: core/doctype/user/user.py:314 msgid "Role has been set as per the user type {0}" -msgstr "" +msgstr "Die Rolle wurde gemäß Benutzertyp {0} festgelegt" #: core/page/permission_manager/permission_manager.js:59 msgid "Roles" @@ -26186,7 +26339,7 @@ msgstr "Zeile" #: core/doctype/doctype/doctype.py:1772 core/doctype/doctype/doctype.py:1782 msgid "Row # {0}: Non administrator user can not set the role {1} to the custom doctype" -msgstr "" +msgstr "Zeile # {0}: Nicht-Administrator-Benutzer können die Rolle {1} nicht auf den benutzerdefinierten Doctype einstellen" #: model/base_document.py:868 msgid "Row #{0}:" @@ -26194,7 +26347,7 @@ msgstr "Zeile #{0}:" #: core/doctype/doctype/doctype.py:492 msgid "Row #{}: Fieldname is required" -msgstr "" +msgstr "Zeile #{}: Feldname ist erforderlich" #. Label of a Data field in DocType 'Transaction Log' #: core/doctype/transaction_log/transaction_log.json @@ -26260,7 +26413,7 @@ msgstr "Regelname" #: permissions.py:662 msgid "Rule for this doctype, role, permlevel and if-owner combination already exists." -msgstr "" +msgstr "Die Regel für diese Kombination aus Doctype, Rolle, Permlevel und if-owner existiert bereits." #. Group in DocType's connections #: core/doctype/doctype/doctype.json @@ -26592,7 +26745,7 @@ msgstr "Speichere Anpassung..." #: desk/doctype/module_onboarding/module_onboarding.js:8 msgid "Saving this will export this document as well as the steps linked here as json." -msgstr "" +msgstr "Wenn Sie dies speichern, werden dieses Dokument und die hier verlinkten Schritte als JSON exportiert." #: public/js/form_builder/store.js:228 #: public/js/print_format_builder/store.js:36 @@ -26628,7 +26781,7 @@ msgstr "Senden planen" #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Schedule sending at a later time" -msgstr "" +msgstr "Senden zu einem späteren Zeitpunkt planen" #: email/doctype/newsletter/newsletter_list.js:7 msgid "Scheduled" @@ -26684,13 +26837,13 @@ msgstr "Geplanter Auftragstyp" #: core/workspace/build/build.json msgctxt "Scheduled Job Log" msgid "Scheduled Jobs Logs" -msgstr "" +msgstr "Protokolle geplanter Jobs" #. Label of a Section Break field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Scheduled Sending" -msgstr "" +msgstr "Geplanter Versand" #. Label of a Int field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json @@ -26718,7 +26871,7 @@ msgstr "Scheduler Inaktiv" #: utils/scheduler.py:196 msgid "Scheduler can not be re-enabled when maintenance mode is active." -msgstr "" +msgstr "Scheduler kann nicht wieder aktiviert werden, wenn der Wartungsmodus aktiv ist." #: core/doctype/data_import/data_import.py:97 msgid "Scheduler is inactive. Cannot import data." @@ -26726,48 +26879,48 @@ msgstr "Scheduler ist inaktiv. Daten können nicht importiert werden." #: core/doctype/rq_job/rq_job_list.js:19 msgid "Scheduler: Active" -msgstr "" +msgstr "Planer: Aktiv" #: core/doctype/rq_job/rq_job_list.js:21 msgid "Scheduler: Inactive" -msgstr "" +msgstr "Zeitplaner: Inaktiv" #. Label of a Data field in DocType 'OAuth Scope' #: integrations/doctype/oauth_scope/oauth_scope.json msgctxt "OAuth Scope" msgid "Scope" -msgstr "" +msgstr "Geltungsbereich" #. Label of a Section Break field in DocType 'Connected App' #. Label of a Table field in DocType 'Connected App' #: integrations/doctype/connected_app/connected_app.json msgctxt "Connected App" msgid "Scopes" -msgstr "" +msgstr "Geltungsbereiche" #. Label of a Text field in DocType 'OAuth Authorization Code' #: integrations/doctype/oauth_authorization_code/oauth_authorization_code.json msgctxt "OAuth Authorization Code" msgid "Scopes" -msgstr "" +msgstr "Geltungsbereiche" #. Label of a Text field in DocType 'OAuth Bearer Token' #: integrations/doctype/oauth_bearer_token/oauth_bearer_token.json msgctxt "OAuth Bearer Token" msgid "Scopes" -msgstr "" +msgstr "Geltungsbereiche" #. Label of a Text field in DocType 'OAuth Client' #: integrations/doctype/oauth_client/oauth_client.json msgctxt "OAuth Client" msgid "Scopes" -msgstr "" +msgstr "Geltungsbereiche" #. Label of a Table field in DocType 'Token Cache' #: integrations/doctype/token_cache/token_cache.json msgctxt "Token Cache" msgid "Scopes" -msgstr "" +msgstr "Geltungsbereiche" #. Label of a Code field in DocType 'Client Script' #: custom/doctype/client_script/client_script.json @@ -26825,19 +26978,19 @@ msgstr "Skripttyp" #. Label of a Card Break in the Build Workspace #: core/workspace/build/build.json msgid "Scripting" -msgstr "" +msgstr "Skripterstellung" #. Label of a Tab Break field in DocType 'Web Page' #: website/doctype/web_page/web_page.json msgctxt "Web Page" msgid "Scripting" -msgstr "" +msgstr "Skripterstellung" #. Label of a Section Break field in DocType 'Web Form' #: website/doctype/web_form/web_form.json msgctxt "Web Form" msgid "Scripting / Style" -msgstr "" +msgstr "Skripterstellung / Stil" #: public/js/frappe/form/link_selector.js:46 #: public/js/frappe/ui/toolbar/search.js:49 @@ -26972,7 +27125,7 @@ msgstr "Auf der Webseite ansehen" #: website/doctype/web_form/templates/web_form.html:150 msgid "See previous responses" -msgstr "" +msgstr "Vorherige Antworten anzeigen" #: integrations/doctype/slack_webhook_url/slack_webhook_url.py:48 msgid "See the document at {0}" @@ -27267,11 +27420,11 @@ msgstr "DocType auswählen, um ein neues Format zu erstellen" #: integrations/doctype/webhook/webhook.py:130 msgid "Select a document to check if it meets conditions." -msgstr "" +msgstr "Wählen Sie ein Dokument aus, um zu prüfen, ob es die Bedingungen erfüllt." #: integrations/doctype/webhook/webhook.py:142 msgid "Select a document to preview request data" -msgstr "" +msgstr "Wählen Sie ein Dokument zur Vorschau der Anfragedaten" #: public/js/frappe/views/treeview.js:342 msgid "Select a group node first." @@ -27331,7 +27484,7 @@ msgstr "Bitte Element auswählen, nach dem ein neues Feld eingefügt werden soll #: public/js/frappe/utils/diffview.js:101 msgid "Select two versions to view the diff." -msgstr "" +msgstr "Wählen Sie zwei Versionen aus, um den Unterschied anzuzeigen." #: public/js/frappe/form/link_selector.js:24 #: public/js/frappe/form/multi_select_dialog.js:79 @@ -27379,7 +27532,7 @@ msgstr "E-Mail-Benachrichtigung senden" #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Send Email At" -msgstr "" +msgstr "E-Mail senden am" #. Description of the 'Send Print as PDF' (Check) field in DocType 'Print #. Settings' @@ -27488,7 +27641,7 @@ msgstr "Abmelde-Link senden" #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Send Web View Link" -msgstr "" +msgstr "Webview-Link senden" #. Label of a Check field in DocType 'User' #: core/doctype/user/user.json @@ -27498,7 +27651,7 @@ msgstr "Willkommens-E-Mail senden" #: www/me.html:67 msgid "Send a request to delete your account" -msgstr "" +msgstr "Senden Sie eine Anfrage zum Löschen Ihres Kontos" #: email/doctype/newsletter/newsletter.js:10 msgid "Send a test email" @@ -27815,15 +27968,15 @@ msgstr "Serverskript" #: utils/safe_exec.py:90 msgid "Server Scripts are disabled. Please enable server scripts from bench configuration." -msgstr "" +msgstr "Serverskripte sind deaktiviert. Bitte aktivieren Sie Server-Skripte in der Bankkonfiguration." #: core/doctype/server_script/server_script.js:32 msgid "Server Scripts feature is not available on this site." -msgstr "" +msgstr "Server-Skript-Funktion ist auf dieser Seite nicht verfügbar." #: public/js/frappe/request.js:243 public/js/frappe/request.js:251 msgid "Server was too busy to process this request. Please try again." -msgstr "" +msgstr "Server war zu beschäftigt, um diese Anfrage zu bearbeiten. Bitte versuchen Sie es erneut." #. Label of a Select field in DocType 'Email Account' #: email/doctype/email_account/email_account.json @@ -27923,7 +28076,7 @@ msgstr "Limit festlegen" #: core/doctype/document_naming_settings/document_naming_settings.json msgctxt "Document Naming Settings" msgid "Set Naming Series options on your transactions." -msgstr "" +msgstr "Legen Sie Nummernkreise für Ihre Transaktionen fest." #. Label of a Password field in DocType 'User' #: core/doctype/user/user.json @@ -28058,7 +28211,24 @@ msgid "Set the filters here. For example:\n" "\treqd: 1\n" "}]\n" "
" -msgstr "" +msgstr "Legen Sie hier die Filter fest. Beispiel:\n" +"
\n"
+"[{\n"
+"\tfieldname: \"company\",\n"
+"\tlabel: __(\"Company\"),\n"
+"\tfieldtype: \"Link\",\n"
+"\toptions: \"Company\",\n"
+"\tdefault: frappe.defaults.get_user_default(\"Company\"),\n"
+"\treqd: 1\n"
+"},\n"
+"{\n"
+"\tfieldname: \"account\",\n"
+"\tlabel: __(\"Account\"),\n"
+"\tfieldtype: \"Link\",\n"
+"\toptions: \"Account\",\n"
+"\treqd: 1\n"
+"}]\n"
+"
" #. Description of the 'Method' (Data) field in DocType 'Number Card' #: desk/doctype/number_card/number_card.json @@ -28071,7 +28241,14 @@ msgid "Set the path to a whitelisted function that will return the data for the "\t\"route_options\": {\"from_date\": \"2023-05-23\"},\n" "\t\"route\": [\"query-report\", \"Permitted Documents For User\"]\n" "}" -msgstr "" +msgstr "Tragen Sie den Pfad zu einer freigegebenen Funktion ein, die die Daten für die Zahlenkarte im folgenden Format zurückgibt:\n\n" +"
\n"
+"{\n"
+"\t\"value\": value,\n"
+"\t\"fieldtype\": \"Currency\",\n"
+"\t\"route_options\": {\"from_date\": \"2023-05-23\"},\n"
+"\t\"route\": [\"query-report\", \"Permitted Documents For User\"]\n"
+"}
" #: contacts/doctype/address_template/address_template.py:32 msgid "Setting this Address Template as default as there is no other default" @@ -28138,7 +28315,7 @@ msgstr "Einrichtung" #. Title of an Onboarding Step #: custom/onboarding_step/workflows/workflows.json msgid "Setup Approval Workflows" -msgstr "" +msgstr "Genehmigungsworkflows einrichten" #: public/js/frappe/views/reports/query_report.js:1658 #: public/js/frappe/views/reports/report_view.js:1609 @@ -28158,7 +28335,7 @@ msgstr "Einrichtung abgeschlossen" #. Title of an Onboarding Step #: custom/onboarding_step/role_permissions/role_permissions.json msgid "Setup Limited Access for a User" -msgstr "" +msgstr "Eingeschränkten Zugriff für einen Benutzer einrichten" #. Title of an Onboarding Step #: custom/onboarding_step/naming_series/naming_series.json @@ -28257,7 +28434,7 @@ msgstr "Anzeigen" #: website/doctype/blog_settings/blog_settings.json msgctxt "Blog Settings" msgid "Show \"Call to Action\" in Blog" -msgstr "" +msgstr "„Aufruf zum Handeln\" im Blog anzeigen" #. Label of a Check field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json @@ -28599,7 +28776,7 @@ msgstr "Registrieren" #: integrations/doctype/social_login_key/social_login_key.json msgctxt "Social Login Key" msgid "Sign ups" -msgstr "" +msgstr "Registrierungen" #. Option for the 'Field Type' (Select) field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json @@ -28683,7 +28860,7 @@ msgstr "Einzelne Typen haben nur einen Datensatz, keine Tabellen zugeordnet. Die #: database/database.py:230 msgid "Site is running in read only mode for maintenance or site update, this action can not be performed right now. Please try again later." -msgstr "" +msgstr "Diese Instanz läuft im schreibgeschützten Modus für Wartungsarbeiten und Aktualisierungen. Diese Aktion kann daher momentan nicht ausgeführt werden. Bitte versuchen Sie es später erneut." #: public/js/onboarding_tours/onboarding_tours.js:18 msgid "Skip" @@ -28725,11 +28902,11 @@ msgstr "Spalte {0} wird übersprungen" #: modules/utils.py:162 msgid "Skipping fixture syncing for doctype {0} from file {1}" -msgstr "" +msgstr "Überspringe Synchronisierung von Fixtures für den Doctype {0} aus der Datei {1}" #: core/doctype/data_import/data_import.js:39 msgid "Skipping {0} of {1}, {2}" -msgstr "" +msgstr "Überspringe {0} von {1}, {2}" #. Label of a Data field in DocType 'Contact Us Settings' #: website/doctype/contact_us_settings/contact_us_settings.json @@ -28840,13 +29017,13 @@ msgstr "Typ des sozialen Links" #. Name of a DocType #: integrations/doctype/social_login_key/social_login_key.json msgid "Social Login Key" -msgstr "" +msgstr "Social Login Key" #. Label of a Link in the Integrations Workspace #: integrations/workspace/integrations/integrations.json msgctxt "Social Login Key" msgid "Social Login Key" -msgstr "" +msgstr "Social Login Key" #. Label of a Select field in DocType 'Social Login Key' #: integrations/doctype/social_login_key/social_login_key.json @@ -29043,7 +29220,7 @@ msgstr "Standard" #: model/delete_doc.py:79 msgid "Standard DocType can not be deleted." -msgstr "" +msgstr "Standard DocType kann nicht gelöscht werden." #: core/doctype/doctype/doctype.py:228 msgid "Standard DocType cannot have default print format, use Customize Form" @@ -29085,7 +29262,7 @@ msgstr "Standardrollen kann nicht umbenannt werden" #: core/doctype/user_type/user_type.py:60 msgid "Standard user type {0} can not be deleted." -msgstr "" +msgstr "Standard-Benutzertyp {0} kann nicht gelöscht werden." #: templates/emails/energy_points_summary.html:33 msgid "Standings" @@ -29136,7 +29313,7 @@ msgstr "Startzeit" #: templates/includes/comments/comments.html:8 msgid "Start a new discussion" -msgstr "" +msgstr "Eine neue Unterhaltung starten" #: core/doctype/data_export/exporter.py:22 msgid "Start entering data below this line" @@ -29452,7 +29629,7 @@ msgstr "Speichert die JSON (JavaScript Object Notation) der letzten bekannten Ve #: core/doctype/user/user.json msgctxt "User" msgid "Stores the datetime when the last reset password key was generated." -msgstr "" +msgstr "Speichert das Datum, an dem der letzte Schlüssel zum Zurücksetzen des Passworts generiert wurde." #: utils/password_strength.py:99 msgid "Straight rows of keys are easy to guess" @@ -29681,11 +29858,11 @@ msgstr "Nach dem Import buchen" #: website/doctype/web_form/web_form.json msgctxt "Web Form" msgid "Submit Button Label" -msgstr "" +msgstr "Übertrage Button Label" #: website/doctype/web_form/templates/web_form.html:153 msgid "Submit another response" -msgstr "" +msgstr "Eine weitere Antwort senden" #. Label of a Check field in DocType 'Auto Repeat' #: automation/doctype/auto_repeat/auto_repeat.json @@ -29730,7 +29907,7 @@ msgstr "Buchung kann nicht in Entwurf umgewandelt werden. Zeile {0}" #: public/js/workflow_builder/utils.js:176 msgid "Submitted document cannot be converted back to draft while transitioning from {0} State to {1} State" -msgstr "" +msgstr "Das gebuchte Dokument kann beim Übergang vom Status {0} zum Status {1} nicht wieder in einen Entwurf umgewandelt werden." #: public/js/frappe/form/save.js:10 msgctxt "Freeze message while submitting a document" @@ -29857,11 +30034,11 @@ msgstr "Erfolgreich aktualisiert" #: core/doctype/data_import/data_import.js:434 msgid "Successfully imported {0}" -msgstr "" +msgstr "Erfolgreich importiert {0}" #: desk/doctype/form_tour/form_tour.py:86 msgid "Successfully reset onboarding status for all users." -msgstr "" +msgstr "Onboarding-Status für alle Benutzer erfolgreich zurückgesetzt." #: public/js/frappe/views/translation_manager.js:22 msgid "Successfully updated translations" @@ -29869,11 +30046,11 @@ msgstr "Erfolgreich aktualisierte Übersetzungen" #: core/doctype/data_import/data_import.js:442 msgid "Successfully updated {0}" -msgstr "" +msgstr "Erfolgreich aktualisiert {0}" #: core/doctype/data_import/data_import.js:149 msgid "Successfully {0} 1 record." -msgstr "" +msgstr "Erfolgreich {0} 1 Datensatz." #: core/doctype/data_import/data_import.js:156 msgid "Successfully {0} {1} record out of {2}. Click on Export Errored Rows, fix the errors and import again." @@ -29885,7 +30062,7 @@ msgstr "" #: core/doctype/data_import/data_import.js:151 msgid "Successfully {0} {1} records." -msgstr "" +msgstr "Erfolgreich {0} {1} Datensätze." #: core/doctype/user/user.py:679 msgid "Suggested Username: {0}" @@ -29971,7 +30148,7 @@ msgstr "Kamera wird gewechselt" #: geo/doctype/currency/currency.json msgctxt "Currency" msgid "Symbol" -msgstr "" +msgstr "Symbol" #. Label of a Section Break field in DocType 'Google Calendar' #: integrations/doctype/google_calendar/google_calendar.json @@ -29999,7 +30176,7 @@ msgstr "Anpassungen bei jeder Datenbankmigration synchronisieren" #: integrations/doctype/google_calendar/google_calendar.py:295 msgid "Sync token was invalid and has been reset, Retry syncing." -msgstr "" +msgstr "Das Synchronisierungstoken war ungültig und wurde zurückgesetzt. Versuchen Sie die Synchronisierung erneut." #. Label of a Check field in DocType 'Event' #: desk/doctype/event/event.json @@ -30038,7 +30215,7 @@ msgstr "Syntaxfehler" #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "System" -msgstr "" +msgstr "System" #. Name of a DocType #: desk/doctype/system_console/system_console.json @@ -30047,12 +30224,12 @@ msgstr "Systemkonsole" #: custom/doctype/custom_field/custom_field.py:358 msgid "System Generated Fields can not be renamed" -msgstr "" +msgstr "Systemgenerierte Felder können nicht umbenannt werden" #. Label of a Card Break in the Build Workspace #: core/workspace/build/build.json msgid "System Logs" -msgstr "" +msgstr "Systemprotokolle" #. Name of a role #: automation/doctype/assignment_rule/assignment_rule.json @@ -30187,7 +30364,7 @@ msgstr "System-Manager" #: desk/page/backups/backups.js:36 msgid "System Manager privileges required." -msgstr "" +msgstr "System-Manager-Berechtigungen erforderlich." #. Option for the 'Channel' (Select) field in DocType 'Notification' #: email/doctype/notification/notification.json @@ -30199,7 +30376,7 @@ msgstr "Systembenachrichtigung" #: desk/doctype/notification_settings/notification_settings.json msgctxt "Notification Settings" msgid "System Notifications" -msgstr "" +msgstr "Systembenachrichtigungen" #. Label of a Check field in DocType 'Page' #: core/doctype/page/page.json @@ -30234,19 +30411,19 @@ msgstr "Bio" #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "Tab Break" -msgstr "" +msgstr "Tab-Umbruch" #. Option for the 'Type' (Select) field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json msgctxt "Customize Form Field" msgid "Tab Break" -msgstr "" +msgstr "Tab-Umbruch" #. Option for the 'Type' (Select) field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Tab Break" -msgstr "" +msgstr "Tab-Umbruch" #: core/doctype/data_export/exporter.py:23 msgid "Table" @@ -30290,7 +30467,7 @@ msgstr "Tabellenfeldname" #: core/doctype/doctype/doctype.py:1154 msgid "Table Fieldname Missing" -msgstr "" +msgstr "Tabellenfeldname fehlt" #. Label of a HTML field in DocType 'Version' #: core/doctype/version/version.json @@ -30328,7 +30505,7 @@ msgstr "Tabelle {0} darf nicht leer sein" #: printing/doctype/print_settings/print_settings.json msgctxt "Print Settings" msgid "Tabloid" -msgstr "" +msgstr "Tabloid" #. Name of a DocType #: desk/doctype/tag/tag.json @@ -30398,13 +30575,13 @@ msgstr "Überschrift zu den Teammitgliedern" #: website/doctype/about_us_settings/about_us_settings.json msgctxt "About Us Settings" msgid "Team Members Subtitle" -msgstr "" +msgstr "Teammitglieder Untertitel" #. Label of a Section Break field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Telemetry" -msgstr "" +msgstr "Telemetrie" #. Label of a Code field in DocType 'Address Template' #: contacts/doctype/address_template/address_template.json @@ -30439,7 +30616,7 @@ msgstr "Vorlagenfehler" #: printing/doctype/print_format_field_template/print_format_field_template.json msgctxt "Print Format Field Template" msgid "Template File" -msgstr "" +msgstr "Vorlagendatei" #. Label of a Code field in DocType 'Data Import' #: core/doctype/data_import/data_import.json @@ -30473,31 +30650,31 @@ msgstr "Test_Ordner" #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "Text" -msgstr "" +msgstr "Text" #. Option for the 'Type' (Select) field in DocType 'Customize Form Field' #: custom/doctype/customize_form_field/customize_form_field.json msgctxt "Customize Form Field" msgid "Text" -msgstr "" +msgstr "Text" #. Option for the 'Type' (Select) field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Text" -msgstr "" +msgstr "Text" #. Option for the 'Fieldtype' (Select) field in DocType 'Web Form Field' #: website/doctype/web_form_field/web_form_field.json msgctxt "Web Form Field" msgid "Text" -msgstr "" +msgstr "Text" #. Option for the 'Fieldtype' (Select) field in DocType 'Web Template Field' #: website/doctype/web_template_field/web_template_field.json msgctxt "Web Template Field" msgid "Text" -msgstr "" +msgstr "Text" #. Label of a Select field in DocType 'Web Page' #: website/doctype/web_page/web_page.json @@ -30579,7 +30756,9 @@ msgctxt "Google Settings" msgid "The Client ID obtained from the Google Cloud Console under \n" "\"APIs & Services\" > \"Credentials\"\n" "" -msgstr "" +msgstr "Die Client-ID, die Sie in der Google Cloud Console unter \n" +"\"APIs & Services\" > \"Credentials\"\n" +"erhalten" #: email/doctype/notification/notification.py:129 msgid "The Condition '{0}' is invalid" @@ -30591,7 +30770,7 @@ msgstr "Die eingegebene Datei-URL ist falsch" #: website/doctype/personal_data_deletion_request/personal_data_deletion_request.py:364 msgid "The User record for this request has been auto-deleted due to inactivity by system admins." -msgstr "" +msgstr "Der Benutzerdatensatz für diese Anfrage wurde aufgrund der Inaktivität der Systemadministratoren automatisch gelöscht." #: public/js/frappe/desk.js:127 msgid "The application has been updated to a new version, please refresh this page" @@ -30614,7 +30793,9 @@ msgctxt "Google Settings" msgid "The browser API key obtained from the Google Cloud Console under \n" "\"APIs & Services\" > \"Credentials\"\n" "" -msgstr "" +msgstr "Der Browser-API-Schlüssel, den Sie von der Google Cloud Console unter \n" +"\"APIs & Dienste\" > \"Zugangsdaten\"\n" +"erhalten" #: database/database.py:388 msgid "The changes have been reverted." @@ -30641,14 +30822,14 @@ msgstr "Das Dokument wurde {0} zugewiesen" #: desk/doctype/dashboard_chart/dashboard_chart.json msgctxt "Dashboard Chart" msgid "The document type selected is a child table, so the parent document type is required." -msgstr "" +msgstr "Der ausgewählte Dokumenttyp ist eine untergeordnete Tabelle, daher ist der übergeordnete Dokumenttyp erforderlich." #. Description of the 'Parent Document Type' (Link) field in DocType 'Number #. Card' #: desk/doctype/number_card/number_card.json msgctxt "Number Card" msgid "The document type selected is a child table, so the parent document type is required." -msgstr "" +msgstr "Der ausgewählte Dokumenttyp ist eine untergeordnete Tabelle, daher ist der übergeordnete Dokumenttyp erforderlich." #: core/doctype/user_type/user_type.py:109 msgid "The field {0} is mandatory" @@ -30656,11 +30837,11 @@ msgstr "Das Feld {0} ist ein Pflichtfeld" #: core/doctype/file/file.py:143 msgid "The fieldname you've specified in Attached To Field is invalid" -msgstr "" +msgstr "Der Feldname, den Sie im Angehängten Feld angegeben haben, ist ungültig" #: core/doctype/data_import/importer.py:1035 msgid "The following values are invalid: {0}. Values must be one of {1}" -msgstr "" +msgstr "Die folgenden Werte sind ungültig: {0}. Die Werte müssen einer der folgenden sein: {1}" #: core/doctype/data_import/importer.py:998 msgid "The following values do not exist for {0}: {1}" @@ -30668,7 +30849,7 @@ msgstr "Die folgenden Werte existieren nicht für {0}: {1}" #: core/doctype/user_type/user_type.py:88 msgid "The limit has not set for the user type {0} in the site config file." -msgstr "" +msgstr "Das Limit für den Benutzertyp {0} in der Seitenkonfigurationsdatei wurde nicht gesetzt." #: templates/emails/login_with_email_link.html:21 msgid "The link will expire in {0} minutes" @@ -30722,7 +30903,9 @@ msgctxt "Google Settings" msgid "The project number obtained from Google Cloud Console under \n" "\"IAM & Admin\" > \"Settings\"\n" "" -msgstr "" +msgstr "Die Projektnummer aus der Google Cloud Console unter \n" +"\"IAM & Admin\" > \"Einstellungen\"\n" +"" #: core/doctype/user/user.py:943 msgid "The reset password link has been expired" @@ -30742,11 +30925,11 @@ msgstr "Die Rolle {0} sollte eine benutzerdefinierte Rolle sein." #: core/doctype/audit_trail/audit_trail.py:45 msgid "The selected document {0} is not a {1}." -msgstr "" +msgstr "Das ausgewählte Dokument {0} ist nicht vom Typ {1}." #: utils/response.py:321 msgid "The system is being updated. Please refresh again after a few moments." -msgstr "" +msgstr "Das System wird gerade aktualisiert. Bitte probieren Sie es nach einigen Augenblicken erneut." #: public/js/frappe/form/grid_row.js:615 msgid "The total column width cannot be more than 10." @@ -30754,7 +30937,7 @@ msgstr "Die Gesamtbreite aller Spalten darf nicht mehr als 10 sein." #: core/doctype/user_type/user_type.py:96 msgid "The total number of user document types limit has been crossed." -msgstr "" +msgstr "Das Limit für die Gesamtzahl der Benutzerdokumenttypen wurde überschritten." #. Description of the 'User Field' (Select) field in DocType 'Energy Point #. Rule' @@ -30765,7 +30948,7 @@ msgstr "Der Benutzer aus diesem Feld erhält Punkte" #: public/js/frappe/form/controls/data.js:24 msgid "The value you pasted was {0} characters long. Max allowed characters is {1}." -msgstr "" +msgstr "Der Wert, den Sie eingefügt haben, war {0} Zeichen lang. Die maximal erlaubten Zeichen sind {1}." #. Description of the 'Condition' (Small Text) field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json @@ -30872,7 +31055,7 @@ msgstr "Hier gibt es nichts" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "These settings are required if 'Custom' LDAP Directory is used" -msgstr "" +msgstr "Diese Einstellungen sind erforderlich, wenn das 'Benutzerdefinierte' LDAP-Verzeichnis verwendet wird" #. Description of the 'Defaults' (Section Break) field in DocType 'User' #: core/doctype/user/user.json @@ -30928,7 +31111,7 @@ msgstr "Dieses Diagramm steht allen Benutzern zur Verfügung, wenn dies festgele #: desk/doctype/workspace/workspace.js:23 msgid "This document allows you to edit limited fields. For all kinds of workspace customization, use the Edit button located on the workspace page" -msgstr "" +msgstr "In diesem Dokument können Sie begrenzte Felder bearbeiten. Für alle Arten von Anpassungen des Arbeitsbereichs verwenden Sie die Schaltfläche Bearbeiten auf der Seite des Arbeitsbereichs" #: social/doctype/energy_point_log/energy_point_log.py:90 msgid "This document cannot be reverted" @@ -30957,7 +31140,8 @@ msgstr "Diese E-Mail wurde automatisch generiert" #: printing/doctype/network_printer_settings/network_printer_settings.py:29 msgid "This feature can not be used as dependencies are missing.\n" "\t\t\t\tPlease contact your system manager to enable this by installing pycups!" -msgstr "" +msgstr "Diese Funktion kann nicht verwendet werden, da die Abhängigkeiten fehlen.\n" +"\t\t\t\tBitte wenden Sie sich an Ihren Systemmanager, um diese Funktion durch die Installation von pycups zu aktivieren!" #. Description of the 'Depends On' (Code) field in DocType 'Customize Form #. Field' @@ -30967,11 +31151,14 @@ msgid "This field will appear only if the fieldname defined here has value OR th "myfield\n" "eval:doc.myfield=='My Value'\n" "eval:doc.age>18" -msgstr "" +msgstr "Dieses Feld wird nur angezeigt, wenn der hier definierte Feldname Wert hat oder die Regeln wahr sind (Beispiele):\n" +"myfield\n" +"eval:doc.myfield=='Mein Wert'\n" +"eval:doc.age>18" #: core/doctype/file/file.js:10 msgid "This file is public. It can be accessed without authentication." -msgstr "" +msgstr "Diese Datei ist öffentlich. Sie kann ohne Authentifizierung aufgerufen werden." #: public/js/frappe/form/form.js:1172 msgid "This form has been modified after you have loaded it" @@ -31012,7 +31199,7 @@ msgstr "Dies ist ein sehr verbreitetes Passwort." #: core/doctype/rq_job/rq_job.js:9 msgid "This is a virtual doctype and data is cleared periodically." -msgstr "" +msgstr "Dies ist ein virtueller Dokumenttyp und die Daten werden regelmäßig gelöscht." #: templates/emails/auto_reply.html:5 msgid "This is an automatically generated reply" @@ -31058,7 +31245,7 @@ msgstr "Dieser Newsletter wird voraussichtlich am {0} verschickt" #: email/doctype/newsletter/newsletter.js:50 msgid "This newsletter was scheduled to send on a later date. Are you sure you want to send it now?" -msgstr "" +msgstr "Der Versand dieses Newsletters war zu einem späteren Zeitpunkt geplant. Sind Sie sicher, dass Sie es jetzt senden möchten?" #: templates/emails/auto_email_report.html:57 msgid "This report was generated on {0}" @@ -31074,7 +31261,7 @@ msgstr "Diese Anfrage wurde vom Benutzer noch nicht genehmigt." #: templates/includes/navbar/navbar_items.html:95 msgid "This site is in read only mode, full functionality will be restored soon." -msgstr "" +msgstr "Diese Instanz erlaubt derzeit nur lesenden Zugriff. Die volle Funktionalität wird bald wiederhergestellt werden." #: core/doctype/doctype/doctype.js:76 msgid "This site is running in developer mode. Any change made here will be updated in code." @@ -31593,14 +31780,16 @@ msgstr "An Benutzer" msgctxt "Auto Repeat" msgid "To add dynamic subject, use jinja tags like\n\n" "
New {{ doc.doctype }} #{{ doc.name }}
" -msgstr "" +msgstr "Um einen dynamischen Betreff hinzuzufügen, verwenden Sie Jinja-Tags wie\n\n" +"
Neu {{ doc.doctype }} #{{ doc.name }}
" #. Description of the 'Subject' (Data) field in DocType 'Notification' #: email/doctype/notification/notification.json msgctxt "Notification" msgid "To add dynamic subject, use jinja tags like\n\n" "
{{ doc.name }} Delivered
" -msgstr "" +msgstr "Um einen dynamischen Betreff hinzuzufügen, verwenden Sie Jinja-Tags wie\n\n" +"
{{ doc.name }} Delivered
" #. Description of the 'JSON Request Body' (Code) field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json @@ -31610,11 +31799,15 @@ msgid "To add dynamic values from the document, use jinja tags like\n\n" "
{ \"id\": \"{{ doc.name }}\" }\n"
 "
\n" "" -msgstr "" +msgstr "Um dynamische Werte aus dem Dokument hinzuzufügen, verwenden Sie Jinja-Tags wie\n\n" +"
\n" +"
{ \"id\": \"{{ doc.name }}\" }\n"
+"
\n" +"
" #: email/doctype/auto_email_report/auto_email_report.py:101 msgid "To allow more reports update limit in System Settings." -msgstr "" +msgstr "Um mehr Berichte zuzulassen, aktualisieren Sie das Limit in den Systemeinstellungen." #. Label of a Section Break field in DocType 'Communication' #: core/doctype/communication/communication.json @@ -31632,7 +31825,7 @@ msgstr "Befolgen Sie zum Aktivieren die Anweisungen unter folgendem Link: {0}" #: desk/doctype/onboarding_step/onboarding_step.js:18 msgid "To export this step as JSON, link it in a Onboarding document and save the document." -msgstr "" +msgstr "Um diesen Schritt als JSON zu exportieren, verknüpfen Sie ihn in einem Onboarding-Dokument und speichern das Dokument." #: public/js/frappe/views/reports/query_report.js:783 msgid "To get the updated report, click on {0}." @@ -31640,17 +31833,17 @@ msgstr "Klicken Sie auf {0}, um den aktualisierten Bericht abzurufen." #: www/me.html:51 msgid "To manage your authorized third party apps" -msgstr "" +msgstr "Um Ihre autorisierten Drittanbieter-Apps zu verwalten" #. Description of the 'Console' (Code) field in DocType 'System Console' #: desk/doctype/system_console/system_console.json msgctxt "System Console" msgid "To print output use print(text)" -msgstr "" +msgstr "Um die Ausgabe zu drucken, verwenden Sie print(text)" #: core/doctype/user_type/user_type.py:295 msgid "To set the role {0} in the user {1}, kindly set the {2} field as {3} in one of the {4} record." -msgstr "" +msgstr "Um die Rolle {0} im Benutzer {1} festzulegen, legen Sie bitte das Feld {2} als {3} in einem der Datensätze {4} fest." #: integrations/doctype/google_calendar/google_calendar.js:8 msgid "To use Google Calendar, enable {0}." @@ -31669,17 +31862,17 @@ msgstr "Aktivieren Sie {0}, um Google Drive zu verwenden." #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" msgid "To use Google Indexing, enable Google Settings." -msgstr "" +msgstr "Um die Google-Indizierung zu verwenden, aktivieren Sie die Google-Einstellungen." #. Description of the 'Slack Channel' (Link) field in DocType 'Notification' #: email/doctype/notification/notification.json msgctxt "Notification" msgid "To use Slack Channel, add a Slack Webhook URL." -msgstr "" +msgstr "Um den Slack Kanal zu verwenden, fügen Sie eine Slack Webhook URL hinzu." #: public/js/frappe/utils/diffview.js:43 msgid "To version" -msgstr "" +msgstr "Zu Version" #. Name of a DocType #. Name of a report @@ -31747,19 +31940,19 @@ msgstr "Zeichen" #. Name of a DocType #: integrations/doctype/token_cache/token_cache.json msgid "Token Cache" -msgstr "" +msgstr "Token Cache" #. Linked DocType in Connected App's connections #: integrations/doctype/connected_app/connected_app.json msgctxt "Connected App" msgid "Token Cache" -msgstr "" +msgstr "Token Cache" #. Linked DocType in User's connections #: core/doctype/user/user.json msgctxt "User" msgid "Token Cache" -msgstr "" +msgstr "Token Cache" #. Label of a Data field in DocType 'Token Cache' #: integrations/doctype/token_cache/token_cache.json @@ -31836,7 +32029,7 @@ msgstr "Oben links" #: templates/emails/energy_points_summary.html:3 msgid "Top Performer" -msgstr "" +msgstr "Top Performer" #: templates/emails/energy_points_summary.html:18 msgid "Top Reviewer" @@ -31918,7 +32111,7 @@ msgstr "Summenzeile" #: core/doctype/error_log/error_log.json msgctxt "Error Log" msgid "Trace ID" -msgstr "" +msgstr "Trace ID" #. Label of a Code field in DocType 'Patch Log' #: core/doctype/patch_log/patch_log.json @@ -31960,7 +32153,7 @@ msgstr "Track gesehen" #: desk/doctype/form_tour/form_tour.json msgctxt "Form Tour" msgid "Track Steps" -msgstr "" +msgstr "Schritte verfolgen" #. Label of a Check field in DocType 'Customize Form' #: custom/doctype/customize_form/customize_form.json @@ -31981,11 +32174,13 @@ msgctxt "Email Account" msgid "Track if your email has been opened by the recipient.\n" "
\n" "Note: If you're sending to multiple recipients, even if 1 recipient reads the email, it'll be considered \"Opened\"" -msgstr "" +msgstr "Verfolgen Sie, ob Ihre E-Mail vom Empfänger geöffnet wurde.\n" +"
\n" +"Hinweis: Wenn Sie an mehrere Empfänger senden, gilt die E-Mail auch dann als \"Geöffnet\", wenn nur ein Empfänger sie liest." #: public/js/frappe/utils/utils.js:1744 msgid "Tracking URL generated and copied to clipboard" -msgstr "" +msgstr "Tracking URL generiert und in die Zwischenablage kopiert" #. Label of a Small Text field in DocType 'Transaction Log' #: core/doctype/transaction_log/transaction_log.json @@ -32104,7 +32299,7 @@ msgstr "Primäre Aktion auslösen" #: tests/test_translate.py:55 msgid "Trigger caching" -msgstr "" +msgstr "Caching auslösen" #. Description of the 'Trigger Method' (Data) field in DocType 'Notification' #: email/doctype/notification/notification.json @@ -32259,7 +32454,7 @@ msgstr "Typ" #: public/js/frappe/form/controls/comment.js:78 msgid "Type a reply / comment" -msgstr "" +msgstr "Geben Sie eine Antwort/einen Kommentar ein" #: templates/includes/search_template.html:51 msgid "Type something in the search box to search" @@ -32342,7 +32537,8 @@ msgstr "UNGESEHEN" msgctxt "OAuth Client" msgid "URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n" "
e.g. http://hostname/api/method/frappe.integrations.oauth2_logins.login_via_facebook" -msgstr "" +msgstr "URIs für den Empfang des Autorisierungscodes, sobald der Benutzer den Zugriff erlaubt, sowie für Fehlerantworten. In der Regel ein REST-Endpunkt, der von der Client-App bereitgestellt wird.\n" +"
z.B. http://hostname/api/method/frappe.integrations.oauth2_logins.login_via_facebook" #. Label of a Small Text field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json @@ -32417,7 +32613,7 @@ msgstr "Das Dateiformat für {0} kann nicht gelesen werden" #: core/doctype/communication/email.py:173 msgid "Unable to send mail because of a missing email account. Please setup default Email Account from Settings > Email Account" -msgstr "" +msgstr "Sie können keine E-Mail senden, weil ein E-Mail-Konto fehlt. Bitte richten Sie ein Standard-E-Mail-Konto unter Einstellungen > E-Mail-Konto ein." #: public/js/frappe/views/calendar/calendar.js:439 msgid "Unable to update event" @@ -32498,7 +32694,7 @@ msgstr "Unbekannte Datei-Codierung. Folgende Verfahren wurden ausprobiert: UTF-8 #: core/doctype/submission_queue/submission_queue.js:7 msgid "Unlock Reference Document" -msgstr "" +msgstr "Referenzdokument entsperren" #: website/doctype/blog_post/blog_post.js:36 #: website/doctype/web_form/web_form.js:77 @@ -32614,7 +32810,7 @@ msgstr "Aktualisieren" #: core/doctype/document_naming_settings/document_naming_settings.json msgctxt "Document Naming Settings" msgid "Update Amendment Naming" -msgstr "" +msgstr "Benennung aktualisieren" #: public/js/frappe/views/workspace/workspace.js:596 msgid "Update Details" @@ -32725,11 +32921,11 @@ msgstr "Aktualisierung läuft" #: email/doctype/email_queue/email_queue.py:406 msgid "Updating Email Queue Statuses. The emails will be picked up in the next scheduled run." -msgstr "" +msgstr "Aktualisieren des Status der E-Mail-Warteschlange. Die E-Mails werden beim nächsten geplanten Lauf abgeholt." #: core/doctype/document_naming_rule/document_naming_rule.js:17 msgid "Updating counter may lead to document name conflicts if not done properly" -msgstr "" +msgstr "Aktualisierungszähler kann zu Dokumentennamenskonflikten führen, wenn sie nicht ordnungsgemäß ausgeführt werden" #: desk/page/setup_wizard/setup_wizard.py:23 msgid "Updating global settings" @@ -32741,7 +32937,7 @@ msgstr "Optionen für Nummernkreise werden aktualisiert" #: public/js/frappe/form/toolbar.js:126 msgid "Updating related fields..." -msgstr "" +msgstr "Aktualisiere zugehörige Felder..." #: desk/doctype/bulk_update/bulk_update.py:98 msgid "Updating {0}" @@ -32774,7 +32970,7 @@ msgstr "Auf Google Drive hochgeladen" #, python-format msgctxt "Onboarding Step" msgid "Use % for any non empty value." -msgstr "" +msgstr "Verwenden Sie % für jeden nicht leeren Wert." #. Label of a Check field in DocType 'Email Account' #: email/doctype/email_account/email_account.json @@ -32880,7 +33076,7 @@ msgstr "Verwenden Sie diesen Feldnamen, um den Titel zu erzeugen" #: core/doctype/user_email/user_email.json msgctxt "User Email" msgid "Used OAuth" -msgstr "" +msgstr "OAuh verwendet" #. Name of a DocType #: core/doctype/user/user.json @@ -33071,7 +33267,7 @@ msgstr "Benutzeraktivitätsbericht" #. Name of a DocType #: core/doctype/report/user_activity_report_without_sort.json msgid "User Activity Report Without Sort" -msgstr "" +msgstr "Benutzeraktivitätsbericht ohne Sortierung" #. Label of a Data field in DocType 'Web Page View' #: website/doctype/web_page_view/web_page_view.json @@ -33171,7 +33367,7 @@ msgstr "Benutzer-Id-Feld" #: core/doctype/user_type/user_type.py:287 msgid "User Id Field is mandatory in the user type {0}" -msgstr "" +msgstr "Benutzer-Id Feld ist obligatorisch in der Benutzerart {0}" #. Label of a Attach Image field in DocType 'User' #: core/doctype/user/user.json @@ -33291,7 +33487,7 @@ msgstr "Benutzer existiert nicht" #: core/doctype/user_type/user_type.py:82 msgid "User does not have permission to create the new {0}" -msgstr "" +msgstr "Der Benutzer hat keine Berechtigung zum Erstellen der neuen {0}" #: core/doctype/docshare/docshare.py:55 msgid "User is mandatory for Share" @@ -33317,7 +33513,7 @@ msgstr "Benutzer mit E-Mail-Adresse {0} existiert nicht" #: integrations/doctype/ldap_settings/ldap_settings.py:224 msgid "User with email: {0} does not exist in the system. Please ask 'System Administrator' to create the user for you." -msgstr "" +msgstr "Ein Benutzer mit der E-Mail-Adresse {0} existiert nicht im System. Bitten Sie den 'Systemadministrator', den Benutzer für Sie anzulegen." #: core/doctype/user/user.py:504 msgid "User {0} cannot be deleted" @@ -33350,13 +33546,13 @@ msgstr "Benutzer {0} ist deaktiviert" #: desk/form/assign_to.py:101 msgid "User {0} is not permitted to access this document." -msgstr "" +msgstr "Der Benutzer {0} ist nicht berechtigt, auf dieses Dokument zuzugreifen." #. Label of a Data field in DocType 'Connected App' #: integrations/doctype/connected_app/connected_app.json msgctxt "Connected App" msgid "Userinfo URI" -msgstr "" +msgstr "Benutzerinfo URI" #: www/login.py:99 msgid "Username" @@ -33403,7 +33599,7 @@ msgstr "Benutzer mit Rolle {0}:" #: public/js/frappe/ui/theme_switcher.js:70 msgid "Uses system's theme to switch between light and dark mode" -msgstr "" +msgstr "Verwendet das Systemdesign, um zwischen hellem und dunklem Modus zu wechseln" #: public/js/frappe/desk.js:112 msgid "Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand." @@ -33638,7 +33834,7 @@ msgstr "Alle ansehen" #: public/js/frappe/form/toolbar.js:507 msgid "View Audit Trail" -msgstr "" +msgstr "Prüfprotokoll anzeigen" #: templates/includes/likes/likes.py:34 msgid "View Blog Post" @@ -33749,11 +33945,11 @@ msgstr "Virtuell" #: model/virtual_doctype.py:78 msgid "Virtual DocType {} requires a static method called {} found {}" -msgstr "" +msgstr "Virtueller DocType {} benötigt eine statische Methode namens {} gefunden {}" #: model/virtual_doctype.py:91 msgid "Virtual DocType {} requires overriding an instance method called {} found {}" -msgstr "" +msgstr "Der virtuelle DocType {} erfordert das Überschreiben einer Instanzmethode namens {} gefunden {}" #. Label of a Section Break field in DocType 'DocField' #: core/doctype/docfield/docfield.json @@ -33779,7 +33975,7 @@ msgstr "Besucher-ID" #: templates/discussions/reply_section.html:38 msgid "Want to discuss?" -msgstr "" +msgstr "Möchten Sie mitreden?" #. Option for the 'Address Type' (Select) field in DocType 'Address' #: contacts/doctype/address/address.json @@ -33801,7 +33997,7 @@ msgstr "Warnung: {0} kann in keiner Tabelle zu {1} gefunden werden" #: core/doctype/document_naming_rule/document_naming_rule.json msgctxt "Document Naming Rule" msgid "Warning: Updating counter may lead to document name conflicts if not done properly" -msgstr "" +msgstr "Warnung: Die Aktualisierung des Zählers kann zu Konflikten bei den Dokumentennamen führen, wenn sie nicht ordnungsgemäß durchgeführt wird." #: website/doctype/help_article/templates/help_article.html:24 msgid "Was this article helpful?" @@ -33815,7 +34011,7 @@ msgstr "Schau Video" #: desk/doctype/workspace/workspace.js:38 msgid "We do not allow editing of this document. Simply click the Edit button on the workspace page to make your workspace editable and customize it as you wish" -msgstr "" +msgstr "Wir erlauben keine Bearbeitung dieses Dokuments. Klicken Sie einfach auf die Schaltfläche Bearbeiten auf der Arbeitsbereichsseite, um Ihren Arbeitsbereich bearbeitbar zu machen und ihn nach Ihren Wünschen anzupassen." #: templates/emails/delete_data_confirmation.html:2 msgid "We have received a request for deletion of {0} data associated with: {1}" @@ -33867,7 +34063,7 @@ msgstr "Web-Formularfelder" #. Name of a DocType #: website/doctype/web_form_list_column/web_form_list_column.json msgid "Web Form List Column" -msgstr "" +msgstr "Web-Formular-Listenspalte" #. Name of a DocType #: website/doctype/web_page/web_page.json @@ -33936,7 +34132,7 @@ msgstr "Webvorlagenwerte" #: utils/jinja_globals.py:48 msgid "Web Template is not specified" -msgstr "" +msgstr "Web-Vorlage ist nicht angegeben" #. Label of a Section Break field in DocType 'DocType' #: core/doctype/doctype/doctype.json @@ -33981,7 +34177,7 @@ msgstr "Webhook Daten" #. Name of a DocType #: integrations/doctype/webhook_header/webhook_header.json msgid "Webhook Header" -msgstr "" +msgstr "Webhook-Kopfzeile" #. Label of a Section Break field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json @@ -33998,13 +34194,13 @@ msgstr "Webhook Anfrage" #. Name of a DocType #: integrations/doctype/webhook_request_log/webhook_request_log.json msgid "Webhook Request Log" -msgstr "" +msgstr "Webhook-Anfrage-Protokoll" #. Linked DocType in Webhook's connections #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Webhook Request Log" -msgstr "" +msgstr "Webhook-Anfrage-Protokoll" #. Label of a Password field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json @@ -34045,7 +34241,7 @@ msgstr "Webseite" #. Name of a report #: website/report/website_analytics/website_analytics.json msgid "Website Analytics" -msgstr "" +msgstr "Website-Analysen" #. Name of a role #: core/doctype/comment/comment.json @@ -34103,11 +34299,11 @@ msgstr "Webseiten-Skript" #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Website Search Field" -msgstr "" +msgstr "Website-Suchfeld" #: core/doctype/doctype/doctype.py:1473 msgid "Website Search Field must be a valid fieldname" -msgstr "" +msgstr "Website-Suchfeld muss ein gültiger Feldname sein" #. Name of a DocType #: website/doctype/website_settings/website_settings.json @@ -34386,7 +34582,7 @@ msgstr "Wenn diese Option aktiviert ist, können Gäste Dateien in Ihre Bewerbun #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "When uploading files, force the use of the web-based image capture. If this is unchecked, the default behavior is to use the mobile native camera when use from a mobile is detected." -msgstr "" +msgstr "Erzwingen Sie beim Hochladen von Dateien die Verwendung der webbasierten Bilderfassung. Falls diese Option nicht aktiviert ist, wird standardmäßig die native Kamera des Mobiltelefons verwendet, wenn eine Nutzung von einem Mobiltelefon aus erkannt wird." #: public/js/frappe/widgets/widget_dialog.js:440 msgid "Which view of the associated DocType should this shortcut take you to?" @@ -34475,43 +34671,43 @@ msgstr "Worflow-Stati existieren nicht" #: core/doctype/rq_worker/rq_worker.json msgctxt "RQ Worker" msgid "Worker Information" -msgstr "" +msgstr "Arbeitnehmerinformationen" #. Label of a Data field in DocType 'RQ Worker' #: core/doctype/rq_worker/rq_worker.json msgctxt "RQ Worker" msgid "Worker Name" -msgstr "" +msgstr "Name des Arbeiters" #. Name of a DocType #: workflow/doctype/workflow/workflow.json msgid "Workflow" -msgstr "" +msgstr "Workflow" #. Option for the 'Comment Type' (Select) field in DocType 'Comment' #: core/doctype/comment/comment.json msgctxt "Comment" msgid "Workflow" -msgstr "" +msgstr "Workflow" #. Option for the 'Comment Type' (Select) field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Workflow" -msgstr "" +msgstr "Workflow" #. Group in DocType's connections #. Linked DocType in DocType's connections #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Workflow" -msgstr "" +msgstr "Workflow" #. Label of a Link in the Build Workspace #: core/workspace/build/build.json msgctxt "Workflow" msgid "Workflow" -msgstr "" +msgstr "Workflow" #. Name of a DocType #: workflow/doctype/workflow_action/workflow_action.json @@ -34533,7 +34729,7 @@ msgstr "Workflow-Aktionsname" #. Name of a DocType #: workflow/doctype/workflow_action_permitted_role/workflow_action_permitted_role.json msgid "Workflow Action Permitted Role" -msgstr "" +msgstr "Zulässige Rolle für Workflow-Aktionen" #. Description of the 'Is Optional State' (Check) field in DocType 'Workflow #. Document State' @@ -34544,29 +34740,29 @@ msgstr "Für optionale Status wird keine Workflow-Aktion erstellt" #: workflow/page/workflow_builder/workflow_builder.js:4 msgid "Workflow Builder" -msgstr "" +msgstr "Workflow-Generator" #. Label of a Data field in DocType 'Workflow Document State' #: workflow/doctype/workflow_document_state/workflow_document_state.json msgctxt "Workflow Document State" msgid "Workflow Builder ID" -msgstr "" +msgstr "Workflow-Generator-ID" #. Label of a Data field in DocType 'Workflow Transition' #: workflow/doctype/workflow_transition/workflow_transition.json msgctxt "Workflow Transition" msgid "Workflow Builder ID" -msgstr "" +msgstr "Workflow-Generator-ID" #: workflow/doctype/workflow/workflow.js:11 msgid "Workflow Builder allows you to create workflows visually. You can drag and drop states and link them to create transitions. Also you can update thieir properties from the sidebar." -msgstr "" +msgstr "Mit dem Workflow-Generator können Sie Arbeitsabläufe visuell erstellen. Sie können Zustände per Drag-and-Drop verschieben und sie miteinander verknüpfen, um Übergänge zu erstellen. Außerdem können Sie ihre Eigenschaften über die Seitenleiste aktualisieren." #. Label of a JSON field in DocType 'Workflow' #: workflow/doctype/workflow/workflow.json msgctxt "Workflow" msgid "Workflow Data" -msgstr "" +msgstr "Workflow-Daten" #. Name of a DocType #: workflow/doctype/workflow_document_state/workflow_document_state.json @@ -34616,7 +34812,7 @@ msgstr "Workflow-Übergang" #. Description of the Onboarding Step 'Setup Approval Workflows' #: custom/onboarding_step/workflows/workflows.json msgid "Workflows allow you to define custom rules for the approval process of a particular document in ERPNext. You can also set complex Workflow Rules and set approval conditions." -msgstr "" +msgstr "Mit Workflows können Sie benutzerdefinierte Regeln für den Genehmigungsprozess eines bestimmten Dokuments in ERPNext festlegen. Sie können auch komplexe Workflow-Regeln aufstellen und Genehmigungsbedingungen festlegen." #. Name of a DocType #: desk/doctype/workspace/workspace.json @@ -34670,7 +34866,7 @@ msgstr "Arbeitsbereich-Nummernkarte" #. Name of a DocType #: desk/doctype/workspace_quick_list/workspace_quick_list.json msgid "Workspace Quick List" -msgstr "" +msgstr "Arbeitsbereich-Schnellliste" #. Name of a DocType #: desk/doctype/workspace_shortcut/workspace_shortcut.json @@ -34679,15 +34875,15 @@ msgstr "Arbeitsbereich-Verknüpfung" #: public/js/frappe/views/workspace/workspace.js:1265 msgid "Workspace {0} Created Successfully" -msgstr "" +msgstr "Arbeitsbereich {0} erfolgreich erstellt" #: public/js/frappe/views/workspace/workspace.js:894 msgid "Workspace {0} Deleted Successfully" -msgstr "" +msgstr "Arbeitsbereich {0} erfolgreich gelöscht" #: public/js/frappe/views/workspace/workspace.js:672 msgid "Workspace {0} Edited Successfully" -msgstr "" +msgstr "Arbeitsbereich {0} erfolgreich bearbeitet" #. Option for the 'View' (Select) field in DocType 'Form Tour' #: desk/doctype/form_tour/form_tour.json @@ -34893,7 +35089,7 @@ msgstr "Benutzer" #: public/js/frappe/form/footer/form_timeline.js:462 msgid "You Liked" -msgstr "" +msgstr "Ihnen gefällt" #: public/js/frappe/dom.js:425 msgid "You are connected to internet." @@ -34905,7 +35101,7 @@ msgstr "Sie haben keine Berechtigung, auf diesen Eintrag in {0} zuzugreifen, da #: permissions.py:406 msgid "You are not allowed to access this {0} record because it is linked to {1} '{2}' in row {3}, field {4}" -msgstr "" +msgstr "Sie können auf diesen Datensatz {0} nicht zugreifen, da er mit {1} '{2}' in Zeile {3}, Feld {4} verknüpft ist" #: public/js/frappe/views/kanban/kanban_board.bundle.js:69 msgid "You are not allowed to create columns" @@ -34921,7 +35117,7 @@ msgstr "Sie sind nicht berechtigt, eine Standard-Webseiten-Vorlage zu löschen" #: core/doctype/report/report.py:380 msgid "You are not allowed to edit the report." -msgstr "" +msgstr "Sie sind nicht berechtigt, den Bericht zu bearbeiten." #: permissions.py:614 msgid "You are not allowed to export {} doctype" @@ -34945,7 +35141,7 @@ msgstr "Sie sind nicht mit dem Internet verbunden. Versuchen Sie es später erne #: public/js/frappe/web_form/webform_script.js:22 msgid "You are not permitted to access this page without login." -msgstr "" +msgstr "Sie sind nicht berechtigt, ohne Anmeldung auf diese Seite zuzugreifen." #: www/app.py:25 msgid "You are not permitted to access this page." @@ -34965,7 +35161,7 @@ msgstr "Sie können nur die Reihenfolge verändern, keine Anwendungen entfernen #: email/doctype/email_account/email_account.js:221 msgid "You are selecting Sync Option as ALL, It will resync all read as well as unread message from server. This may also cause the duplication of Communication (emails)." -msgstr "" +msgstr "Sie wählen als Synchronisierungsoption „ALLE“ aus. Dadurch werden alle gelesenen und ungelesenen Nachrichten vom Server neu synchronisiert. Dies kann auch zu einer Duplizierung der Kommunikation (E-Mails) führen." #: public/js/frappe/form/footer/form_timeline.js:413 msgctxt "Form timeline" @@ -34990,7 +35186,7 @@ msgstr "Sie können diese {0} auch kopieren und in Ihren Browser einfügen" #: public/js/frappe/logtypes.js:21 msgid "You can change the retention policy from {0}." -msgstr "" +msgstr "Sie können die Aufbewahrungsrichtlinie unter {0} ändern." #: public/js/frappe/widgets/onboarding_widget.js:199 msgid "You can continue with the onboarding after exploring this page" @@ -35364,7 +35560,7 @@ msgstr "Null bedeutet, dass Sendeaufzeichnungen jederzeit aktualisiert werden" #: desk/doctype/desktop_icon/desktop_icon.json msgctxt "Desktop Icon" msgid "_doctype" -msgstr "" +msgstr "_doctype" #. Label of a Link field in DocType 'Desktop Icon' #: desk/doctype/desktop_icon/desktop_icon.json @@ -35374,11 +35570,11 @@ msgstr "_Bericht" #: utils/background_jobs.py:94 msgid "`job_id` paramater is required for deduplication." -msgstr "" +msgstr "Der Parameter `job_id` ist für die Deduplizierung erforderlich." #: public/js/frappe/form/footer/version_timeline_content_builder.js:219 msgid "added rows for {0}" -msgstr "" +msgstr "Zeilen für {0} hinzugefügt" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -35622,7 +35818,7 @@ msgstr "bereinigen" #: workflow/doctype/workflow_state/workflow_state.json msgctxt "Workflow State" msgid "cog" -msgstr "" +msgstr "cog" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -35646,7 +35842,7 @@ msgstr "türkis" #: public/js/frappe/utils/utils.js:1113 msgctxt "Days (Field: Duration)" msgid "d" -msgstr "" +msgstr "T" #. Option for the 'Indicator Color' (Select) field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json @@ -35719,7 +35915,7 @@ msgstr "herunterladen" #: workflow/doctype/workflow_state/workflow_state.json msgctxt "Workflow State" msgid "download-alt" -msgstr "" +msgstr "download-alt" #. Description of the 'Email Account Name' (Data) field in DocType 'Email #. Account' @@ -35979,7 +36175,7 @@ msgstr "grau" #: utils/backups.py:375 msgid "gzip not found in PATH! This is required to take a backup." -msgstr "" +msgstr "gzip nicht in PATH gefunden! Dies ist erforderlich, um ein Backup zu erstellen." #: public/js/frappe/utils/utils.js:1117 msgctxt "Hours (Field: Duration)" @@ -36342,7 +36538,7 @@ msgstr "eine(r/s) von" #: utils/data.py:1535 msgid "only." -msgstr "nur." +msgstr "." #: public/js/frappe/utils/utils.js:393 www/login.html:87 msgid "or" @@ -36515,7 +36711,7 @@ msgstr "Entfernen-Zeichen" #: public/js/frappe/form/footer/version_timeline_content_builder.js:221 msgid "removed rows for {0}" -msgstr "" +msgstr "entfernte Zeilen für {0}" #: model/rename_doc.py:217 msgid "renamed from {0} to {1}" @@ -36583,7 +36779,7 @@ msgstr "Straße" #: public/js/frappe/utils/utils.js:1125 msgctxt "Seconds (Field: Duration)" msgid "s" -msgstr "" +msgstr "Sek" #. Option for the 'Code challenge method' (Select) field in DocType 'OAuth #. Authorization Code' @@ -36721,21 +36917,21 @@ msgstr "halt" #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "string value, i.e. group" -msgstr "" +msgstr "String-Wert, z. B. Gruppe" #. Description of the 'LDAP Group Member attribute' (Data) field in DocType #. 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "string value, i.e. member" -msgstr "" +msgstr "String-Wert, z. B. Mitglied" #. Description of the 'Custom Group Search' (Data) field in DocType 'LDAP #. Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" msgid "string value, i.e. {0} or uid={0},ou=users,dc=example,dc=com" -msgstr "" +msgstr "String-Wert, z.B {0} oder uid={0},ou=users,dc=example,dc=com" #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' @@ -36786,19 +36982,19 @@ msgstr "Textbreite" #: workflow/doctype/workflow_state/workflow_state.json msgctxt "Workflow State" msgid "th" -msgstr "" +msgstr "th" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json msgctxt "Workflow State" msgid "th-large" -msgstr "" +msgstr "th-large" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json msgctxt "Workflow State" msgid "th-list" -msgstr "" +msgstr "th-list" #: public/js/frappe/form/controls/data.js:35 msgid "this form" @@ -36806,7 +37002,7 @@ msgstr "dieses Formular" #: tests/test_translate.py:158 msgid "this shouldn't break" -msgstr "" +msgstr "das sollte nicht kaputt gehen" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -36869,7 +37065,7 @@ msgstr "Werte durch Komma getrennt" #: core/doctype/audit_trail/audit_trail.json msgctxt "Audit Trail" msgid "version_table" -msgstr "" +msgstr "version_table" #: automation/doctype/assignment_rule/assignment_rule.py:386 msgid "via Assignment Rule" @@ -36918,7 +37114,7 @@ msgstr "Lautstärke erhöhen" #: templates/includes/oauth_confirmation.html:5 msgid "wants to access the following details from your account" -msgstr "" +msgstr "möchte auf folgende Details von Ihrem Konto zugreifen" #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -36931,7 +37127,7 @@ msgstr "Warnschild" #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" msgid "when clicked on element it will focus popover if present." -msgstr "" +msgstr "wenn Sie auf ein Element klicken, wird das Popover aktiviert, falls vorhanden." #. Option for the 'Icon' (Select) field in DocType 'Workflow State' #: workflow/doctype/workflow_state/workflow_state.json @@ -36977,25 +37173,25 @@ msgstr "verkleinern" #: desk/doctype/event/event.js:83 #: integrations/doctype/google_drive/google_drive.js:19 msgid "{0}" -msgstr "" +msgstr "{0}" #: public/js/frappe/ui/toolbar/search_utils.js:81 #: public/js/frappe/ui/toolbar/search_utils.js:82 msgid "{0} ${label}" -msgstr "" +msgstr "{0} ${label}" #: public/js/frappe/ui/toolbar/search_utils.js:177 msgid "{0} ${skip_list ? \"\" : type}" -msgstr "" +msgstr "{0} ${skip_list ? \"\" : type}" #: public/js/frappe/ui/toolbar/search_utils.js:182 msgid "{0} ${type}" -msgstr "" +msgstr "{0} ${type}" #: public/js/frappe/data_import/data_exporter.js:77 #: public/js/frappe/views/gantt/gantt_view.js:54 msgid "{0} ({1})" -msgstr "" +msgstr "{0} ({1})" #: public/js/frappe/data_import/data_exporter.js:76 msgid "{0} ({1}) (1 row mandatory)" @@ -37003,12 +37199,12 @@ msgstr "{0} ({1}) (1 Zeile obligatorisch)" #: public/js/frappe/views/gantt/gantt_view.js:53 msgid "{0} ({1}) - {2}%" -msgstr "" +msgstr "{0} ({1}) - {2}%" #: public/js/frappe/ui/toolbar/awesome_bar.js:346 #: public/js/frappe/ui/toolbar/awesome_bar.js:349 msgid "{0} = {1}" -msgstr "" +msgstr "{0} = {1}" #: public/js/frappe/views/calendar/calendar.js:29 msgid "{0} Calendar" @@ -37042,7 +37238,7 @@ msgstr "{0} Google-Kontakte synchronisiert" #: public/js/frappe/form/footer/form_timeline.js:463 msgid "{0} Liked" -msgstr "" +msgstr "{0} Gefällt mir" #: public/js/frappe/utils/utils.js:923 #: public/js/frappe/widgets/chart_widget.js:317 www/list.html:4 www/list.html:8 @@ -37051,7 +37247,7 @@ msgstr "{0} Liste" #: public/js/frappe/utils/pretty_date.js:37 msgid "{0} M" -msgstr "" +msgstr "{0} M" #: public/js/frappe/views/map/map_view.js:14 msgid "{0} Map" @@ -37063,11 +37259,11 @@ msgstr "{0} Module" #: public/js/frappe/form/quick_entry.js:113 msgid "{0} Name" -msgstr "" +msgstr "{0} ID" #: model/base_document.py:1027 msgid "{0} Not allowed to change {1} after submission from {2} to {3}" -msgstr "" +msgstr "{0} Es ist nicht erlaubt, {1} nach dem Buchen von {2} auf {3} zu ändern" #: public/js/frappe/utils/utils.js:920 #: public/js/frappe/widgets/chart_widget.js:325 @@ -37182,11 +37378,11 @@ msgstr "{0} hat folgende Werte geändert: {1} {2}" #: public/js/frappe/form/footer/version_timeline_content_builder.js:186 msgid "{0} changed the values for {1}" -msgstr "" +msgstr "{0} hat die Werte für {1} geändert" #: public/js/frappe/form/footer/version_timeline_content_builder.js:177 msgid "{0} changed the values for {1} {2}" -msgstr "" +msgstr "{0} hat die Werte für {1} {2} geändert" #: public/js/frappe/form/footer/form_timeline.js:443 msgctxt "Form timeline" @@ -37208,11 +37404,11 @@ msgstr "Von {0} erstellt" #: public/js/frappe/form/sidebar/review.js:154 msgid "{0} criticism point for {1}" -msgstr "" +msgstr "{0} Kritikpunkt für {1}" #: public/js/frappe/form/sidebar/review.js:156 msgid "{0} criticism points for {1}" -msgstr "" +msgstr "{0} Kritikpunkte für {1}" #: public/js/frappe/utils/energy_point_utils.js:41 msgid "{0} criticized on {1}" @@ -37234,7 +37430,7 @@ msgstr "{0} kritisiert {1}" #: public/js/frappe/utils/pretty_date.js:33 msgid "{0} d" -msgstr "" +msgstr "{0} d" #: public/js/frappe/utils/pretty_date.js:60 msgid "{0} days ago" @@ -37251,7 +37447,7 @@ msgstr "Feld {0} kann in {1} nicht als einzigartig gesetzt werden, da es nicht-e #: core/doctype/data_import/importer.py:1017 msgid "{0} format could not be determined from the values in this column. Defaulting to {1}." -msgstr "" +msgstr "Das {0} Format konnte nicht von den Werten in dieser Spalte bestimmt werden. Standard ist {1}." #: public/js/frappe/form/footer/version_timeline_content_builder.js:97 msgid "{0} from {1} to {2}" @@ -37397,11 +37593,11 @@ msgstr "{0} ist kein gültiger Workflow-Status. Bitte aktualisieren Sie Ihren Wo #: permissions.py:795 msgid "{0} is not a valid parent DocType for {1}" -msgstr "" +msgstr "{0} ist kein gültiger übergeordneter DocType für {1}" #: permissions.py:815 msgid "{0} is not a valid parentfield for {1}" -msgstr "" +msgstr "{0} ist kein gültiges übergeordnetes Feld für {1}" #: email/doctype/auto_email_report/auto_email_report.py:109 msgid "{0} is not a valid report format. Report format should one of the following {1}" @@ -37500,7 +37696,8 @@ msgstr "{0} muss einmalig sein" #: core/doctype/language/language.py:42 msgid "{0} must begin and end with a letter and can only contain letters,\n" "\t\t\t\thyphen or underscore." -msgstr "" +msgstr "{0} muss mit einem Buchstaben beginnen und enden und darf nur Buchstaben,\n" +"\t\t\t\tBindestriche oder Unterstriche enthalten." #: workflow/doctype/workflow/workflow.py:93 msgid "{0} not a valid State" @@ -37536,7 +37733,7 @@ msgstr "{0} Datensatz gelöscht" #: public/js/frappe/logtypes.js:22 msgid "{0} records are not automatically deleted." -msgstr "" +msgstr "{0} Datensätze werden nicht automatisch gelöscht." #: public/js/frappe/logtypes.js:29 msgid "{0} records are retained for {1} days." @@ -37596,7 +37793,7 @@ msgstr "{0} teilte dieses Dokument mit {1}" #: core/doctype/doctype/doctype.py:320 msgid "{0} should be indexed because it's referred in dashboard connections" -msgstr "" +msgstr "{0} sollte indiziert werden, da es in Dashboard-Verknüpfungen verwendet wird" #: automation/doctype/auto_repeat/auto_repeat.py:136 msgid "{0} should not be same as {1}" @@ -37644,7 +37841,7 @@ msgstr "Von {0} angesehen" #: public/js/frappe/utils/pretty_date.js:35 msgid "{0} w" -msgstr "" +msgstr "{0} W" #: public/js/frappe/utils/pretty_date.js:64 msgid "{0} weeks ago" @@ -37652,7 +37849,7 @@ msgstr "vor {0} Woche(n)" #: public/js/frappe/utils/pretty_date.js:39 msgid "{0} y" -msgstr "" +msgstr "{0} J" #: public/js/frappe/utils/pretty_date.js:72 msgid "{0} years ago" @@ -37798,7 +37995,7 @@ msgstr "{0}: {1} ist auf Status {2} festgelegt" #: public/js/frappe/views/reports/query_report.js:1190 msgid "{0}: {1} vs {2}" -msgstr "" +msgstr "{0}: {1} vs {2}" #: core/doctype/doctype/doctype.py:1385 msgid "{0}:Fieldtype {1} for {2} cannot be indexed" @@ -37838,7 +38035,7 @@ msgstr "{} Possibly invalid python code.
{}" #: core/doctype/log_settings/log_settings.py:54 msgid "{} does not support automated log clearing." -msgstr "" +msgstr "{} unterstützt keine automatische Protokolllöschung." #: core/doctype/audit_trail/audit_trail.py:40 msgid "{} field cannot be empty." From d89cec362c7bd2bdab0d999a81e138d53b9a7717 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 29 Jan 2024 16:46:42 +0530 Subject: [PATCH 28/95] fix: do not allow to set if_owner & report perm together --- .../page/permission_manager/permission_manager.js | 11 +++++++++++ .../page/permission_manager/permission_manager.py | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index f25ec6d4ad..308bbfedba 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -253,6 +253,10 @@ frappe.PermissionEngine = class PermissionEngine { if (!d.is_submittable && ["submit", "cancel", "amend"].includes(r)) return; if (d.in_create && ["create", "delete"].includes(r)) return; this.add_check(perm_container, d, r); + + if (d.if_owner && r == "report") { + perm_container.find("div[data-fieldname='report']").toggle(false); + } }); // buttons @@ -414,6 +418,13 @@ frappe.PermissionEngine = class PermissionEngine { chk.prop("checked", !chk.prop("checked")); } else { me.get_perm(args.role)[args.ptype] = args.value; + + if (args.ptype == "if_owner") { + let report_checkbox = chk + .closest("div.row") + .find("div[data-fieldname='report']"); + report_checkbox.toggle(!args.value); + } } }, }); diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 71d6a4a002..64c6e7b8bb 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -129,8 +129,15 @@ def update( frappe.clear_cache(doctype=doctype) frappe.only_for("System Manager") + + if ptype == "report" and value == "1" and if_owner == "1": + frappe.throw(_("Cannot set 'Report' permission if 'Only If Creator' permission is set")) + out = update_permission_property(doctype, role, permlevel, ptype, value, if_owner=if_owner) + if ptype == "if_owner" and value == "1": + update_permission_property(doctype, role, permlevel, "report", "0", if_owner=value) + frappe.db.after_commit.add(clear_cache) return "refresh" if out else None From 4dce9d0d3711c74cff05c15f1f8eb2bb2e00da5d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 29 Jan 2024 16:47:04 +0530 Subject: [PATCH 29/95] ci: skip UI test for PO files (#24581) --- .github/helper/roulette.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/helper/roulette.py b/.github/helper/roulette.py index e3b212fa89..ebca901c95 100644 --- a/.github/helper/roulette.py +++ b/.github/helper/roulette.py @@ -73,8 +73,9 @@ def has_label(pr_number, label, repo="frappe/frappe"): ) -def is_py(file): - return file.endswith("py") +def is_server_side_code(file): + """File exclusively affects server side code""" + return file.endswith("py") or file.endswith(".po") def is_ci(file): @@ -112,7 +113,7 @@ if __name__ == "__main__": ci_files_changed = any(f for f in files_list if is_ci(f)) only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list) only_frontend_code_changed = len(list(filter(is_frontend_code, files_list))) == len(files_list) - updated_py_file_count = len(list(filter(is_py, files_list))) + updated_py_file_count = len(list(filter(is_server_side_code, files_list))) only_py_changed = updated_py_file_count == len(files_list) if has_skip_ci_label(pr_number, repo): From 0973e20c19a1db7c64ca40b8cd268157c6fbf46c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 29 Jan 2024 17:09:00 +0530 Subject: [PATCH 30/95] revert: added back quick list status indicator --- frappe/public/js/frappe/widgets/quick_list_widget.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/public/js/frappe/widgets/quick_list_widget.js b/frappe/public/js/frappe/widgets/quick_list_widget.js index f3b22158f1..40bac6d967 100644 --- a/frappe/public/js/frappe/widgets/quick_list_widget.js +++ b/frappe/public/js/frappe/widgets/quick_list_widget.js @@ -133,6 +133,8 @@ export default class QuickListWidget extends Widget { } setup_quick_list_item(doc) { + const indicator = frappe.get_indicator(doc, this.document_type); + let $quick_list_item = $(`
@@ -147,6 +149,14 @@ export default class QuickListWidget extends Widget {
`); + if (indicator) { + $(` +
+ ${__(indicator[0])} +
+ `).appendTo($quick_list_item); + } + $(`
${frappe.utils.icon("right", "xs")}
`).appendTo( $quick_list_item ); From f48ec5b6905b12cb90b85af389a0df7645026e24 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 25 Jan 2024 16:28:26 +0530 Subject: [PATCH 31/95] refactor(module_map): fetch only installed apps if a site is initialized Signed-off-by: Akhil Narang --- frappe/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 289e5c6e3d..452ddc9332 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -261,12 +261,14 @@ def init(site: str, sites_path: str = ".", new_site: bool = False, force=False) local.qb = get_query_builder(local.conf.db_type) local.qb.get_query = get_query setup_redis_cache_connection() - setup_module_map() if not _qb_patched.get(local.conf.db_type): patch_query_execute() patch_query_aggregation() + if site: + setup_module_map() + local.initialised = True # Set the user as database name if not set in config @@ -1644,7 +1646,8 @@ def setup_module_map(): if not (local.app_modules and local.module_app): local.module_app, local.app_modules = {}, {} - for app in get_all_apps(with_internal_apps=True): + + for app in get_installed_apps(_ensure_on_bench=True): local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) From f967aa95084425fdd83b889069e1fa12714ca85e Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 25 Jan 2024 16:32:16 +0530 Subject: [PATCH 32/95] feat: print a warning if module names conflict Signed-off-by: Akhil Narang --- frappe/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/__init__.py b/frappe/__init__.py index 452ddc9332..3ccc6c87fc 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1651,6 +1651,9 @@ def setup_module_map(): local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) + if module in local.module_app: + print(f"WARNING: module `{module}` found in apps `{local.module_app[module]}` and `{app}`") + local.module_app[module] = app local.app_modules[app].append(module) From db72dd37aa0369cf576cba2e6d94572b4ad93c1d Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Mon, 29 Jan 2024 17:25:18 +0530 Subject: [PATCH 33/95] refactor: fallback to all apps on bench when site isn't ready yet Signed-off-by: Akhil Narang --- frappe/__init__.py | 33 +++++++++++++++++++++++++++------ frappe/commands/site.py | 7 +++---- frappe/installer.py | 4 ++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 3ccc6c87fc..ec55af2081 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -202,8 +202,19 @@ if TYPE_CHECKING: # pragma: no cover # end: static analysis hack -def init(site: str, sites_path: str = ".", new_site: bool = False, force=False) -> None: - """Initialize frappe for the current site. Reset thread locals `frappe.local`""" +def init( + site: str, sites_path: str = ".", new_site: bool = False, force=False, site_ready: bool = True +) -> None: + """ + Initialize frappe for the current site. Reset thread locals `frappe.local` + + :param site: Site name. + :param sites_path: Path to sites directory. + :param new_site: Sets a flag to indicate a new site. + :param force: Force initialization if already previously run. + :param site_ready: Any init during site installation should set this to False. + + """ if getattr(local, "initialised", None) and not force: return @@ -267,7 +278,7 @@ def init(site: str, sites_path: str = ".", new_site: bool = False, force=False) patch_query_aggregation() if site: - setup_module_map() + setup_module_map(site_ready) local.initialised = True @@ -1638,8 +1649,13 @@ def append_hook(target, key, value): target[key].extend(value) -def setup_module_map(): - """Rebuild map of all modules (internal).""" +def setup_module_map(site_ready: bool = True): + """ + Rebuild map of all modules (internal). + + :param site_ready: If the site isn't fully ready yet - install is still going on, we can't + fetch apps from site DB. Fallback to fetching all apps on bench for module map temporarily. + """ if conf.db_name: local.app_modules = cache.get_value("app_modules") local.module_app = cache.get_value("module_app") @@ -1647,7 +1663,12 @@ def setup_module_map(): if not (local.app_modules and local.module_app): local.module_app, local.app_modules = {}, {} - for app in get_installed_apps(_ensure_on_bench=True): + if site_ready: + apps = get_installed_apps(_ensure_on_bench=True) + else: + apps = get_all_apps() + + for app in apps: local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 3b6fcb32f1..da6509e219 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -76,7 +76,7 @@ def new_site( "Create a new site" from frappe.installer import _new_site - frappe.init(site=site, new_site=True) + frappe.init(site=site, new_site=True, site_ready=False) _new_site( db_name, @@ -417,7 +417,7 @@ def _reinstall( if not yes: click.confirm("This will wipe your database. Are you sure you want to reinstall?", abort=True) try: - frappe.init(site=site) + frappe.init(site=site, site_ready=False) frappe.connect() frappe.clear_cache() installed = frappe.get_installed_apps() @@ -429,7 +429,7 @@ def _reinstall( frappe.db.close() frappe.destroy() - frappe.init(site=site) + frappe.init(site=site, site_ready=False) _new_site( frappe.conf.db_name, @@ -726,7 +726,6 @@ def disable_user(context, email): @pass_context def migrate(context, skip_failing=False, skip_search_index=False): "Run patches, sync schema and rebuild files/translations" - from traceback_with_variables import activate_by_import from frappe.migrate import SiteMigration diff --git a/frappe/installer.py b/frappe/installer.py index 2f7138cb48..1a4e8cbbb4 100644 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -64,7 +64,7 @@ def _new_site( print("--no-mariadb-socket requires db_type to be set to mariadb.") sys.exit(1) - frappe.init(site=site) + frappe.init(site=site, site_ready=False) if not db_name: import hashlib @@ -557,7 +557,7 @@ def make_conf( ) sites_path = frappe.local.sites_path frappe.destroy() - frappe.init(site, sites_path=sites_path) + frappe.init(site, sites_path=sites_path, site_ready=False) def make_site_config( From bcca1da8d841e985cbe304cf9cbb99e73475bbe4 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 29 Jan 2024 13:25:45 +0100 Subject: [PATCH 34/95] fix: Extract header/footer html into `content` to simplify things - Extract header/footer html into `content` first, then remove any instances from the main content since it is already rendered via the extracted header/footer html --- frappe/utils/pdf.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 321c65a73c..be25b8cd48 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -238,7 +238,13 @@ def prepare_header_footer(soup: BeautifulSoup): # extract header and footer for html_id in ("header-html", "footer-html"): - if content := soup.find(id=html_id): + if content := soup.find(id=html_id).extract(): + # `header/footer-html` are extracted, rendered as html + # and passed in wkhtmltopdf options (as '--header/footer-html') + # Remove instances of them from main content for render_template + for tag in soup.find_all(id=html_id): + tag.extract() + toggle_visible_pdf(content) id_map = {"header-html": "pdf_header_html", "footer-html": "pdf_footer_html"} hook_func = frappe.get_hooks(id_map.get(html_id)) @@ -251,10 +257,6 @@ def prepare_header_footer(soup: BeautifulSoup): css=css, ) - # there could be multiple instances of header-html/footer-html - for tag in soup.find_all(id=html_id): - tag.extract() - # create temp file fname = os.path.join("/tmp", f"frappe-pdf-{frappe.generate_hash()}.html") with open(fname, "wb") as f: From 1cce6588ee97e1581437088508aaa436b9fe49d9 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:08:11 +0530 Subject: [PATCH 35/95] fix: sentry minor fix (#24588) --- frappe/desk/doctype/event/event.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/event/event.js b/frappe/desk/doctype/event/event.js index 299cbe5cc3..ef2b2eb7e1 100644 --- a/frappe/desk/doctype/event/event.js +++ b/frappe/desk/doctype/event/event.js @@ -44,9 +44,13 @@ frappe.ui.form.on("Event", { const [ends_on_date] = frm.doc.ends_on ? frm.doc.ends_on.split(" ") - : frm.doc.starts_on.split(" "); + : frm.doc.starts_on?.split(" ") || []; - if (frm.doc.google_meet_link && frappe.datetime.now_date() <= ends_on_date) { + if ( + ends_on_date && + frm.doc.google_meet_link && + frappe.datetime.now_date() <= ends_on_date + ) { frm.dashboard.set_headline( __("Join video conference with {0}", [ `Google Meet`, From 3bea50d519b03b8003b56859b8c6846482c1742d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 29 Jan 2024 19:48:19 +0530 Subject: [PATCH 36/95] fix: Return empty result if no perm level access (#24591) --- frappe/model/db_query.py | 4 ++++ frappe/tests/test_db_query.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 1957fb40f7..11054a9a7a 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -217,6 +217,10 @@ class DatabaseQuery: args = self.prepare_args() args.limit = self.add_limit() + if not args.fields: + # apply_fieldlevel_read_permissions has likely removed ALL the fields that user asked for + return [] + if args.conditions: args.conditions = "where " + args.conditions diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 87f044ff39..178d80e29f 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -1255,6 +1255,9 @@ class TestReportView(FrappeTestCase): response = execute_cmd("frappe.desk.reportview.get") self.assertNotIn("published", response["keys"]) + # If none of the fields are accessible then result should be empty + self.assertEqual(frappe.get_list("Blog Post", "published"), []) + def test_reportview_get_admin(self): # Admin should be able to see access all fields with setup_patched_blog_post(): From d2d1b14cc64adbe251424a0ac0fe9ab20cef7b96 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 29 Jan 2024 19:41:53 +0530 Subject: [PATCH 37/95] fix: Always allow meta/standard fields in perm level Perm levels CANNOT applied on these fields so these should ALWAYS be allowed. --- frappe/model/__init__.py | 8 ++++---- frappe/tests/test_model_utils.py | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 2dad3df4fd..d3d120c872 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -230,6 +230,9 @@ def get_permitted_fields( if permission_type is None: permission_type = "select" if frappe.only_has_select_perm(doctype, user=user) else "read" + meta_fields = meta.default_fields.copy() + optional_meta_fields = [x for x in optional_fields if x in valid_columns] + if permitted_fields := meta.get_permitted_fieldnames( parenttype=parenttype, user=user, @@ -239,15 +242,12 @@ def get_permitted_fields( if permission_type == "select": return permitted_fields - meta_fields = meta.default_fields.copy() - optional_meta_fields = [x for x in optional_fields if x in valid_columns] - if meta.istable: meta_fields.extend(child_table_fields) return meta_fields + permitted_fields + optional_meta_fields - return [] + return meta_fields + optional_meta_fields def is_default_field(fieldname: str) -> bool: diff --git a/frappe/tests/test_model_utils.py b/frappe/tests/test_model_utils.py index 61828ef500..72f5776808 100644 --- a/frappe/tests/test_model_utils.py +++ b/frappe/tests/test_model_utils.py @@ -36,10 +36,10 @@ class TestModelUtils(FrappeTestCase): todo_all_columns = frappe.get_meta("ToDo").get_valid_columns() self.assertListEqual(todo_all_fields, todo_all_columns) - # Guest should have access to no fields in ToDo + # Guest should have access to no non-std fields in ToDo with set_user("Guest"): guest_permitted_fields = get_permitted_fields("ToDo") - self.assertEqual(guest_permitted_fields, []) + self.assertNotIn("description", guest_permitted_fields) # everyone should have access to all fields of core doctypes with set_user("Guest"): @@ -55,15 +55,14 @@ class TestModelUtils(FrappeTestCase): "Installed Application", parenttype="Installed Applications" ) child_all_fields = frappe.get_meta("Installed Application").get_valid_columns() - self.assertEqual(without_parent_fields, []) self.assertLess(len(without_parent_fields), len(with_parent_fields)) self.assertSequenceEqual(set(with_parent_fields), set(child_all_fields)) - # guest has access to no fields + # guest has access to no non-std fields with set_user("Guest"): - self.assertEqual(get_permitted_fields("Installed Application"), []) - self.assertEqual( - get_permitted_fields("Installed Application", parenttype="Installed Applications"), [] + self.assertNotIn("app_name", get_permitted_fields("Installed Application")) + self.assertNotIn( + "app_name", get_permitted_fields("Installed Application", parenttype="Installed Applications") ) def test_is_default_field(self): From a378c8bee2fcd9746f7638192a097f4292a445e0 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Jan 2024 03:49:32 +0100 Subject: [PATCH 38/95] chore: regenerate pot file (#24600) --- frappe/locale/main.pot | 991 ++++++++++++++++++++++------------------- 1 file changed, 535 insertions(+), 456 deletions(-) diff --git a/frappe/locale/main.pot b/frappe/locale/main.pot index 863603eb01..ae11132373 100644 --- a/frappe/locale/main.pot +++ b/frappe/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Frappe Framework VERSION\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" -"POT-Creation-Date: 2024-01-12 01:53+0053\n" -"PO-Revision-Date: 2024-01-12 01:53+0053\n" +"POT-Creation-Date: 2024-01-29 18:10+0053\n" +"PO-Revision-Date: 2024-01-29 18:10+0053\n" "Last-Translator: developers@frappe.io\n" "Language-Team: developers@frappe.io\n" "MIME-Version: 1.0\n" @@ -188,7 +188,7 @@ msgstr "" msgid "'In Global Search' is not allowed for field {0} of type {1}" msgstr "" -#: core/doctype/doctype/doctype.py:1305 +#: core/doctype/doctype/doctype.py:1301 msgid "'In Global Search' not allowed for type {0} in row {1}" msgstr "" @@ -208,7 +208,7 @@ msgstr "" msgid "'{0}' is not a valid URL" msgstr "" -#: core/doctype/doctype/doctype.py:1299 +#: core/doctype/doctype/doctype.py:1295 msgid "'{0}' not allowed for type {1} in row {2}" msgstr "" @@ -229,7 +229,7 @@ msgctxt "Web Page" msgid "0 is highest" msgstr "" -#: public/js/frappe/form/grid_row.js:786 +#: public/js/frappe/form/grid_row.js:806 msgid "1 = True & 0 = False" msgstr "" @@ -253,7 +253,7 @@ msgstr "" msgid "1 comment" msgstr "" -#: tests/test_utils.py:647 +#: tests/test_utils.py:648 msgid "1 day ago" msgstr "" @@ -261,15 +261,15 @@ msgstr "" msgid "1 hour" msgstr "" -#: public/js/frappe/utils/pretty_date.js:52 tests/test_utils.py:645 +#: public/js/frappe/utils/pretty_date.js:52 tests/test_utils.py:646 msgid "1 hour ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:48 tests/test_utils.py:643 +#: public/js/frappe/utils/pretty_date.js:48 tests/test_utils.py:644 msgid "1 minute ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:66 tests/test_utils.py:651 +#: public/js/frappe/utils/pretty_date.js:66 tests/test_utils.py:652 msgid "1 month ago" msgstr "" @@ -277,35 +277,35 @@ msgstr "" msgid "1 record will be exported" msgstr "" -#: tests/test_utils.py:642 +#: tests/test_utils.py:643 msgid "1 second ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:62 tests/test_utils.py:649 +#: public/js/frappe/utils/pretty_date.js:62 tests/test_utils.py:650 msgid "1 week ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:70 tests/test_utils.py:653 +#: public/js/frappe/utils/pretty_date.js:70 tests/test_utils.py:654 msgid "1 year ago" msgstr "" -#: tests/test_utils.py:646 +#: tests/test_utils.py:647 msgid "2 hours ago" msgstr "" -#: tests/test_utils.py:652 +#: tests/test_utils.py:653 msgid "2 months ago" msgstr "" -#: tests/test_utils.py:650 +#: tests/test_utils.py:651 msgid "2 weeks ago" msgstr "" -#: tests/test_utils.py:654 +#: tests/test_utils.py:655 msgid "2 years ago" msgstr "" -#: tests/test_utils.py:644 +#: tests/test_utils.py:645 msgid "3 minutes ago" msgstr "" @@ -321,7 +321,7 @@ msgstr "" msgid "5 Records" msgstr "" -#: tests/test_utils.py:648 +#: tests/test_utils.py:649 msgid "5 days ago" msgstr "" @@ -343,7 +343,7 @@ msgctxt "Document Naming Rule Condition" msgid "<=" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:564 +#: public/js/frappe/widgets/widget_dialog.js:570 msgid "{0} is not a valid URL" msgstr "" @@ -715,7 +715,7 @@ msgstr "" msgid "A DocType (Document Type) is used to insert forms in ERPNext. Forms such as Customer, Orders, and Invoices are Doctypes in the backend. You can also create new DocTypes to create new forms in ERPNext as per your business needs." msgstr "" -#: core/doctype/doctype/doctype.py:1015 +#: core/doctype/doctype/doctype.py:1011 msgid "A DocType's name should start with a letter and can only consist of letters, numbers, spaces, underscores and hyphens" msgstr "" @@ -983,7 +983,7 @@ msgctxt "Social Login Key" msgid "Access Token URL" msgstr "" -#: auth.py:444 +#: auth.py:455 msgid "Access not allowed from this IP Address" msgstr "" @@ -1001,7 +1001,7 @@ msgstr "" #. Name of a role #: automation/doctype/auto_repeat/auto_repeat.json -#: contacts/doctype/contact/contact.json +#: contacts/doctype/contact/contact.json geo/doctype/currency/currency.json msgid "Accounts Manager" msgstr "" @@ -1202,9 +1202,9 @@ msgstr "" #: core/page/permission_manager/permission_manager.js:465 #: email/doctype/email_group/email_group.js:60 -#: public/js/frappe/form/grid_row.js:468 +#: public/js/frappe/form/grid_row.js:469 #: public/js/frappe/form/sidebar/assign_to.js:100 -#: public/js/frappe/list/bulk_operations.js:372 +#: public/js/frappe/list/bulk_operations.js:393 #: public/js/frappe/views/dashboard/dashboard_view.js:440 #: public/js/frappe/views/reports/query_report.js:265 #: public/js/frappe/views/reports/query_report.js:293 @@ -1283,7 +1283,7 @@ msgid "Add Custom Tags" msgstr "" #: public/js/frappe/widgets/widget_dialog.js:159 -#: public/js/frappe/widgets/widget_dialog.js:683 +#: public/js/frappe/widgets/widget_dialog.js:689 msgid "Add Filters" msgstr "" @@ -1346,16 +1346,16 @@ msgstr "" msgid "Add Subscribers" msgstr "" -#: public/js/frappe/list/bulk_operations.js:360 +#: public/js/frappe/list/bulk_operations.js:381 msgid "Add Tags" msgstr "" -#: public/js/frappe/list/list_view.js:1834 +#: public/js/frappe/list/list_view.js:1858 msgctxt "Button in list view actions menu" msgid "Add Tags" msgstr "" -#: public/js/frappe/views/communication.js:320 +#: public/js/frappe/views/communication.js:362 msgid "Add Template" msgstr "" @@ -1381,7 +1381,7 @@ msgctxt "Event" msgid "Add Video Conferencing" msgstr "" -#: public/js/frappe/form/form_tour.js:203 +#: public/js/frappe/form/form_tour.js:205 msgid "Add a Row" msgstr "" @@ -1557,7 +1557,7 @@ msgstr "" msgid "Addresses And Contacts" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:536 +#: public/js/frappe/ui/toolbar/search_utils.js:552 msgid "Administration" msgstr "" @@ -1604,8 +1604,8 @@ msgctxt "User Permission" msgid "Advanced Control" msgstr "" -#: public/js/frappe/form/controls/link.js:315 -#: public/js/frappe/form/controls/link.js:317 +#: public/js/frappe/form/controls/link.js:316 +#: public/js/frappe/form/controls/link.js:318 msgid "Advanced Search" msgstr "" @@ -2398,7 +2398,7 @@ msgstr "" msgid "App not found for module: {0}" msgstr "" -#: __init__.py:1677 +#: __init__.py:1731 msgid "App {0} is not installed" msgstr "" @@ -2477,7 +2477,7 @@ msgctxt "Property Setter" msgid "Applied On" msgstr "" -#: public/js/frappe/list/list_view.js:1819 +#: public/js/frappe/list/list_view.js:1843 msgctxt "Button in list view actions menu" msgid "Apply Assignment Rule" msgstr "" @@ -2579,11 +2579,15 @@ msgstr "" msgid "Archived Columns" msgstr "" +#: public/js/frappe/list/list_view.js:1822 +msgid "Are you sure you want to clear the assignments?" +msgstr "" + #: public/js/frappe/form/grid.js:269 msgid "Are you sure you want to delete all rows?" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:885 +#: public/js/frappe/views/workspace/workspace.js:891 msgid "Are you sure you want to delete page {0}?" msgstr "" @@ -2765,10 +2769,6 @@ msgctxt "Assignment Rule" msgid "Assignment Days" msgstr "" -#: automation/doctype/assignment_rule/assignment_rule.py:64 -msgid "Assignment Day{0} {1} has been repeated." -msgstr "" - #. Name of a DocType #: automation/doctype/assignment_rule/assignment_rule.json msgid "Assignment Rule" @@ -2803,7 +2803,7 @@ msgstr "" msgid "Assignment Rule User" msgstr "" -#: automation/doctype/assignment_rule/assignment_rule.py:53 +#: automation/doctype/assignment_rule/assignment_rule.py:54 msgid "Assignment Rule is not allowed on {0} document type" msgstr "" @@ -2835,16 +2835,16 @@ msgctxt "Notification Settings" msgid "Assignments" msgstr "" -#: public/js/frappe/form/grid_row.js:629 +#: public/js/frappe/form/grid_row.js:649 msgid "At least one column is required to show in the grid." msgstr "" #: website/doctype/web_form/web_form.js:64 -msgid "Atleast one field is required in Web Form Fields Table" +msgid "At least one field is required in Web Form Fields Table" msgstr "" #: core/doctype/data_export/data_export.js:44 -msgid "Atleast one field of Parent Document Type is mandatory" +msgid "At least one field of Parent Document Type is mandatory" msgstr "" #: public/js/frappe/form/controls/attach.js:5 @@ -3252,7 +3252,7 @@ msgctxt "Email Account" msgid "Auto Reply Message" msgstr "" -#: automation/doctype/assignment_rule/assignment_rule.py:179 +#: automation/doctype/assignment_rule/assignment_rule.py:176 msgid "Auto assignment failed: {0}" msgstr "" @@ -3327,11 +3327,11 @@ msgctxt "User" msgid "Automatic" msgstr "" -#: email/doctype/email_account/email_account.py:675 +#: email/doctype/email_account/email_account.py:677 msgid "Automatic Linking can be activated only for one Email Account." msgstr "" -#: email/doctype/email_account/email_account.py:670 +#: email/doctype/email_account/email_account.py:672 msgid "Automatic Linking can be activated only if Incoming is enabled." msgstr "" @@ -3543,7 +3543,11 @@ msgctxt "System Settings" msgid "Background Workers" msgstr "" -#: integrations/doctype/google_drive/google_drive.js:31 +#: integrations/doctype/google_drive/google_drive.py:173 +msgid "Backing up Data." +msgstr "" + +#: integrations/doctype/google_drive/google_drive.js:32 msgid "Backing up to Google Drive." msgstr "" @@ -4142,11 +4146,11 @@ msgstr "" msgid "Bulk Delete" msgstr "" -#: public/js/frappe/list/bulk_operations.js:256 +#: public/js/frappe/list/bulk_operations.js:277 msgid "Bulk Edit" msgstr "" -#: public/js/frappe/form/grid.js:1151 +#: public/js/frappe/form/grid.js:1153 msgid "Bulk Edit {0}" msgstr "" @@ -4453,7 +4457,7 @@ msgstr "" msgid "Camera" msgstr "" -#: public/js/frappe/utils/utils.js:1711 +#: public/js/frappe/utils/utils.js:1712 #: website/report/website_analytics/website_analytics.js:39 msgid "Campaign" msgstr "" @@ -4480,7 +4484,7 @@ msgstr "" msgid "Can not rename as column {0} is already present on DocType." msgstr "" -#: core/doctype/doctype/doctype.py:1114 +#: core/doctype/doctype/doctype.py:1110 msgid "Can only change to/from Autoincrement naming rule when there is no data in the doctype" msgstr "" @@ -4495,12 +4499,12 @@ msgstr "" msgid "Can't rename {0} to {1} because {0} doesn't exist." msgstr "" -#: core/doctype/doctype/doctype_list.js:113 +#: core/doctype/doctype/doctype_list.js:130 #: public/js/frappe/form/reminders.js:54 msgid "Cancel" msgstr "" -#: public/js/frappe/list/list_view.js:1889 +#: public/js/frappe/list/list_view.js:1913 msgctxt "Button in list view actions menu" msgid "Cancel" msgstr "" @@ -4553,7 +4557,7 @@ msgstr "" msgid "Cancel Scheduling" msgstr "" -#: public/js/frappe/list/list_view.js:1894 +#: public/js/frappe/list/list_view.js:1918 msgctxt "Title of confirmation dialog" msgid "Cancel {0} documents?" msgstr "" @@ -4622,7 +4626,7 @@ msgstr "" msgid "Cannot Remove" msgstr "" -#: model/base_document.py:1034 +#: model/base_document.py:1053 msgid "Cannot Update After Submit" msgstr "" @@ -4638,7 +4642,7 @@ msgstr "" msgid "Cannot cancel before submitting. See Transition {0}" msgstr "" -#: public/js/frappe/list/bulk_operations.js:229 +#: public/js/frappe/list/bulk_operations.js:250 msgid "Cannot cancel {0}." msgstr "" @@ -4658,7 +4662,7 @@ msgstr "" msgid "Cannot change state of Cancelled Document. Transition row {0}" msgstr "" -#: core/doctype/doctype/doctype.py:1104 +#: core/doctype/doctype/doctype.py:1100 msgid "Cannot change to/from autoincrement autoname in Customize Form" msgstr "" @@ -4666,7 +4670,7 @@ msgstr "" msgid "Cannot create a {0} against a child document: {1}" msgstr "" -#: desk/doctype/workspace/workspace.py:250 +#: desk/doctype/workspace/workspace.py:254 msgid "Cannot create private workspace of other users" msgstr "" @@ -4678,11 +4682,11 @@ msgstr "" msgid "Cannot delete or cancel because {0} {1} is linked with {2} {3} {4}" msgstr "" -#: desk/doctype/workspace/workspace.py:417 +#: desk/doctype/workspace/workspace.py:423 msgid "Cannot delete private workspace of other users" msgstr "" -#: desk/doctype/workspace/workspace.py:410 +#: desk/doctype/workspace/workspace.py:416 msgid "Cannot delete public workspace without Workspace Manager role" msgstr "" @@ -4786,16 +4790,16 @@ msgstr "" msgid "Cannot share {0} with submit permission as the doctype {1} is not submittable" msgstr "" -#: public/js/frappe/list/bulk_operations.js:226 +#: public/js/frappe/list/bulk_operations.js:247 msgid "Cannot submit {0}." msgstr "" -#: desk/doctype/workspace/workspace.py:351 +#: desk/doctype/workspace/workspace.py:357 msgid "Cannot update private workspace of other users" msgstr "" #: desk/doctype/bulk_update/bulk_update.js:26 -#: public/js/frappe/list/bulk_operations.js:301 +#: public/js/frappe/list/bulk_operations.js:322 msgid "Cannot update {0}" msgstr "" @@ -4807,7 +4811,7 @@ msgstr "" msgid "Cannot use {0} in order/group by" msgstr "" -#: public/js/frappe/list/bulk_operations.js:232 +#: public/js/frappe/list/bulk_operations.js:253 msgid "Cannot {0} {1}." msgstr "" @@ -4835,7 +4839,7 @@ msgstr "" msgid "Card Label" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:227 +#: public/js/frappe/widgets/widget_dialog.js:233 msgid "Card Links" msgstr "" @@ -5133,11 +5137,11 @@ msgctxt "Form Tour Step" msgid "Child Doctype" msgstr "" -#: core/doctype/doctype/doctype.py:1588 +#: core/doctype/doctype/doctype.py:1584 msgid "Child Table {0} for field {1}" msgstr "" -#: core/doctype/doctype/doctype_list.js:37 +#: core/doctype/doctype/doctype_list.js:52 msgid "Child Tables are shown as a Grid in other DocTypes" msgstr "" @@ -5147,11 +5151,11 @@ msgctxt "DocType" msgid "Child Tables are shown as a Grid in other DocTypes" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:614 +#: public/js/frappe/widgets/widget_dialog.js:620 msgid "Choose Existing Card or create New Card" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1385 +#: public/js/frappe/views/workspace/workspace.js:1391 msgid "Choose a block or continue typing" msgstr "" @@ -5186,7 +5190,7 @@ msgstr "" msgid "Clear" msgstr "" -#: public/js/frappe/views/communication.js:325 +#: public/js/frappe/views/communication.js:367 msgid "Clear & Add Template" msgstr "" @@ -5194,6 +5198,11 @@ msgstr "" msgid "Clear & Add template" msgstr "" +#: public/js/frappe/list/list_view.js:1819 +msgctxt "Button in list view actions menu" +msgid "Clear Assignment" +msgstr "" + #: public/js/frappe/ui/keyboard.js:275 msgid "Clear Cache and Reload" msgstr "" @@ -5212,7 +5221,7 @@ msgstr "" msgid "Clear User Permissions" msgstr "" -#: public/js/frappe/views/communication.js:326 +#: public/js/frappe/views/communication.js:368 msgid "Clear the email message and add the template" msgstr "" @@ -5228,7 +5237,7 @@ msgstr "" msgid "Click here to verify" msgstr "" -#: integrations/doctype/google_drive/google_drive.js:46 +#: integrations/doctype/google_drive/google_drive.js:47 msgid "Click on Authorize Google Drive Access to authorize Google Drive Access." msgstr "" @@ -5465,7 +5474,7 @@ msgctxt "OAuth Authorization Code" msgid "Code challenge method" msgstr "" -#: public/js/frappe/form/form_tour.js:268 +#: public/js/frappe/form/form_tour.js:270 #: public/js/frappe/widgets/base_widget.js:157 msgid "Collapse" msgstr "" @@ -5517,8 +5526,8 @@ msgstr "" #. Name of a DocType #: public/js/frappe/views/reports/query_report.js:1140 -#: public/js/frappe/widgets/widget_dialog.js:505 -#: public/js/frappe/widgets/widget_dialog.js:657 +#: public/js/frappe/widgets/widget_dialog.js:511 +#: public/js/frappe/widgets/widget_dialog.js:663 #: website/doctype/color/color.json msgid "Color" msgstr "" @@ -5665,7 +5674,7 @@ msgstr "" msgid "Column Name cannot be empty" msgstr "" -#: public/js/frappe/form/grid_row.js:593 +#: public/js/frappe/form/grid_row.js:613 msgid "Column width cannot be zero." msgstr "" @@ -6332,7 +6341,7 @@ msgctxt "Web Page" msgid "Content Type" msgstr "" -#: desk/doctype/workspace/workspace.py:79 +#: desk/doctype/workspace/workspace.py:82 msgid "Content data shoud be a list" msgstr "" @@ -6392,7 +6401,7 @@ msgctxt "Social Login Key" msgid "Controls whether new users can sign up using this Social Login Key. If unset, Website Settings is respected. " msgstr "" -#: public/js/frappe/utils/utils.js:1030 +#: public/js/frappe/utils/utils.js:1031 msgid "Copied to clipboard." msgstr "" @@ -6452,11 +6461,11 @@ msgctxt "Number Card" msgid "Count" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:499 +#: public/js/frappe/widgets/widget_dialog.js:505 msgid "Count Customizations" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:484 +#: public/js/frappe/widgets/widget_dialog.js:490 msgid "Count Filter" msgstr "" @@ -6531,7 +6540,7 @@ msgstr "" #: public/js/frappe/views/file/file_view.js:112 #: public/js/frappe/views/interaction.js:18 #: public/js/frappe/views/reports/query_report.js:1172 -#: public/js/frappe/views/workspace/workspace.js:1217 +#: public/js/frappe/views/workspace/workspace.js:1223 #: workflow/page/workflow_builder/workflow_builder.js:46 msgid "Create" msgstr "" @@ -6554,7 +6563,7 @@ msgctxt "User Document Type" msgid "Create" msgstr "" -#: core/doctype/doctype/doctype_list.js:85 +#: core/doctype/doctype/doctype_list.js:102 msgid "Create & Continue" msgstr "" @@ -6584,7 +6593,7 @@ msgstr "" msgid "Create Custom Fields" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:925 +#: public/js/frappe/views/workspace/workspace.js:931 msgid "Create Duplicate" msgstr "" @@ -6606,7 +6615,7 @@ msgstr "" msgid "Create New" msgstr "" -#: core/doctype/doctype/doctype_list.js:83 +#: core/doctype/doctype/doctype_list.js:100 msgid "Create New DocType" msgstr "" @@ -6618,7 +6627,7 @@ msgstr "" msgid "Create User Email" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:465 +#: public/js/frappe/views/workspace/workspace.js:471 msgid "Create Workspace" msgstr "" @@ -6626,7 +6635,7 @@ msgstr "" msgid "Create a Reminder" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:521 +#: public/js/frappe/ui/toolbar/search_utils.js:537 msgid "Create a new ..." msgstr "" @@ -6634,8 +6643,8 @@ msgstr "" msgid "Create a new record" msgstr "" -#: public/js/frappe/form/controls/link.js:291 -#: public/js/frappe/form/controls/link.js:293 +#: public/js/frappe/form/controls/link.js:292 +#: public/js/frappe/form/controls/link.js:294 #: public/js/frappe/form/link_selector.js:139 #: public/js/frappe/list/list_view.js:470 msgid "Create a new {0}" @@ -7103,7 +7112,7 @@ msgctxt "Translation" msgid "Custom Translation" msgstr "" -#: core/doctype/doctype/doctype_list.js:65 +#: core/doctype/doctype/doctype_list.js:82 msgid "Custom?" msgstr "" @@ -7149,7 +7158,7 @@ msgstr "" msgid "Customization onboarding is all done!" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:511 +#: public/js/frappe/views/workspace/workspace.js:517 msgid "Customizations Discarded" msgstr "" @@ -7340,7 +7349,7 @@ msgstr "" msgid "Daily Event Digest is sent for Calendar Events where reminders are set." msgstr "" -#: desk/doctype/event/event.py:93 +#: desk/doctype/event/event.py:94 msgid "Daily Events should finish on the Same Day." msgstr "" @@ -7381,7 +7390,7 @@ msgstr "" #. Name of a DocType #: core/page/dashboard_view/dashboard_view.js:10 #: desk/doctype/dashboard/dashboard.json -#: public/js/frappe/ui/toolbar/search_utils.js:546 +#: public/js/frappe/ui/toolbar/search_utils.js:562 msgid "Dashboard" msgstr "" @@ -7571,7 +7580,7 @@ msgstr "" msgid "Data Too Long" msgstr "" -#: model/base_document.py:703 +#: model/base_document.py:722 msgid "Data missing in table" msgstr "" @@ -8000,11 +8009,11 @@ msgctxt "DocType" msgid "Default View" msgstr "" -#: core/doctype/doctype/doctype.py:1327 +#: core/doctype/doctype/doctype.py:1323 msgid "Default for 'Check' type of field {0} must be either '0' or '1'" msgstr "" -#: core/doctype/doctype/doctype.py:1340 +#: core/doctype/doctype/doctype.py:1336 msgid "Default value for {0} must be in the list of options." msgstr "" @@ -8049,12 +8058,12 @@ msgstr "" #: public/js/frappe/form/toolbar.js:423 #: public/js/frappe/views/reports/report_view.js:1645 #: public/js/frappe/views/treeview.js:313 -#: public/js/frappe/views/workspace/workspace.js:823 +#: public/js/frappe/views/workspace/workspace.js:829 #: templates/discussions/reply_card.html:35 msgid "Delete" msgstr "" -#: public/js/frappe/list/list_view.js:1857 +#: public/js/frappe/list/list_view.js:1881 msgctxt "Button in list view actions menu" msgid "Delete" msgstr "" @@ -8089,11 +8098,11 @@ msgstr "" msgid "Delete Kanban Board" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:824 +#: public/js/frappe/views/workspace/workspace.js:830 msgid "Delete Workspace" msgstr "" -#: public/js/frappe/form/footer/form_timeline.js:696 +#: public/js/frappe/form/footer/form_timeline.js:719 msgid "Delete comment?" msgstr "" @@ -8101,12 +8110,12 @@ msgstr "" msgid "Delete this record to allow sending to this email address" msgstr "" -#: public/js/frappe/list/list_view.js:1862 +#: public/js/frappe/list/list_view.js:1886 msgctxt "Title of confirmation dialog" msgid "Delete {0} item permanently?" msgstr "" -#: public/js/frappe/list/list_view.js:1868 +#: public/js/frappe/list/list_view.js:1892 msgctxt "Title of confirmation dialog" msgid "Delete {0} items permanently?" msgstr "" @@ -8235,6 +8244,7 @@ msgid "Descendants Of (inclusive)" msgstr "" #: desk/report/todo/todo.py:39 public/js/frappe/form/reminders.js:44 +#: public/js/frappe/widgets/widget_dialog.js:227 msgid "Description" msgstr "" @@ -8335,6 +8345,12 @@ msgctxt "Website Slideshow Item" msgid "Description" msgstr "" +#. Label of a HTML Editor field in DocType 'Workspace Link' +#: desk/doctype/workspace_link/workspace_link.json +msgctxt "Workspace Link" +msgid "Description" +msgstr "" + #. Description of the 'Blog Intro' (Small Text) field in DocType 'Blog Post' #: website/doctype/blog_post/blog_post.json msgctxt "Blog Post" @@ -8634,7 +8650,7 @@ msgstr "" #: public/js/frappe/views/communication.js:30 #: public/js/frappe/views/dashboard/dashboard_view.js:70 -#: public/js/frappe/views/workspace/workspace.js:502 +#: public/js/frappe/views/workspace/workspace.js:508 #: public/js/frappe/web_form/web_form.js:187 #: website/doctype/web_form/templates/web_form.html:41 msgid "Discard" @@ -8695,7 +8711,7 @@ msgctxt "LDAP Settings" msgid "Do not create new user if user with email does not exist in the system" msgstr "" -#: public/js/frappe/form/grid.js:1156 +#: public/js/frappe/form/grid.js:1158 msgid "Do not edit headers which are preset in the template" msgstr "" @@ -8852,7 +8868,7 @@ msgctxt "Workspace Shortcut" msgid "DocType" msgstr "" -#: core/doctype/doctype/doctype.py:1528 +#: core/doctype/doctype/doctype.py:1524 msgid "DocType {0} provided for the field {1} must have atleast one Link field" msgstr "" @@ -8895,7 +8911,7 @@ msgctxt "Property Setter" msgid "DocType Link" msgstr "" -#: core/doctype/doctype/doctype_list.js:10 +#: core/doctype/doctype/doctype_list.js:22 msgid "DocType Name" msgstr "" @@ -8916,11 +8932,11 @@ msgctxt "Workspace Shortcut" msgid "DocType View" msgstr "" -#: core/doctype/doctype/doctype.py:646 +#: core/doctype/doctype/doctype.py:642 msgid "DocType can not be merged" msgstr "" -#: core/doctype/doctype/doctype.py:640 +#: core/doctype/doctype/doctype.py:636 msgid "DocType can only be renamed by Administrator" msgstr "" @@ -8958,7 +8974,7 @@ msgstr "" msgid "DocType {} not found" msgstr "" -#: core/doctype/doctype/doctype.py:1009 +#: core/doctype/doctype/doctype.py:1005 msgid "DocType's name should not start or end with whitespace" msgstr "" @@ -8966,7 +8982,7 @@ msgstr "" msgid "DocTypes can not be modified, please use {0} instead" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:645 +#: public/js/frappe/widgets/widget_dialog.js:651 msgid "Doctype" msgstr "" @@ -8976,7 +8992,7 @@ msgctxt "Document Follow" msgid "Doctype" msgstr "" -#: core/doctype/doctype/doctype.py:1004 +#: core/doctype/doctype/doctype.py:1000 msgid "Doctype name is limited to {0} characters ({1})" msgstr "" @@ -8984,7 +9000,7 @@ msgstr "" msgid "Doctype required" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1303 +#: public/js/frappe/views/workspace/workspace.js:1309 msgid "Doctype with same route already exist. Please choose different title." msgstr "" @@ -9058,24 +9074,24 @@ msgctxt "Customize Form" msgid "Document Links" msgstr "" -#: core/doctype/doctype/doctype.py:1162 +#: core/doctype/doctype/doctype.py:1158 msgid "Document Links Row #{0}: Could not find field {1} in {2} DocType" msgstr "" -#: core/doctype/doctype/doctype.py:1182 +#: core/doctype/doctype/doctype.py:1178 msgid "Document Links Row #{0}: Invalid doctype or fieldname." msgstr "" -#: core/doctype/doctype/doctype.py:1145 +#: core/doctype/doctype/doctype.py:1141 msgid "Document Links Row #{0}: Parent DocType is mandatory for internal links" msgstr "" -#: core/doctype/doctype/doctype.py:1151 +#: core/doctype/doctype/doctype.py:1147 msgid "Document Links Row #{0}: Table Fieldname is mandatory for internal links" msgstr "" #: core/doctype/user_permission/user_permission_list.js:36 -#: public/js/frappe/form/form_tour.js:58 +#: public/js/frappe/form/form_tour.js:60 msgid "Document Name" msgstr "" @@ -9602,7 +9618,7 @@ msgstr "" #: public/js/frappe/views/workspace/blocks/header.js:46 #: public/js/frappe/views/workspace/blocks/paragraph.js:136 #: public/js/frappe/views/workspace/blocks/spacer.js:44 -#: public/js/frappe/views/workspace/workspace.js:565 +#: public/js/frappe/views/workspace/workspace.js:571 #: public/js/frappe/widgets/base_widget.js:33 msgid "Drag" msgstr "" @@ -9653,8 +9669,8 @@ msgid "Due Date Based On" msgstr "" #: public/js/frappe/form/toolbar.js:377 -#: public/js/frappe/views/workspace/workspace.js:808 -#: public/js/frappe/views/workspace/workspace.js:975 +#: public/js/frappe/views/workspace/workspace.js:814 +#: public/js/frappe/views/workspace/workspace.js:981 msgid "Duplicate" msgstr "" @@ -9666,12 +9682,12 @@ msgstr "" msgid "Duplicate Filter Name" msgstr "" -#: model/base_document.py:563 model/rename_doc.py:113 +#: model/base_document.py:582 model/rename_doc.py:113 msgid "Duplicate Name" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:547 -#: public/js/frappe/views/workspace/workspace.js:809 +#: public/js/frappe/views/workspace/workspace.js:553 +#: public/js/frappe/views/workspace/workspace.js:815 msgid "Duplicate Workspace" msgstr "" @@ -9679,7 +9695,7 @@ msgstr "" msgid "Duplicate current row" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:990 +#: public/js/frappe/views/workspace/workspace.js:996 msgid "Duplicate of {0} named as {1} is created successfully" msgstr "" @@ -9811,12 +9827,12 @@ msgstr "" #: printing/page/print_format_builder_beta/print_format_builder_beta.js:46 #: printing/page/print_format_builder_beta/print_format_builder_beta.js:85 #: public/js/frappe/form/controls/markdown_editor.js:31 -#: public/js/frappe/form/footer/form_timeline.js:638 +#: public/js/frappe/form/footer/form_timeline.js:661 #: public/js/frappe/form/toolbar.js:672 #: public/js/frappe/views/reports/query_report.js:809 #: public/js/frappe/views/reports/query_report.js:1617 -#: public/js/frappe/views/workspace/workspace.js:448 -#: public/js/frappe/views/workspace/workspace.js:802 +#: public/js/frappe/views/workspace/workspace.js:454 +#: public/js/frappe/views/workspace/workspace.js:808 #: public/js/frappe/widgets/base_widget.js:64 #: public/js/frappe/widgets/chart_widget.js:298 #: public/js/frappe/widgets/number_card_widget.js:314 @@ -9826,7 +9842,7 @@ msgstr "" msgid "Edit" msgstr "" -#: public/js/frappe/list/list_view.js:1943 +#: public/js/frappe/list/list_view.js:1967 msgctxt "Button in list view actions menu" msgid "Edit" msgstr "" @@ -9903,7 +9919,7 @@ msgctxt "Website Settings" msgid "Edit Values" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:803 +#: public/js/frappe/views/workspace/workspace.js:809 msgid "Edit Workspace" msgstr "" @@ -9923,7 +9939,7 @@ msgstr "" msgid "Edit {0}" msgstr "" -#: core/doctype/doctype/doctype_list.js:41 +#: core/doctype/doctype/doctype_list.js:57 msgid "Editable Grid" msgstr "" @@ -10373,7 +10389,7 @@ msgstr "" msgid "Email has been moved to trash" msgstr "" -#: public/js/frappe/views/communication.js:707 +#: public/js/frappe/views/communication.js:749 msgid "Email not sent to {0} (unsubscribed / disabled)" msgstr "" @@ -10696,7 +10712,7 @@ msgstr "" msgid "Enabled Scheduler" msgstr "" -#: email/doctype/email_account/email_account.py:896 +#: email/doctype/email_account/email_account.py:898 msgid "Enabled email inbox for user {0}" msgstr "" @@ -10873,7 +10889,7 @@ msgstr "" msgid "Enter Client Id and Client Secret in Google Settings." msgstr "" -#: public/js/frappe/views/communication.js:663 +#: public/js/frappe/views/communication.js:705 msgid "Enter Email Recipient(s)" msgstr "" @@ -10888,7 +10904,7 @@ msgctxt "Title of prompt dialog" msgid "Enter Value" msgstr "" -#: public/js/frappe/form/form_tour.js:56 +#: public/js/frappe/form/form_tour.js:58 msgid "Enter a name for this {0}" msgstr "" @@ -10939,8 +10955,8 @@ msgstr "" msgid "Equals" msgstr "" -#: desk/page/backups/backups.js:35 model/base_document.py:703 -#: model/base_document.py:708 public/js/frappe/ui/messages.js:22 +#: desk/page/backups/backups.js:35 model/base_document.py:722 +#: model/base_document.py:727 public/js/frappe/ui/messages.js:22 msgid "Error" msgstr "" @@ -11053,7 +11069,7 @@ msgstr "" msgid "Error in print format on line {0}: {1}" msgstr "" -#: email/doctype/email_account/email_account.py:586 +#: email/doctype/email_account/email_account.py:588 msgid "Error while connecting to email account {0}" msgstr "" @@ -11065,7 +11081,7 @@ msgstr "" msgid "Error: Document has been modified after you have opened it" msgstr "" -#: model/base_document.py:716 +#: model/base_document.py:735 msgid "Error: Value missing for {0}: {1}" msgstr "" @@ -11132,7 +11148,7 @@ msgctxt "Recorder" msgid "Event Type" msgstr "" -#: desk/doctype/event/event.py:263 +#: desk/doctype/event/event.py:264 msgid "Events in Today's Calendar" msgstr "" @@ -11334,7 +11350,7 @@ msgstr "" msgid "Export" msgstr "" -#: public/js/frappe/list/list_view.js:1965 +#: public/js/frappe/list/list_view.js:1989 msgctxt "Button in list view actions menu" msgid "Export" msgstr "" @@ -11531,7 +11547,7 @@ msgstr "" msgid "Failed to connect to server" msgstr "" -#: auth.py:649 +#: auth.py:660 msgid "Failed to decode token, please provide a valid base64-encoded token." msgstr "" @@ -11672,7 +11688,7 @@ msgid "Fetching default Global Search documents." msgstr "" #: desk/page/leaderboard/leaderboard.js:131 -#: public/js/frappe/list/bulk_operations.js:262 +#: public/js/frappe/list/bulk_operations.js:283 #: public/js/frappe/views/reports/query_report.js:235 #: public/js/frappe/views/reports/query_report.js:1706 msgid "Field" @@ -11720,11 +11736,11 @@ msgctxt "Web Form List Column" msgid "Field" msgstr "" -#: core/doctype/doctype/doctype.py:419 +#: core/doctype/doctype/doctype.py:415 msgid "Field \"route\" is mandatory for Web Views" msgstr "" -#: core/doctype/doctype/doctype.py:1477 +#: core/doctype/doctype/doctype.py:1473 msgid "Field \"title\" is mandatory if \"Website Search Field\" is set." msgstr "" @@ -11738,7 +11754,7 @@ msgctxt "Custom Field" msgid "Field Description" msgstr "" -#: core/doctype/doctype/doctype.py:1040 +#: core/doctype/doctype/doctype.py:1036 msgid "Field Missing" msgstr "" @@ -11786,7 +11802,7 @@ msgstr "" msgid "Field type cannot be changed for {0}" msgstr "" -#: database/database.py:783 +#: database/database.py:828 msgid "Field {0} does not exist on {1}" msgstr "" @@ -11844,11 +11860,11 @@ msgctxt "Webhook Data" msgid "Fieldname" msgstr "" -#: core/doctype/doctype/doctype.py:270 +#: core/doctype/doctype/doctype.py:266 msgid "Fieldname '{0}' conflicting with a {1} of the name {2} in {3}" msgstr "" -#: core/doctype/doctype/doctype.py:1039 +#: core/doctype/doctype/doctype.py:1035 msgid "Fieldname called {0} must exist to enable autonaming" msgstr "" @@ -11872,11 +11888,11 @@ msgstr "" msgid "Fieldname {0} cannot have special characters like {1}" msgstr "" -#: core/doctype/doctype/doctype.py:1850 +#: core/doctype/doctype/doctype.py:1846 msgid "Fieldname {0} conflicting with meta object" msgstr "" -#: core/doctype/doctype/doctype.py:495 public/js/form_builder/utils.js:302 +#: core/doctype/doctype/doctype.py:491 public/js/form_builder/utils.js:302 msgid "Fieldname {0} is restricted" msgstr "" @@ -12148,11 +12164,11 @@ msgctxt "Prepared Report" msgid "Filter Values" msgstr "" -#: utils/data.py:2021 +#: utils/data.py:2015 msgid "Filter must be a tuple or list (in a list)" msgstr "" -#: utils/data.py:2029 +#: utils/data.py:2023 msgid "Filter must have 4 values (doctype, fieldname, operator, value): {0}" msgstr "" @@ -12255,7 +12271,7 @@ msgctxt "Number Card" msgid "Filters Section" msgstr "" -#: public/js/frappe/form/controls/link.js:486 +#: public/js/frappe/form/controls/link.js:488 msgid "Filters applied for {0}" msgstr "" @@ -12269,14 +12285,14 @@ msgctxt "Report" msgid "Filters will be accessible via filters.

Send output as result = [result], or for old style data = [columns], [result]" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:556 +#: public/js/frappe/ui/toolbar/search_utils.js:572 msgid "Find '{0}' in ..." msgstr "" -#: public/js/frappe/ui/toolbar/awesome_bar.js:325 -#: public/js/frappe/ui/toolbar/awesome_bar.js:326 -#: public/js/frappe/ui/toolbar/search_utils.js:125 -#: public/js/frappe/ui/toolbar/search_utils.js:128 +#: public/js/frappe/ui/toolbar/awesome_bar.js:327 +#: public/js/frappe/ui/toolbar/awesome_bar.js:328 +#: public/js/frappe/ui/toolbar/search_utils.js:141 +#: public/js/frappe/ui/toolbar/search_utils.js:144 msgid "Find {0} in {1}" msgstr "" @@ -12410,11 +12426,11 @@ msgctxt "Report Filter" msgid "Fold" msgstr "" -#: core/doctype/doctype/doctype.py:1401 +#: core/doctype/doctype/doctype.py:1397 msgid "Fold can not be at the end of the form" msgstr "" -#: core/doctype/doctype/doctype.py:1399 +#: core/doctype/doctype/doctype.py:1395 msgid "Fold must come before a Section Break" msgstr "" @@ -12460,7 +12476,7 @@ msgstr "" msgid "Following fields have invalid values:" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:314 +#: public/js/frappe/widgets/widget_dialog.js:320 msgid "Following fields have missing values" msgstr "" @@ -12630,7 +12646,7 @@ msgstr "" msgid "For Document Type" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:529 +#: public/js/frappe/widgets/widget_dialog.js:535 msgid "For Example: {} Open" msgstr "" @@ -12723,7 +12739,7 @@ msgstr "" msgid "For updating, you can update only selective columns." msgstr "" -#: core/doctype/doctype/doctype.py:1692 +#: core/doctype/doctype/doctype.py:1688 msgid "For {0} at level {1} in {2} in row {3}" msgstr "" @@ -12870,7 +12886,7 @@ msgctxt "Webhook" msgid "Form URL-Encoded" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:528 +#: public/js/frappe/widgets/widget_dialog.js:534 msgid "Format" msgstr "" @@ -13108,7 +13124,7 @@ msgid "Full Width" msgstr "" #: public/js/frappe/views/reports/query_report.js:245 -#: public/js/frappe/widgets/widget_dialog.js:666 +#: public/js/frappe/widgets/widget_dialog.js:672 msgid "Function" msgstr "" @@ -13118,11 +13134,11 @@ msgctxt "Number Card" msgid "Function" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:673 +#: public/js/frappe/widgets/widget_dialog.js:679 msgid "Function Based On" msgstr "" -#: __init__.py:835 +#: __init__.py:875 msgid "Function {0} is not whitelisted." msgstr "" @@ -13206,12 +13222,12 @@ msgstr "" msgid "Generate New Report" msgstr "" -#: public/js/frappe/ui/toolbar/awesome_bar.js:366 +#: public/js/frappe/ui/toolbar/awesome_bar.js:368 msgid "Generate Random Password" msgstr "" #: public/js/frappe/ui/toolbar/toolbar.js:137 -#: public/js/frappe/utils/utils.js:1750 +#: public/js/frappe/utils/utils.js:1751 msgid "Generate Tracking URL" msgstr "" @@ -13838,6 +13854,12 @@ msgctxt "Auto Email Report" msgid "Half Yearly" msgstr "" +#. Option for the 'Repeat On' (Select) field in DocType 'Event' +#: desk/doctype/event/event.json +msgctxt "Event" +msgid "Half Yearly" +msgstr "" + #: public/js/frappe/utils/common.js:402 msgid "Half-yearly" msgstr "" @@ -14066,7 +14088,7 @@ msgctxt "Print Settings" msgid "Helvetica Neue" msgstr "" -#: public/js/frappe/utils/utils.js:1747 +#: public/js/frappe/utils/utils.js:1748 msgid "Here's your tracking URL" msgstr "" @@ -14134,7 +14156,7 @@ msgctxt "Form Tour Step" msgid "Hidden Fields" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:814 +#: public/js/frappe/views/workspace/workspace.js:820 #: public/js/frappe/widgets/base_widget.js:46 #: public/js/frappe/widgets/base_widget.js:176 msgid "Hide" @@ -14291,7 +14313,7 @@ msgstr "" msgid "Hide Weekends" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:815 +#: public/js/frappe/views/workspace/workspace.js:821 msgid "Hide Workspace" msgstr "" @@ -14501,9 +14523,9 @@ msgctxt "Comment" msgid "IP Address" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:632 -#: public/js/frappe/views/workspace/workspace.js:960 -#: public/js/frappe/views/workspace/workspace.js:1205 +#: public/js/frappe/views/workspace/workspace.js:638 +#: public/js/frappe/views/workspace/workspace.js:966 +#: public/js/frappe/views/workspace/workspace.js:1211 msgid "Icon" msgstr "" @@ -14606,7 +14628,7 @@ msgctxt "Workflow Document State" msgid "If Checked workflow status will not override status in list view" msgstr "" -#: core/doctype/doctype/doctype.py:1706 +#: core/doctype/doctype/doctype.py:1702 msgid "If Owner" msgstr "" @@ -14973,11 +14995,11 @@ msgctxt "Letter Head" msgid "Image Width" msgstr "" -#: core/doctype/doctype/doctype.py:1457 +#: core/doctype/doctype/doctype.py:1453 msgid "Image field must be a valid fieldname" msgstr "" -#: core/doctype/doctype/doctype.py:1459 +#: core/doctype/doctype/doctype.py:1455 msgid "Image field must be of type Attach Image" msgstr "" @@ -15221,7 +15243,7 @@ msgstr "" msgid "In Progress" msgstr "" -#: database/database.py:233 +#: database/database.py:241 msgid "In Read Only Mode" msgstr "" @@ -15351,7 +15373,7 @@ msgstr "" msgid "Incomplete Virtual Doctype Implementation" msgstr "" -#: auth.py:236 +#: auth.py:238 msgid "Incomplete login details" msgstr "" @@ -15433,9 +15455,9 @@ msgctxt "Workspace" msgid "Indicator Color" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:639 -#: public/js/frappe/views/workspace/workspace.js:967 -#: public/js/frappe/views/workspace/workspace.js:1211 +#: public/js/frappe/views/workspace/workspace.js:645 +#: public/js/frappe/views/workspace/workspace.js:973 +#: public/js/frappe/views/workspace/workspace.js:1217 msgid "Indicator color" msgstr "" @@ -15499,7 +15521,7 @@ msgstr "" msgid "Insert Column Before {0}" msgstr "" -#: public/js/frappe/form/controls/markdown_editor.js:81 +#: public/js/frappe/form/controls/markdown_editor.js:82 msgid "Insert Image in Markdown" msgstr "" @@ -15515,8 +15537,8 @@ msgctxt "Web Page" msgid "Insert Style" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:646 -#: public/js/frappe/ui/toolbar/search_utils.js:647 +#: public/js/frappe/ui/toolbar/search_utils.js:662 +#: public/js/frappe/ui/toolbar/search_utils.js:663 msgid "Install {0} from Marketplace" msgstr "" @@ -15540,11 +15562,11 @@ msgstr "" msgid "Installed Apps" msgstr "" -#: permissions.py:826 +#: permissions.py:829 msgid "Insufficient Permission Level for {0}" msgstr "" -#: database/query.py:371 desk/form/load.py:40 model/document.py:234 +#: database/query.py:372 desk/form/load.py:40 model/document.py:234 msgid "Insufficient Permission for {0}" msgstr "" @@ -15556,7 +15578,7 @@ msgstr "" msgid "Insufficient Permissions for editing Report" msgstr "" -#: core/doctype/doctype/doctype.py:447 +#: core/doctype/doctype/doctype.py:443 msgid "Insufficient attachment limit" msgstr "" @@ -15704,7 +15726,7 @@ msgctxt "OAuth Authorization Code" msgid "Invalid" msgstr "" -#: public/js/form_builder/utils.js:221 public/js/frappe/form/grid_row.js:748 +#: public/js/form_builder/utils.js:221 public/js/frappe/form/grid_row.js:768 #: public/js/frappe/form/layout.js:774 msgid "Invalid \"depends_on\" expression" msgstr "" @@ -15741,11 +15763,11 @@ msgstr "" msgid "Invalid DocType" msgstr "" -#: database/query.py:95 +#: database/query.py:96 msgid "Invalid DocType: {0}" msgstr "" -#: core/doctype/doctype/doctype.py:1223 +#: core/doctype/doctype/doctype.py:1219 msgid "Invalid Fieldname" msgstr "" @@ -15785,7 +15807,7 @@ msgstr "" msgid "Invalid Operation" msgstr "" -#: core/doctype/doctype/doctype.py:1582 core/doctype/doctype/doctype.py:1591 +#: core/doctype/doctype/doctype.py:1578 core/doctype/doctype/doctype.py:1587 msgid "Invalid Option" msgstr "" @@ -15811,7 +15833,7 @@ msgstr "" msgid "Invalid Phone Number" msgstr "" -#: auth.py:93 utils/oauth.py:184 utils/oauth.py:191 www/login.py:112 +#: auth.py:95 utils/oauth.py:184 utils/oauth.py:191 www/login.py:112 msgid "Invalid Request" msgstr "" @@ -15819,7 +15841,7 @@ msgstr "" msgid "Invalid Search Field {0}" msgstr "" -#: core/doctype/doctype/doctype.py:1165 +#: core/doctype/doctype/doctype.py:1161 msgid "Invalid Table Fieldname" msgstr "" @@ -15827,7 +15849,7 @@ msgstr "" msgid "Invalid Transition" msgstr "" -#: core/doctype/file/file.py:217 public/js/frappe/widgets/widget_dialog.js:565 +#: core/doctype/file/file.py:217 public/js/frappe/widgets/widget_dialog.js:571 #: utils/csvutils.py:199 utils/csvutils.py:220 msgid "Invalid URL" msgstr "" @@ -15860,11 +15882,11 @@ msgstr "" msgid "Invalid expression set in filter {0} ({1})" msgstr "" -#: utils/data.py:2128 +#: utils/data.py:2122 msgid "Invalid field name {0}" msgstr "" -#: core/doctype/doctype/doctype.py:1048 +#: core/doctype/doctype/doctype.py:1044 msgid "Invalid fieldname '{0}' in autoname" msgstr "" @@ -15872,7 +15894,7 @@ msgstr "" msgid "Invalid file path: {0}" msgstr "" -#: database/query.py:173 public/js/frappe/ui/filters/filter_list.js:199 +#: database/query.py:174 public/js/frappe/ui/filters/filter_list.js:199 msgid "Invalid filter: {0}" msgstr "" @@ -15923,7 +15945,7 @@ msgctxt "Error message in web form" msgid "Invalid values for fields:" msgstr "" -#: core/doctype/doctype/doctype.py:1515 +#: core/doctype/doctype/doctype.py:1511 msgid "Invalid {0} condition" msgstr "" @@ -15965,7 +15987,7 @@ msgctxt "DocType" msgid "Is Calendar and Gantt" msgstr "" -#: core/doctype/doctype/doctype_list.js:34 +#: core/doctype/doctype/doctype_list.js:49 msgid "Is Child Table" msgstr "" @@ -16127,7 +16149,7 @@ msgctxt "DocType" msgid "Is Published Field" msgstr "" -#: core/doctype/doctype/doctype.py:1466 +#: core/doctype/doctype/doctype.py:1462 msgid "Is Published Field must be a valid fieldname" msgstr "" @@ -16143,7 +16165,7 @@ msgctxt "Integration Request" msgid "Is Remote Request?" msgstr "" -#: core/doctype/doctype/doctype_list.js:48 +#: core/doctype/doctype/doctype_list.js:64 msgid "Is Single" msgstr "" @@ -16225,7 +16247,7 @@ msgctxt "Web Form" msgid "Is Standard" msgstr "" -#: core/doctype/doctype/doctype_list.js:25 +#: core/doctype/doctype/doctype_list.js:39 msgid "Is Submittable" msgstr "" @@ -16451,7 +16473,7 @@ msgstr "" msgid "Job is not running." msgstr "" -#: desk/doctype/event/event.js:51 +#: desk/doctype/event/event.js:55 msgid "Join video conference with {0}" msgstr "" @@ -16745,8 +16767,9 @@ msgid "LDAP settings incorrect. validation response was: {0}" msgstr "" #: printing/page/print_format_builder/print_format_builder.js:474 -#: public/js/frappe/widgets/widget_dialog.js:606 -#: public/js/frappe/widgets/widget_dialog.js:639 +#: public/js/frappe/widgets/widget_dialog.js:222 +#: public/js/frappe/widgets/widget_dialog.js:612 +#: public/js/frappe/widgets/widget_dialog.js:645 msgid "Label" msgstr "" @@ -17116,7 +17139,7 @@ msgid "Leave blank to repeat always" msgstr "" #: core/doctype/communication/mixins.py:206 -#: email/doctype/email_account/email_account.py:624 +#: email/doctype/email_account/email_account.py:626 msgid "Leave this conversation" msgstr "" @@ -17518,6 +17541,12 @@ msgstr "" msgid "Link Expired" msgstr "" +#. Label of a Int field in DocType 'System Settings' +#: core/doctype/system_settings/system_settings.json +msgctxt "System Settings" +msgid "Link Field Results Limit" +msgstr "" + #. Label of a Data field in DocType 'DocType Link' #: core/doctype/doctype_link/doctype_link.json msgctxt "DocType Link" @@ -17755,7 +17784,7 @@ msgctxt "Web Page" msgid "List as [{\"label\": _(\"Jobs\"), \"route\":\"jobs\"}]" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:526 +#: public/js/frappe/ui/toolbar/search_utils.js:542 msgid "Lists" msgstr "" @@ -17928,7 +17957,7 @@ msgstr "" msgid "Login is required to see web form list view. Enable {0} to see list settings" msgstr "" -#: auth.py:322 auth.py:325 +#: auth.py:324 auth.py:327 msgid "Login not allowed at this time" msgstr "" @@ -17968,7 +17997,7 @@ msgctxt "System Settings" msgid "Login with email link expiry (in minutes)" msgstr "" -#: auth.py:131 +#: auth.py:133 msgid "Login with username and password is not allowed." msgstr "" @@ -18407,7 +18436,7 @@ msgctxt "System Settings" msgid "Max auto email report per user" msgstr "" -#: core/doctype/doctype/doctype.py:1293 +#: core/doctype/doctype/doctype.py:1289 msgid "Max width for type Currency is 100px in row {0}" msgstr "" @@ -18455,7 +18484,7 @@ msgid "Me" msgstr "" #: public/js/frappe/form/sidebar/assign_to.js:194 -#: public/js/frappe/utils/utils.js:1719 +#: public/js/frappe/utils/utils.js:1720 #: website/report/website_analytics/website_analytics.js:40 msgid "Medium" msgstr "" @@ -18551,7 +18580,7 @@ msgctxt "Communication" msgid "Message" msgstr "" -#: __init__.py:527 public/js/frappe/ui/messages.js:267 +#: __init__.py:567 public/js/frappe/ui/messages.js:267 msgctxt "Default title of the message dialog" msgid "Message" msgstr "" @@ -18641,7 +18670,7 @@ msgctxt "Notification" msgid "Message Type" msgstr "" -#: public/js/frappe/views/communication.js:841 +#: public/js/frappe/views/communication.js:883 msgid "Message clipped" msgstr "" @@ -18857,7 +18886,7 @@ msgstr "" msgid "Missing DocType" msgstr "" -#: core/doctype/doctype/doctype.py:1477 +#: core/doctype/doctype/doctype.py:1473 msgid "Missing Field" msgstr "" @@ -18878,7 +18907,7 @@ msgid "Missing Value" msgstr "" #: public/js/frappe/ui/field_group.js:118 -#: public/js/frappe/widgets/widget_dialog.js:330 +#: public/js/frappe/widgets/widget_dialog.js:336 #: public/js/workflow_builder/store.js:97 #: workflow/doctype/workflow/workflow.js:71 msgid "Missing Values Required" @@ -18921,7 +18950,7 @@ msgstr "" msgid "Modified By" msgstr "" -#: core/doctype/doctype/doctype_list.js:17 +#: core/doctype/doctype/doctype_list.js:30 msgid "Module" msgstr "" @@ -19641,11 +19670,11 @@ msgctxt "Role" msgid "Navigation Settings" msgstr "" -#: desk/doctype/workspace/workspace.py:297 +#: desk/doctype/workspace/workspace.py:303 msgid "Need Workspace Manager role to edit private workspace of other users" msgstr "" -#: desk/doctype/workspace/workspace.py:343 +#: desk/doctype/workspace/workspace.py:349 msgid "Need Workspace Manager role to hide/unhide public workspaces" msgstr "" @@ -19776,7 +19805,7 @@ msgstr "" msgid "New Workflow Name" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1172 +#: public/js/frappe/views/workspace/workspace.js:1178 msgid "New Workspace" msgstr "" @@ -19784,7 +19813,7 @@ msgstr "" msgid "New password cannot be same as old password" msgstr "" -#: utils/change_log.py:306 +#: utils/change_log.py:322 msgid "New updates are available" msgstr "" @@ -19805,10 +19834,10 @@ msgstr "" #: public/js/frappe/form/quick_entry.js:124 public/js/frappe/form/toolbar.js:36 #: public/js/frappe/form/toolbar.js:196 public/js/frappe/form/toolbar.js:209 #: public/js/frappe/form/toolbar.js:490 -#: public/js/frappe/ui/toolbar/search_utils.js:151 -#: public/js/frappe/ui/toolbar/search_utils.js:152 -#: public/js/frappe/ui/toolbar/search_utils.js:201 -#: public/js/frappe/ui/toolbar/search_utils.js:202 +#: public/js/frappe/ui/toolbar/search_utils.js:167 +#: public/js/frappe/ui/toolbar/search_utils.js:168 +#: public/js/frappe/ui/toolbar/search_utils.js:217 +#: public/js/frappe/ui/toolbar/search_utils.js:218 #: public/js/frappe/views/treeview.js:350 #: website/doctype/web_form/web_form.py:310 msgid "New {0}" @@ -19831,7 +19860,7 @@ msgstr "" msgid "New {0}: {1}" msgstr "" -#: utils/change_log.py:298 +#: utils/change_log.py:314 msgid "New {} releases for the following apps are available" msgstr "" @@ -19886,7 +19915,7 @@ msgstr "" msgid "Newsletters" msgstr "" -#: public/js/frappe/form/form_tour.js:316 +#: public/js/frappe/form/form_tour.js:318 #: public/js/onboarding_tours/onboarding_tours.js:15 #: public/js/onboarding_tours/onboarding_tours.js:240 #: templates/includes/slideshow.html:38 website/utils.py:247 @@ -19960,7 +19989,7 @@ msgstr "" #: integrations/doctype/webhook/webhook.py:137 #: public/js/form_builder/utils.js:341 -#: public/js/frappe/form/controls/link.js:471 +#: public/js/frappe/form/controls/link.js:472 #: public/js/frappe/list/list_sidebar_group_by.js:223 #: public/js/frappe/views/reports/query_report.js:1513 #: website/doctype/help_article/templates/help_article.html:26 @@ -20074,7 +20103,7 @@ msgstr "" msgid "No Name Specified for {0}" msgstr "" -#: core/doctype/doctype/doctype.py:1684 +#: core/doctype/doctype/doctype.py:1680 msgid "No Permissions Specified" msgstr "" @@ -20130,7 +20159,7 @@ msgstr "" msgid "No changes made because old and new name are the same." msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1477 +#: public/js/frappe/views/workspace/workspace.js:1483 msgid "No changes made on the page" msgstr "" @@ -20216,7 +20245,7 @@ msgctxt "SMS Log" msgid "No of Sent SMS" msgstr "" -#: __init__.py:1027 client.py:109 client.py:151 +#: __init__.py:1067 client.py:109 client.py:151 msgid "No permission for {0}" msgstr "" @@ -20350,7 +20379,7 @@ msgctxt "DocField" msgid "Not Nullable" msgstr "" -#: __init__.py:921 app.py:354 desk/calendar.py:26 geo/utils.py:97 +#: __init__.py:961 app.py:354 desk/calendar.py:26 geo/utils.py:97 #: public/js/frappe/web_form/webform_script.js:15 #: website/doctype/web_form/web_form.py:603 #: website/page_renderers/not_permitted_page.py:20 www/login.py:177 @@ -20422,7 +20451,7 @@ msgstr "" msgid "Not active" msgstr "" -#: permissions.py:367 +#: permissions.py:370 msgid "Not allowed for {0}: {1}" msgstr "" @@ -20430,7 +20459,7 @@ msgstr "" msgid "Not allowed to attach {0} document, please enable Allow Print For {0} in Print Settings" msgstr "" -#: core/doctype/doctype/doctype.py:338 +#: core/doctype/doctype/doctype.py:334 msgid "Not allowed to create custom Virtual DocType." msgstr "" @@ -20454,12 +20483,12 @@ msgstr "" msgid "Not in Developer Mode" msgstr "" -#: core/doctype/doctype/doctype.py:332 +#: core/doctype/doctype/doctype.py:328 msgid "Not in Developer Mode! Set in site_config.json or make 'Custom' DocType." msgstr "" #: api/v1.py:88 api/v1.py:93 -#: core/doctype/system_settings/system_settings.py:199 handler.py:109 +#: core/doctype/system_settings/system_settings.py:208 handler.py:109 #: public/js/frappe/request.js:157 public/js/frappe/request.js:167 #: public/js/frappe/request.js:172 #: public/js/frappe/views/kanban/kanban_board.bundle.js:68 @@ -20506,7 +20535,7 @@ msgctxt "Google Drive" msgid "Note: By default emails for failed backups are sent." msgstr "" -#: public/js/frappe/utils/utils.js:775 +#: public/js/frappe/utils/utils.js:776 msgid "Note: Changing the Page Name will break previous URL to this page." msgstr "" @@ -20690,7 +20719,7 @@ msgstr "" #. Name of a DocType #: desk/doctype/number_card/number_card.json -#: public/js/frappe/widgets/widget_dialog.js:591 +#: public/js/frappe/widgets/widget_dialog.js:597 msgid "Number Card" msgstr "" @@ -20705,7 +20734,7 @@ msgctxt "Workspace Number Card" msgid "Number Card Name" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:621 +#: public/js/frappe/widgets/widget_dialog.js:627 msgid "Number Cards" msgstr "" @@ -20756,11 +20785,11 @@ msgctxt "Recorder" msgid "Number of Queries" msgstr "" -#: core/doctype/doctype/doctype.py:444 public/js/frappe/doctype/index.js:59 +#: core/doctype/doctype/doctype.py:440 public/js/frappe/doctype/index.js:59 msgid "Number of attachment fields are more than {}, limit updated to {}." msgstr "" -#: core/doctype/system_settings/system_settings.py:152 +#: core/doctype/system_settings/system_settings.py:161 msgid "Number of backups must be greater than zero." msgstr "" @@ -20979,7 +21008,7 @@ msgstr "" msgid "Onboarding complete" msgstr "" -#: core/doctype/doctype/doctype_list.js:28 +#: core/doctype/doctype/doctype_list.js:42 msgid "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended." msgstr "" @@ -21001,7 +21030,7 @@ msgstr "" msgid "One of" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1312 +#: public/js/frappe/views/workspace/workspace.js:1318 msgid "One of the child page with name {0} already exist in {1} Section. Please update the name of the child page first before moving" msgstr "" @@ -21021,7 +21050,7 @@ msgstr "" msgid "Only Administrator can save a standard report. Please rename and save." msgstr "" -#: recorder.py:234 +#: recorder.py:227 msgid "Only Administrator is allowed to use Recorder" msgstr "" @@ -21031,7 +21060,7 @@ msgctxt "Workflow Document State" msgid "Only Allow Edit For" msgstr "" -#: core/doctype/doctype/doctype.py:1561 +#: core/doctype/doctype/doctype.py:1557 msgid "Only Options allowed for Data field are:" msgstr "" @@ -21045,7 +21074,7 @@ msgstr "" msgid "Only Workspace Manager can edit public workspaces" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:536 +#: public/js/frappe/views/workspace/workspace.js:542 msgid "Only Workspace Manager can sort or edit this page" msgstr "" @@ -21087,7 +21116,7 @@ msgstr "" msgid "Only standard DocTypes are allowed to be customized from Customize Form." msgstr "" -#: desk/form/assign_to.py:181 +#: desk/form/assign_to.py:195 msgid "Only the assignee can complete this to-do." msgstr "" @@ -21193,13 +21222,13 @@ msgid "Open your authentication app on your mobile phone." msgstr "" #: desk/doctype/todo/todo_list.js:23 -#: public/js/frappe/ui/toolbar/search_utils.js:261 -#: public/js/frappe/ui/toolbar/search_utils.js:262 -#: public/js/frappe/ui/toolbar/search_utils.js:273 -#: public/js/frappe/ui/toolbar/search_utils.js:283 -#: public/js/frappe/ui/toolbar/search_utils.js:292 -#: public/js/frappe/ui/toolbar/search_utils.js:310 -#: public/js/frappe/ui/toolbar/search_utils.js:311 +#: public/js/frappe/ui/toolbar/search_utils.js:277 +#: public/js/frappe/ui/toolbar/search_utils.js:278 +#: public/js/frappe/ui/toolbar/search_utils.js:289 +#: public/js/frappe/ui/toolbar/search_utils.js:299 +#: public/js/frappe/ui/toolbar/search_utils.js:308 +#: public/js/frappe/ui/toolbar/search_utils.js:326 +#: public/js/frappe/ui/toolbar/search_utils.js:327 #: social/doctype/energy_point_log/energy_point_log_list.js:23 msgid "Open {0}" msgstr "" @@ -21228,7 +21257,7 @@ msgctxt "Activity Log" msgid "Operation" msgstr "" -#: utils/data.py:2063 +#: utils/data.py:2057 msgid "Operator must be one of {0}" msgstr "" @@ -21252,7 +21281,7 @@ msgstr "" msgid "Option 3" msgstr "" -#: core/doctype/doctype/doctype.py:1579 +#: core/doctype/doctype/doctype.py:1575 msgid "Option {0} for field {1} is not a child table" msgstr "" @@ -21310,7 +21339,7 @@ msgctxt "Web Template Field" msgid "Options" msgstr "" -#: core/doctype/doctype/doctype.py:1317 +#: core/doctype/doctype/doctype.py:1313 msgid "Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'" msgstr "" @@ -21320,7 +21349,7 @@ msgctxt "Custom Field" msgid "Options Help" msgstr "" -#: core/doctype/doctype/doctype.py:1601 +#: core/doctype/doctype/doctype.py:1597 msgid "Options for Rating field can range from 3 to 10" msgstr "" @@ -21328,7 +21357,7 @@ msgstr "" msgid "Options for select. Each option on a new line." msgstr "" -#: core/doctype/doctype/doctype.py:1334 +#: core/doctype/doctype/doctype.py:1330 msgid "Options for {0} must be set before setting the default value." msgstr "" @@ -21336,7 +21365,7 @@ msgstr "" msgid "Options is required for field {0} of type {1}" msgstr "" -#: model/base_document.py:767 +#: model/base_document.py:786 msgid "Options not set for link field {0}" msgstr "" @@ -21672,7 +21701,7 @@ msgctxt "Form Tour" msgid "Page Route" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1499 +#: public/js/frappe/views/workspace/workspace.js:1505 msgid "Page Saved Successfully" msgstr "" @@ -21713,7 +21742,7 @@ msgstr "" msgid "Page not found" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1299 +#: public/js/frappe/views/workspace/workspace.js:1305 msgid "Page with title {0} already exist." msgstr "" @@ -21729,9 +21758,9 @@ msgid "Parameter" msgstr "" #: public/js/frappe/model/model.js:132 -#: public/js/frappe/views/workspace/workspace.js:606 -#: public/js/frappe/views/workspace/workspace.js:934 -#: public/js/frappe/views/workspace/workspace.js:1181 +#: public/js/frappe/views/workspace/workspace.js:612 +#: public/js/frappe/views/workspace/workspace.js:940 +#: public/js/frappe/views/workspace/workspace.js:1187 msgid "Parent" msgstr "" @@ -21769,7 +21798,7 @@ msgctxt "Form Tour Step" msgid "Parent Field" msgstr "" -#: core/doctype/doctype/doctype.py:915 +#: core/doctype/doctype/doctype.py:911 msgid "Parent Field (Tree)" msgstr "" @@ -21779,7 +21808,7 @@ msgctxt "DocType" msgid "Parent Field (Tree)" msgstr "" -#: core/doctype/doctype/doctype.py:921 +#: core/doctype/doctype/doctype.py:917 msgid "Parent Field must be a valid fieldname" msgstr "" @@ -21789,7 +21818,7 @@ msgctxt "Top Bar Item" msgid "Parent Label" msgstr "" -#: core/doctype/doctype/doctype.py:1148 +#: core/doctype/doctype/doctype.py:1144 msgid "Parent Missing" msgstr "" @@ -21811,7 +21840,7 @@ msgstr "" msgid "Parent is the name of the document to which the data will get added to." msgstr "" -#: permissions.py:806 +#: permissions.py:809 msgid "Parentfield not specified in {0}: {1}" msgstr "" @@ -21910,7 +21939,7 @@ msgctxt "System Settings" msgid "Password Reset Link Generation Limit" msgstr "" -#: public/js/frappe/form/grid_row.js:790 +#: public/js/frappe/form/grid_row.js:810 msgid "Password cannot be filtered" msgstr "" @@ -21944,7 +21973,7 @@ msgstr "" msgid "Password set" msgstr "" -#: auth.py:239 +#: auth.py:241 msgid "Password size exceeded the maximum allowed size" msgstr "" @@ -22225,7 +22254,7 @@ msgctxt "System Settings" msgid "Permissions" msgstr "" -#: core/doctype/doctype/doctype.py:1775 core/doctype/doctype/doctype.py:1785 +#: core/doctype/doctype/doctype.py:1771 core/doctype/doctype/doctype.py:1781 msgid "Permissions Error" msgstr "" @@ -22425,7 +22454,7 @@ msgstr "" msgid "Please check the filter values set for Dashboard Chart: {}" msgstr "" -#: model/base_document.py:839 +#: model/base_document.py:858 msgid "Please check the value of \"Fetch From\" set for field {0}" msgstr "" @@ -22481,7 +22510,7 @@ msgstr "" msgid "Please duplicate this to make changes" msgstr "" -#: core/doctype/system_settings/system_settings.py:145 +#: core/doctype/system_settings/system_settings.py:154 msgid "Please enable atleast one Social Login Key or LDAP or Login With Email Link before disabling username/password based login." msgstr "" @@ -22489,7 +22518,7 @@ msgstr "" #: email/doctype/auto_email_report/auto_email_report.js:17 #: printing/page/print/print.js:611 printing/page/print/print.js:640 #: public/js/frappe/list/bulk_operations.js:117 -#: public/js/frappe/utils/utils.js:1416 +#: public/js/frappe/utils/utils.js:1417 msgid "Please enable pop-ups" msgstr "" @@ -22618,7 +22647,7 @@ msgstr "" msgid "Please select Entity Type first" msgstr "" -#: core/doctype/system_settings/system_settings.py:103 +#: core/doctype/system_settings/system_settings.py:105 msgid "Please select Minimum Password Score" msgstr "" @@ -22697,7 +22726,7 @@ msgstr "" msgid "Please set the series to be used." msgstr "" -#: core/doctype/system_settings/system_settings.py:116 +#: core/doctype/system_settings/system_settings.py:118 msgid "Please setup SMS before setting it as an authentication method, via SMS Settings" msgstr "" @@ -22717,7 +22746,7 @@ msgstr "" msgid "Please specify" msgstr "" -#: permissions.py:782 +#: permissions.py:785 msgid "Please specify a valid parent DocType for {0}" msgstr "" @@ -22898,7 +22927,7 @@ msgctxt "Web Form Field" msgid "Precision" msgstr "" -#: core/doctype/doctype/doctype.py:1349 +#: core/doctype/doctype/doctype.py:1345 msgid "Precision should be between 1 and 6" msgstr "" @@ -22954,7 +22983,7 @@ msgstr "" msgid "Preparing Report" msgstr "" -#: public/js/frappe/views/communication.js:321 +#: public/js/frappe/views/communication.js:363 msgid "Prepend the template to the email message" msgstr "" @@ -23075,7 +23104,7 @@ msgstr "" msgid "Print" msgstr "" -#: public/js/frappe/list/list_view.js:1849 +#: public/js/frappe/list/list_view.js:1873 msgctxt "Button in list view actions menu" msgid "Print" msgstr "" @@ -23523,9 +23552,9 @@ msgid "Provider Name" msgstr "" #: desk/doctype/note/note_list.js:6 public/js/frappe/views/interaction.js:78 -#: public/js/frappe/views/workspace/workspace.js:613 -#: public/js/frappe/views/workspace/workspace.js:941 -#: public/js/frappe/views/workspace/workspace.js:1187 +#: public/js/frappe/views/workspace/workspace.js:619 +#: public/js/frappe/views/workspace/workspace.js:947 +#: public/js/frappe/views/workspace/workspace.js:1193 msgid "Public" msgstr "" @@ -23746,6 +23775,12 @@ msgctxt "Dashboard Chart" msgid "Quarterly" msgstr "" +#. Option for the 'Repeat On' (Select) field in DocType 'Event' +#: desk/doctype/event/event.json +msgctxt "Event" +msgid "Quarterly" +msgstr "" + #. Label of a Data field in DocType 'Recorder Query' #: core/doctype/recorder_query/recorder_query.json msgctxt "Recorder Query" @@ -23819,7 +23854,7 @@ msgctxt "DocType" msgid "Queue in Background (BETA)" msgstr "" -#: utils/background_jobs.py:473 +#: utils/background_jobs.py:433 msgid "Queue should be one of {0}" msgstr "" @@ -24028,13 +24063,13 @@ msgstr "" msgid "Re-Run in Console" msgstr "" -#: email/doctype/email_account/email_account.py:630 +#: email/doctype/email_account/email_account.py:632 msgid "Re:" msgstr "" #: core/doctype/communication/communication.js:268 -#: public/js/frappe/form/footer/form_timeline.js:564 -#: public/js/frappe/views/communication.js:257 +#: public/js/frappe/form/footer/form_timeline.js:587 +#: public/js/frappe/views/communication.js:299 msgid "Re: {0}" msgstr "" @@ -24242,7 +24277,7 @@ msgstr "" msgid "Recent years are easy to guess." msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:516 +#: public/js/frappe/ui/toolbar/search_utils.js:532 msgid "Recents" msgstr "" @@ -24347,7 +24382,7 @@ msgctxt "Website Settings" msgid "Redirects" msgstr "" -#: sessions.py:148 +#: sessions.py:149 msgid "Redis cache server not running. Please contact Administrator / Tech support" msgstr "" @@ -24951,7 +24986,7 @@ msgstr "" msgid "Removed {0}" msgstr "" -#: custom/doctype/custom_field/custom_field.js:133 +#: custom/doctype/custom_field/custom_field.js:134 #: public/js/frappe/form/toolbar.js:234 public/js/frappe/form/toolbar.js:238 #: public/js/frappe/form/toolbar.js:398 public/js/frappe/model/model.js:737 #: public/js/frappe/views/treeview.js:295 @@ -24959,7 +24994,7 @@ msgid "Rename" msgstr "" #: custom/doctype/custom_field/custom_field.js:115 -#: custom/doctype/custom_field/custom_field.js:132 +#: custom/doctype/custom_field/custom_field.js:133 msgid "Rename Fieldname" msgstr "" @@ -24967,7 +25002,7 @@ msgstr "" msgid "Rename {0}" msgstr "" -#: core/doctype/doctype/doctype.py:688 +#: core/doctype/doctype/doctype.py:684 msgid "Renamed files and replaced code in controllers, please check!" msgstr "" @@ -25274,7 +25309,7 @@ msgctxt "Report" msgid "Report Type" msgstr "" -#: core/doctype/doctype/doctype.py:1750 +#: core/doctype/doctype/doctype.py:1746 msgid "Report cannot be set for Single types" msgstr "" @@ -25312,8 +25347,8 @@ msgstr "" msgid "Report with more than 10 columns looks better in Landscape mode." msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:235 -#: public/js/frappe/ui/toolbar/search_utils.js:236 +#: public/js/frappe/ui/toolbar/search_utils.js:251 +#: public/js/frappe/ui/toolbar/search_utils.js:252 msgid "Report {0}" msgstr "" @@ -25333,7 +25368,7 @@ msgstr "" msgid "Report:" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:531 +#: public/js/frappe/ui/toolbar/search_utils.js:547 msgid "Reports" msgstr "" @@ -25656,8 +25691,8 @@ msgctxt "Title of message showing restrictions in list view" msgid "Restrictions" msgstr "" -#: public/js/frappe/ui/toolbar/awesome_bar.js:354 -#: public/js/frappe/ui/toolbar/awesome_bar.js:369 +#: public/js/frappe/ui/toolbar/awesome_bar.js:356 +#: public/js/frappe/ui/toolbar/awesome_bar.js:371 msgid "Result" msgstr "" @@ -26181,19 +26216,19 @@ msgctxt "Role" msgid "Route: Example \"/desk\"" msgstr "" -#: model/base_document.py:710 model/base_document.py:751 model/document.py:591 +#: model/base_document.py:729 model/base_document.py:770 model/document.py:591 msgid "Row" msgstr "" -#: core/doctype/doctype/doctype.py:1772 core/doctype/doctype/doctype.py:1782 +#: core/doctype/doctype/doctype.py:1768 core/doctype/doctype/doctype.py:1778 msgid "Row # {0}: Non administrator user can not set the role {1} to the custom doctype" msgstr "" -#: model/base_document.py:868 +#: model/base_document.py:887 msgid "Row #{0}:" msgstr "" -#: core/doctype/doctype/doctype.py:492 +#: core/doctype/doctype/doctype.py:488 msgid "Row #{}: Fieldname is required" msgstr "" @@ -26259,7 +26294,7 @@ msgctxt "Energy Point Rule" msgid "Rule Name" msgstr "" -#: permissions.py:662 +#: permissions.py:665 msgid "Rule for this doctype, role, permlevel and if-owner combination already exists." msgstr "" @@ -26508,7 +26543,7 @@ msgstr "" #: desk/page/user_profile/user_profile_controller.js:319 #: printing/page/print/print.js:831 #: printing/page/print_format_builder/print_format_builder.js:160 -#: public/js/frappe/form/footer/form_timeline.js:638 +#: public/js/frappe/form/footer/form_timeline.js:661 #: public/js/frappe/form/quick_entry.js:156 #: public/js/frappe/list/list_settings.js:36 #: public/js/frappe/list/list_settings.js:244 @@ -26520,7 +26555,7 @@ msgstr "" #: public/js/frappe/views/kanban/kanban_view.js:340 #: public/js/frappe/views/reports/query_report.js:1785 #: public/js/frappe/views/reports/report_view.js:1631 -#: public/js/frappe/views/workspace/workspace.js:487 +#: public/js/frappe/views/workspace/workspace.js:493 #: public/js/frappe/widgets/base_widget.js:140 #: public/js/frappe/widgets/quick_list_widget.js:117 #: public/js/print_format_builder/print_format_builder.bundle.js:15 @@ -26565,7 +26600,7 @@ msgctxt "Form Tour" msgid "Save on Completion" msgstr "" -#: public/js/frappe/form/form_tour.js:287 +#: public/js/frappe/form/form_tour.js:289 msgid "Save the document." msgstr "" @@ -26578,7 +26613,7 @@ msgstr "" #: public/js/frappe/list/list_settings.js:40 #: public/js/frappe/views/kanban/kanban_settings.js:47 -#: public/js/frappe/views/workspace/workspace.js:499 +#: public/js/frappe/views/workspace/workspace.js:505 msgid "Saving" msgstr "" @@ -26847,6 +26882,12 @@ msgstr "" msgid "Search" msgstr "" +#. Label of a Section Break field in DocType 'System Settings' +#: core/doctype/system_settings/system_settings.json +msgctxt "System Settings" +msgid "Search" +msgstr "" + #. Label of a Check field in DocType 'Role' #: core/doctype/role/role.json msgctxt "Role" @@ -26879,7 +26920,7 @@ msgstr "" msgid "Search Results for" msgstr "" -#: core/doctype/doctype/doctype.py:1418 +#: core/doctype/doctype/doctype.py:1414 msgid "Search field {0} is not valid" msgstr "" @@ -27179,7 +27220,7 @@ msgstr "" msgid "Select Filters" msgstr "" -#: desk/doctype/event/event.py:96 +#: desk/doctype/event/event.py:97 msgid "Select Google Calendar to which event should be synced." msgstr "" @@ -27278,15 +27319,15 @@ msgstr "" msgid "Select a group node first." msgstr "" -#: core/doctype/doctype/doctype.py:1885 +#: core/doctype/doctype/doctype.py:1881 msgid "Select a valid Sender Field for creating documents from Email" msgstr "" -#: core/doctype/doctype/doctype.py:1869 +#: core/doctype/doctype/doctype.py:1865 msgid "Select a valid Subject field for creating documents from Email" msgstr "" -#: public/js/frappe/form/form_tour.js:313 +#: public/js/frappe/form/form_tour.js:315 msgid "Select an Image" msgstr "" @@ -27324,6 +27365,10 @@ msgstr "" msgid "Select records for assignment" msgstr "" +#: public/js/frappe/list/bulk_operations.js:216 +msgid "Select records for removing assignment" +msgstr "" + #. Description of the 'Insert After' (Select) field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" @@ -27625,7 +27670,7 @@ msgctxt "DocType" msgid "Sender Email Field" msgstr "" -#: core/doctype/doctype/doctype.py:1888 +#: core/doctype/doctype/doctype.py:1884 msgid "Sender Field should have Email in options" msgstr "" @@ -27763,7 +27808,7 @@ msgstr "" msgid "Series counter for {} updated to {} successfully" msgstr "" -#: core/doctype/doctype/doctype.py:1073 +#: core/doctype/doctype/doctype.py:1069 #: core/doctype/document_naming_settings/document_naming_settings.py:171 msgid "Series {0} already used in {1}" msgstr "" @@ -27874,7 +27919,7 @@ msgctxt "System Settings" msgid "Session Expiry (idle timeout)" msgstr "" -#: core/doctype/system_settings/system_settings.py:110 +#: core/doctype/system_settings/system_settings.py:112 msgid "Session Expiry must be in format {0}" msgstr "" @@ -28092,7 +28137,7 @@ msgstr "" #. Label of a Card Break in the Integrations Workspace #: integrations/workspace/integrations/integrations.json #: public/js/frappe/ui/toolbar/toolbar.js:254 -#: public/js/frappe/views/workspace/workspace.js:515 +#: public/js/frappe/views/workspace/workspace.js:521 msgid "Settings" msgstr "" @@ -28128,7 +28173,7 @@ msgid "Settings Dropdown" msgstr "" #. Label of a Card Break in the Website Workspace -#: public/js/frappe/ui/toolbar/search_utils.js:551 +#: public/js/frappe/ui/toolbar/search_utils.js:567 #: website/workspace/website/website.json msgid "Setup" msgstr "" @@ -28675,7 +28720,7 @@ msgstr "" msgid "Single DocTypes cannot be customized." msgstr "" -#: core/doctype/doctype/doctype_list.js:51 +#: core/doctype/doctype/doctype_list.js:67 msgid "Single Types have only one record no tables associated. Values are stored in tabSingles" msgstr "" @@ -28685,7 +28730,7 @@ msgctxt "DocType" msgid "Single Types have only one record no tables associated. Values are stored in tabSingles" msgstr "" -#: database/database.py:230 +#: database/database.py:238 msgid "Site is running in read only mode for maintenance or site update, this action can not be performed right now. Please try again later." msgstr "" @@ -28928,11 +28973,11 @@ msgctxt "Customize Form" msgid "Sort Order" msgstr "" -#: core/doctype/doctype/doctype.py:1501 +#: core/doctype/doctype/doctype.py:1497 msgid "Sort field {0} must be a valid fieldname" msgstr "" -#: public/js/frappe/utils/utils.js:1705 +#: public/js/frappe/utils/utils.js:1706 #: website/report/website_analytics/website_analytics.js:38 msgid "Source" msgstr "" @@ -29049,7 +29094,7 @@ msgstr "" msgid "Standard DocType can not be deleted." msgstr "" -#: core/doctype/doctype/doctype.py:228 +#: core/doctype/doctype/doctype.py:224 msgid "Standard DocType cannot have default print format, use Customize Form" msgstr "" @@ -29599,7 +29644,7 @@ msgctxt "DocType" msgid "Subject Field" msgstr "" -#: core/doctype/doctype/doctype.py:1878 +#: core/doctype/doctype/doctype.py:1874 msgid "Subject Field type should be Data, Text, Long Text, Small Text, Text Editor" msgstr "" @@ -29618,7 +29663,7 @@ msgstr "" msgid "Submit" msgstr "" -#: public/js/frappe/list/list_view.js:1916 +#: public/js/frappe/list/list_view.js:1940 msgctxt "Button in list view actions menu" msgid "Submit" msgstr "" @@ -29705,7 +29750,7 @@ msgstr "" msgid "Submit this document to confirm" msgstr "" -#: public/js/frappe/list/list_view.js:1921 +#: public/js/frappe/list/list_view.js:1945 msgctxt "Title of confirmation dialog" msgid "Submit {0} documents?" msgstr "" @@ -29766,7 +29811,7 @@ msgstr "" #: core/doctype/data_import/data_import.js:470 #: desk/doctype/bulk_update/bulk_update.js:31 #: desk/doctype/desktop_icon/desktop_icon.py:452 -#: public/js/frappe/form/grid.js:1133 +#: public/js/frappe/form/grid.js:1135 #: public/js/frappe/views/translation_manager.js:21 #: templates/pages/integrations/gcalendar-success.html:9 #: workflow/doctype/workflow_action/workflow_action.py:171 @@ -30034,7 +30079,7 @@ msgstr "" msgid "Syncing {0} of {1}" msgstr "" -#: utils/data.py:2424 +#: utils/data.py:2418 msgid "Syntax Error" msgstr "" @@ -30292,7 +30337,7 @@ msgctxt "DocType Link" msgid "Table Fieldname" msgstr "" -#: core/doctype/doctype/doctype.py:1154 +#: core/doctype/doctype/doctype.py:1150 msgid "Table Fieldname Missing" msgstr "" @@ -30320,7 +30365,7 @@ msgctxt "DocField" msgid "Table MultiSelect" msgstr "" -#: public/js/frappe/form/grid.js:1132 +#: public/js/frappe/form/grid.js:1134 msgid "Table updated" msgstr "" @@ -30345,14 +30390,14 @@ msgid "Tag Link" msgstr "" #: model/__init__.py:148 model/meta.py:52 -#: public/js/frappe/list/bulk_operations.js:365 +#: public/js/frappe/list/bulk_operations.js:386 #: public/js/frappe/list/list_sidebar.js:226 public/js/frappe/model/meta.js:204 #: public/js/frappe/model/model.js:123 #: public/js/frappe/ui/toolbar/awesome_bar.js:171 msgid "Tags" msgstr "" -#: integrations/doctype/google_drive/google_drive.js:28 +#: integrations/doctype/google_drive/google_drive.js:29 msgid "Take Backup" msgstr "" @@ -30573,7 +30618,7 @@ msgstr "" msgid "The Auto Repeat for this document has been disabled." msgstr "" -#: public/js/frappe/form/grid.js:1155 +#: public/js/frappe/form/grid.js:1157 msgid "The CSV format is case sensitive" msgstr "" @@ -30622,7 +30667,7 @@ msgid "" "" msgstr "" -#: database/database.py:388 +#: database/database.py:424 msgid "The changes have been reverted." msgstr "" @@ -30664,6 +30709,10 @@ msgstr "" msgid "The fieldname you've specified in Attached To Field is invalid" msgstr "" +#: automation/doctype/assignment_rule/assignment_rule.py:61 +msgid "The following Assignment Days have been repeated: {0}" +msgstr "" + #: core/doctype/data_import/importer.py:1035 msgid "The following values are invalid: {0}. Values must be one of {1}" msgstr "" @@ -30755,7 +30804,7 @@ msgstr "" msgid "The system is being updated. Please refresh again after a few moments." msgstr "" -#: public/js/frappe/form/grid_row.js:615 +#: public/js/frappe/form/grid_row.js:635 msgid "The total column width cannot be more than 10." msgstr "" @@ -30822,7 +30871,7 @@ msgstr "" msgid "There can be only 9 Page Break fields in a Web Form" msgstr "" -#: core/doctype/doctype/doctype.py:1394 +#: core/doctype/doctype/doctype.py:1390 msgid "There can be only one Fold in a form" msgstr "" @@ -30862,7 +30911,7 @@ msgstr "" msgid "There were errors while creating the document. Please try again." msgstr "" -#: public/js/frappe/views/communication.js:728 +#: public/js/frappe/views/communication.js:770 msgid "There were errors while sending email. Please try again." msgstr "" @@ -30913,7 +30962,7 @@ msgstr "" msgid "This Kanban Board will be private" msgstr "" -#: __init__.py:917 +#: __init__.py:957 msgid "This action is only allowed for {}" msgstr "" @@ -31350,11 +31399,11 @@ msgctxt "Activity Log" msgid "Timeline Name" msgstr "" -#: core/doctype/doctype/doctype.py:1489 +#: core/doctype/doctype/doctype.py:1485 msgid "Timeline field must be a Link or Dynamic Link" msgstr "" -#: core/doctype/doctype/doctype.py:1485 +#: core/doctype/doctype/doctype.py:1481 msgid "Timeline field must be a valid fieldname" msgstr "" @@ -31398,9 +31447,9 @@ msgid "Timestamp" msgstr "" #: public/js/form_builder/store.js:89 -#: public/js/frappe/views/workspace/workspace.js:599 -#: public/js/frappe/views/workspace/workspace.js:928 -#: public/js/frappe/views/workspace/workspace.js:1175 +#: public/js/frappe/views/workspace/workspace.js:605 +#: public/js/frappe/views/workspace/workspace.js:934 +#: public/js/frappe/views/workspace/workspace.js:1181 msgid "Title" msgstr "" @@ -31542,7 +31591,7 @@ msgctxt "Website Settings" msgid "Title Prefix" msgstr "" -#: core/doctype/doctype/doctype.py:1426 +#: core/doctype/doctype/doctype.py:1422 msgid "Title field must be a valid fieldname" msgstr "" @@ -31800,7 +31849,7 @@ msgstr "" msgid "Too Many Requests" msgstr "" -#: database/database.py:387 +#: database/database.py:423 msgid "Too many changes to database in single action." msgstr "" @@ -31999,7 +32048,7 @@ msgid "" "Note: If you're sending to multiple recipients, even if 1 recipient reads the email, it'll be considered \"Opened\"" msgstr "" -#: public/js/frappe/utils/utils.js:1744 +#: public/js/frappe/utils/utils.js:1745 msgid "Tracking URL generated and copied to clipboard" msgstr "" @@ -32475,7 +32524,7 @@ msgstr "" msgid "Unhandled Email" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:556 +#: public/js/frappe/views/workspace/workspace.js:562 msgid "Unhide Workspace" msgstr "" @@ -32505,7 +32554,7 @@ msgstr "" msgid "Unknown Rounding Method: {}" msgstr "" -#: auth.py:299 +#: auth.py:301 msgid "Unknown User" msgstr "" @@ -32604,7 +32653,7 @@ msgstr "" msgid "Unzipping files..." msgstr "" -#: desk/doctype/event/event.py:258 +#: desk/doctype/event/event.py:259 msgid "Upcoming Events for Today" msgstr "" @@ -32617,7 +32666,7 @@ msgstr "" #: printing/page/print_format_builder/print_format_builder.js:670 #: printing/page/print_format_builder/print_format_builder.js:757 #: public/js/frappe/form/grid_row.js:402 -#: public/js/frappe/views/workspace/workspace.js:647 +#: public/js/frappe/views/workspace/workspace.js:653 msgid "Update" msgstr "" @@ -32633,7 +32682,7 @@ msgctxt "Document Naming Settings" msgid "Update Amendment Naming" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:596 +#: public/js/frappe/views/workspace/workspace.js:602 msgid "Update Details" msgstr "" @@ -32692,7 +32741,7 @@ msgctxt "Workflow Document State" msgid "Update Value" msgstr "" -#: public/js/frappe/list/bulk_operations.js:310 +#: public/js/frappe/list/bulk_operations.js:331 msgid "Update {0} records" msgstr "" @@ -32721,7 +32770,7 @@ msgstr "" msgid "Updated To A New Version 🎉" msgstr "" -#: public/js/frappe/list/bulk_operations.js:307 +#: public/js/frappe/list/bulk_operations.js:328 msgid "Updated successfully" msgstr "" @@ -32785,6 +32834,18 @@ msgctxt "File" msgid "Uploaded To Google Drive" msgstr "" +#: integrations/doctype/google_drive/google_drive.py:199 +msgid "Uploading backup to Google Drive." +msgstr "" + +#: integrations/doctype/google_drive/google_drive.py:204 +msgid "Uploading successful." +msgstr "" + +#: integrations/doctype/google_drive/google_drive.js:16 +msgid "Uploading to Google Drive" +msgstr "" + #. Description of the 'Value to Validate' (Data) field in DocType 'Onboarding #. Step' #: desk/doctype/onboarding_step/onboarding_step.json @@ -33230,7 +33291,7 @@ msgid "User Permissions" msgstr "" #: core/doctype/user_permission/user_permission_list.js:124 -msgid "User Permissions created sucessfully" +msgid "User Permissions created successfully" msgstr "" #. Label of a shortcut in the Users Workspace @@ -33456,8 +33517,8 @@ msgid "Validity" msgstr "" #: email/doctype/auto_email_report/auto_email_report.js:92 -#: public/js/frappe/list/bulk_operations.js:271 -#: public/js/frappe/list/bulk_operations.js:333 +#: public/js/frappe/list/bulk_operations.js:292 +#: public/js/frappe/list/bulk_operations.js:354 msgid "Value" msgstr "" @@ -33534,7 +33595,7 @@ msgctxt "Notification" msgid "Value To Be Set" msgstr "" -#: model/base_document.py:930 model/document.py:648 +#: model/base_document.py:949 model/document.py:648 msgid "Value cannot be changed for {0}" msgstr "" @@ -33554,7 +33615,7 @@ msgstr "" msgid "Value for field {0} is too long in {1}. Length should be lesser than {2} characters" msgstr "" -#: model/base_document.py:360 +#: model/base_document.py:379 msgid "Value for {0} cannot be a list" msgstr "" @@ -33565,7 +33626,7 @@ msgctxt "Assignment Rule" msgid "Value from this field will be set as the due date in the ToDo" msgstr "" -#: model/base_document.py:712 +#: model/base_document.py:731 msgid "Value missing for" msgstr "" @@ -33579,7 +33640,7 @@ msgctxt "Onboarding Step" msgid "Value to Validate" msgstr "" -#: model/base_document.py:997 +#: model/base_document.py:1016 msgid "Value too big" msgstr "" @@ -33909,7 +33970,7 @@ msgstr "" msgid "Web Page Block" msgstr "" -#: public/js/frappe/utils/utils.js:1697 +#: public/js/frappe/utils/utils.js:1698 msgid "Web Page URL" msgstr "" @@ -34122,7 +34183,7 @@ msgctxt "DocType" msgid "Website Search Field" msgstr "" -#: core/doctype/doctype/doctype.py:1473 +#: core/doctype/doctype/doctype.py:1469 msgid "Website Search Field must be a valid fieldname" msgstr "" @@ -34405,7 +34466,7 @@ msgctxt "System Settings" msgid "When uploading files, force the use of the web-based image capture. If this is unchecked, the default behavior is to use the mobile native camera when use from a mobile is detected." msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:440 +#: public/js/frappe/widgets/widget_dialog.js:446 msgid "Which view of the associated DocType should this shortcut take you to?" msgstr "" @@ -34637,7 +34698,7 @@ msgstr "" #. Name of a DocType #: desk/doctype/workspace/workspace.json -#: public/js/frappe/ui/toolbar/search_utils.js:541 +#: public/js/frappe/ui/toolbar/search_utils.js:557 #: public/js/frappe/views/workspace/workspace.js:10 msgid "Workspace" msgstr "" @@ -34694,15 +34755,19 @@ msgstr "" msgid "Workspace Shortcut" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:1265 +#: desk/doctype/workspace/workspace.py:283 +msgid "Workspace not found" +msgstr "" + +#: public/js/frappe/views/workspace/workspace.js:1271 msgid "Workspace {0} Created Successfully" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:894 +#: public/js/frappe/views/workspace/workspace.js:900 msgid "Workspace {0} Deleted Successfully" msgstr "" -#: public/js/frappe/views/workspace/workspace.js:672 +#: public/js/frappe/views/workspace/workspace.js:678 msgid "Workspace {0} Edited Successfully" msgstr "" @@ -34736,7 +34801,7 @@ msgctxt "User Document Type" msgid "Write" msgstr "" -#: model/base_document.py:840 +#: model/base_document.py:859 msgid "Wrong Fetch From value" msgstr "" @@ -34861,7 +34926,7 @@ msgstr "" #: integrations/doctype/webhook/webhook.py:127 #: integrations/doctype/webhook/webhook.py:137 #: public/js/form_builder/utils.js:336 -#: public/js/frappe/form/controls/link.js:471 +#: public/js/frappe/form/controls/link.js:472 #: public/js/frappe/list/list_sidebar_group_by.js:223 #: public/js/frappe/views/reports/query_report.js:1513 #: website/doctype/help_article/templates/help_article.html:25 @@ -34916,11 +34981,11 @@ msgstr "" msgid "You are connected to internet." msgstr "" -#: permissions.py:417 +#: permissions.py:420 msgid "You are not allowed to access this {0} record because it is linked to {1} '{2}' in field {3}" msgstr "" -#: permissions.py:406 +#: permissions.py:409 msgid "You are not allowed to access this {0} record because it is linked to {1} '{2}' in row {3}, field {4}" msgstr "" @@ -34940,7 +35005,7 @@ msgstr "" msgid "You are not allowed to edit the report." msgstr "" -#: permissions.py:614 +#: permissions.py:617 msgid "You are not allowed to export {} doctype" msgstr "" @@ -34948,7 +35013,7 @@ msgstr "" msgid "You are not allowed to print this report" msgstr "" -#: public/js/frappe/views/communication.js:673 +#: public/js/frappe/views/communication.js:715 msgid "You are not allowed to send emails related to this document" msgstr "" @@ -34968,7 +35033,7 @@ msgstr "" msgid "You are not permitted to access this page." msgstr "" -#: __init__.py:834 +#: __init__.py:874 msgid "You are not permitted to access this resource." msgstr "" @@ -35021,7 +35086,7 @@ msgstr "" msgid "You can manually remove the lock if you think it's safe: {}" msgstr "" -#: public/js/frappe/form/controls/markdown_editor.js:74 +#: public/js/frappe/form/controls/markdown_editor.js:75 msgid "You can only insert images in Markdown fields" msgstr "" @@ -35169,7 +35234,7 @@ msgstr "" msgid "You have hit the row size limit on database table: {0}" msgstr "" -#: public/js/frappe/list/bulk_operations.js:347 +#: public/js/frappe/list/bulk_operations.js:368 msgid "You have not entered a value. The field will be set to empty." msgstr "" @@ -35202,7 +35267,7 @@ msgstr "" msgid "You last edited this" msgstr "" -#: public/js/frappe/widgets/widget_dialog.js:308 +#: public/js/frappe/widgets/widget_dialog.js:314 msgid "You must add atleast one link." msgstr "" @@ -35214,7 +35279,7 @@ msgstr "" msgid "You must login to submit this form" msgstr "" -#: desk/doctype/workspace/workspace.py:69 +#: desk/doctype/workspace/workspace.py:72 msgid "You need to be Workspace Manager to edit this document" msgstr "" @@ -35310,11 +35375,11 @@ msgstr "" msgid "Your account has been deleted" msgstr "" -#: auth.py:465 +#: auth.py:476 msgid "Your account has been locked and will resume after {0} seconds" msgstr "" -#: desk/form/assign_to.py:268 +#: desk/form/assign_to.py:282 msgid "Your assignment on {0} {1} has been removed by {2}" msgstr "" @@ -35389,6 +35454,10 @@ msgctxt "Desktop Icon" msgid "_report" msgstr "" +#: database/database.py:315 +msgid "`as_iterator` only works with `as_list=True` or `as_dict=True`" +msgstr "" + #: utils/background_jobs.py:94 msgid "`job_id` paramater is required for deduplication." msgstr "" @@ -35440,7 +35509,7 @@ msgctxt "Permission Inspector" msgid "amend" msgstr "" -#: public/js/frappe/utils/utils.js:396 utils/data.py:1528 +#: public/js/frappe/utils/utils.js:396 utils/data.py:1526 msgid "and" msgstr "" @@ -35542,7 +35611,7 @@ msgctxt "Workflow State" msgid "bullhorn" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:270 +#: public/js/frappe/ui/toolbar/search_utils.js:286 msgid "calendar" msgstr "" @@ -35660,7 +35729,7 @@ msgctxt "Workspace" msgid "cyan" msgstr "" -#: public/js/frappe/utils/utils.js:1113 +#: public/js/frappe/utils/utils.js:1114 msgctxt "Days (Field: Duration)" msgid "d" msgstr "" @@ -35810,12 +35879,12 @@ msgctxt "Social Link Settings" msgid "email" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:289 +#: public/js/frappe/ui/toolbar/search_utils.js:305 msgid "email inbox" msgstr "" -#: permissions.py:411 permissions.py:422 -#: public/js/frappe/form/controls/link.js:479 +#: permissions.py:414 permissions.py:425 +#: public/js/frappe/form/controls/link.js:481 msgid "empty" msgstr "" @@ -35994,11 +36063,11 @@ msgctxt "Workspace" msgid "grey" msgstr "" -#: utils/backups.py:375 +#: utils/backups.py:379 msgid "gzip not found in PATH! This is required to take a backup." msgstr "" -#: public/js/frappe/utils/utils.js:1117 +#: public/js/frappe/utils/utils.js:1118 msgctxt "Hours (Field: Duration)" msgid "h" msgstr "" @@ -36051,7 +36120,7 @@ msgctxt "Workflow State" msgid "home" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:280 +#: public/js/frappe/ui/toolbar/search_utils.js:296 msgid "hub" msgstr "" @@ -36181,7 +36250,7 @@ msgctxt "RQ Worker" msgid "long" msgstr "" -#: public/js/frappe/utils/utils.js:1121 +#: public/js/frappe/utils/utils.js:1122 msgctxt "Minutes (Field: Duration)" msgid "m" msgstr "" @@ -36253,7 +36322,7 @@ msgctxt "Workflow State" msgid "music" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:144 +#: public/js/frappe/ui/toolbar/search_utils.js:160 msgid "new" msgstr "" @@ -36357,10 +36426,6 @@ msgstr "" msgid "one of" msgstr "" -#: utils/data.py:1535 -msgid "only." -msgstr "" - #: public/js/frappe/utils/utils.js:393 www/login.html:87 msgid "or" msgstr "" @@ -36597,7 +36662,7 @@ msgctxt "Workflow State" msgid "road" msgstr "" -#: public/js/frappe/utils/utils.js:1125 +#: public/js/frappe/utils/utils.js:1126 msgctxt "Seconds (Field: Duration)" msgid "s" msgstr "" @@ -36888,7 +36953,7 @@ msgctxt "Audit Trail" msgid "version_table" msgstr "" -#: automation/doctype/assignment_rule/assignment_rule.py:386 +#: automation/doctype/assignment_rule/assignment_rule.py:383 msgid "via Assignment Rule" msgstr "" @@ -36991,21 +37056,15 @@ msgctxt "Workflow State" msgid "zoom-out" msgstr "" -#: desk/doctype/event/event.js:83 -#: integrations/doctype/google_drive/google_drive.js:19 +#: desk/doctype/event/event.js:87 msgid "{0}" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:81 -#: public/js/frappe/ui/toolbar/search_utils.js:82 -msgid "{0} ${label}" -msgstr "" - -#: public/js/frappe/ui/toolbar/search_utils.js:177 +#: public/js/frappe/ui/toolbar/search_utils.js:193 msgid "{0} ${skip_list ? \"\" : type}" msgstr "" -#: public/js/frappe/ui/toolbar/search_utils.js:182 +#: public/js/frappe/ui/toolbar/search_utils.js:198 msgid "{0} ${type}" msgstr "" @@ -37022,8 +37081,8 @@ msgstr "" msgid "{0} ({1}) - {2}%" msgstr "" -#: public/js/frappe/ui/toolbar/awesome_bar.js:346 -#: public/js/frappe/ui/toolbar/awesome_bar.js:349 +#: public/js/frappe/ui/toolbar/awesome_bar.js:348 +#: public/js/frappe/ui/toolbar/awesome_bar.js:351 msgid "{0} = {1}" msgstr "" @@ -37036,9 +37095,9 @@ msgid "{0} Chart" msgstr "" #: core/page/dashboard_view/dashboard_view.js:67 -#: public/js/frappe/ui/toolbar/search_utils.js:331 -#: public/js/frappe/ui/toolbar/search_utils.js:332 -#: public/js/frappe/utils/utils.js:929 +#: public/js/frappe/ui/toolbar/search_utils.js:347 +#: public/js/frappe/ui/toolbar/search_utils.js:348 +#: public/js/frappe/utils/utils.js:930 #: public/js/frappe/views/dashboard/dashboard_view.js:10 msgid "{0} Dashboard" msgstr "" @@ -37061,7 +37120,9 @@ msgstr "" msgid "{0} Liked" msgstr "" -#: public/js/frappe/utils/utils.js:923 +#: public/js/frappe/ui/toolbar/search_utils.js:83 +#: public/js/frappe/ui/toolbar/search_utils.js:84 +#: public/js/frappe/utils/utils.js:924 #: public/js/frappe/widgets/chart_widget.js:317 www/list.html:4 www/list.html:8 msgid "{0} List" msgstr "" @@ -37074,7 +37135,7 @@ msgstr "" msgid "{0} Map" msgstr "" -#: public/js/frappe/utils/utils.js:926 +#: public/js/frappe/utils/utils.js:927 msgid "{0} Modules" msgstr "" @@ -37082,11 +37143,13 @@ msgstr "" msgid "{0} Name" msgstr "" -#: model/base_document.py:1027 +#: model/base_document.py:1046 msgid "{0} Not allowed to change {1} after submission from {2} to {3}" msgstr "" -#: public/js/frappe/utils/utils.js:920 +#: public/js/frappe/ui/toolbar/search_utils.js:95 +#: public/js/frappe/ui/toolbar/search_utils.js:96 +#: public/js/frappe/utils/utils.js:921 #: public/js/frappe/widgets/chart_widget.js:325 msgid "{0} Report" msgstr "" @@ -37096,6 +37159,8 @@ msgstr "" msgid "{0} Settings" msgstr "" +#: public/js/frappe/ui/toolbar/search_utils.js:87 +#: public/js/frappe/ui/toolbar/search_utils.js:88 #: public/js/frappe/views/treeview.js:139 msgid "{0} Tree" msgstr "" @@ -37109,6 +37174,11 @@ msgstr "" msgid "{0} Web page views" msgstr "" +#: public/js/frappe/ui/toolbar/search_utils.js:91 +#: public/js/frappe/ui/toolbar/search_utils.js:92 +msgid "{0} Workspace" +msgstr "" + #: public/js/frappe/form/link_selector.js:225 msgid "{0} added" msgstr "" @@ -37125,7 +37195,7 @@ msgstr "" msgid "{0} already unsubscribed for {1} {2}" msgstr "" -#: utils/data.py:1715 +#: utils/data.py:1709 msgid "{0} and {1}" msgstr "" @@ -37163,7 +37233,7 @@ msgstr "" msgid "{0} are required" msgstr "" -#: desk/form/assign_to.py:275 +#: desk/form/assign_to.py:289 msgid "{0} assigned a new task {1} {2} to you" msgstr "" @@ -37176,6 +37246,10 @@ msgctxt "Form timeline" msgid "{0} attached {1}" msgstr "" +#: core/doctype/system_settings/system_settings.py:139 +msgid "{0} can not be more than {1}" +msgstr "" + #: public/js/frappe/form/footer/version_timeline_content_builder.js:77 msgid "{0} cancelled this document" msgstr "" @@ -37310,7 +37384,7 @@ msgstr "" msgid "{0} has left the conversation in {1} {2}" msgstr "" -#: __init__.py:2373 +#: __init__.py:2427 msgid "{0} has no versions tracked." msgstr "" @@ -37327,7 +37401,7 @@ msgstr "" msgid "{0} in row {1} cannot have both URL and child items" msgstr "" -#: core/doctype/doctype/doctype.py:916 +#: core/doctype/doctype/doctype.py:912 msgid "{0} is a mandatory field" msgstr "" @@ -37335,7 +37409,7 @@ msgstr "" msgid "{0} is a not a valid zip file" msgstr "" -#: core/doctype/doctype/doctype.py:1559 +#: core/doctype/doctype/doctype.py:1555 msgid "{0} is an invalid Data field." msgstr "" @@ -37412,11 +37486,11 @@ msgstr "" msgid "{0} is not a valid Workflow State. Please update your Workflow and try again." msgstr "" -#: permissions.py:795 +#: permissions.py:798 msgid "{0} is not a valid parent DocType for {1}" msgstr "" -#: permissions.py:815 +#: permissions.py:818 msgid "{0} is not a valid parentfield for {1}" msgstr "" @@ -37506,11 +37580,11 @@ msgstr "" msgid "{0} must be one of {1}" msgstr "" -#: model/base_document.py:771 +#: model/base_document.py:790 msgid "{0} must be set first" msgstr "" -#: model/base_document.py:629 +#: model/base_document.py:648 msgid "{0} must be unique" msgstr "" @@ -37544,7 +37618,12 @@ msgstr "" msgid "{0} of {1} sent" msgstr "" -#: utils/data.py:1705 +#: utils/data.py:1529 +msgctxt "Money in words" +msgid "{0} only." +msgstr "" + +#: utils/data.py:1699 msgid "{0} or {1}" msgstr "" @@ -37612,7 +37691,7 @@ msgstr "" msgid "{0} shared this document with {1}" msgstr "" -#: core/doctype/doctype/doctype.py:320 +#: core/doctype/doctype/doctype.py:316 msgid "{0} should be indexed because it's referred in dashboard connections" msgstr "" @@ -37684,11 +37763,11 @@ msgstr "" msgid "{0} {1} added to Dashboard {2}" msgstr "" -#: model/base_document.py:562 model/rename_doc.py:112 +#: model/base_document.py:581 model/rename_doc.py:112 msgid "{0} {1} already exists" msgstr "" -#: model/base_document.py:873 +#: model/base_document.py:892 msgid "{0} {1} cannot be \"{2}\". It should be one of \"{3}\"" msgstr "" @@ -37704,7 +37783,7 @@ msgstr "" msgid "{0} {1} is linked with the following submitted documents: {2}" msgstr "" -#: model/document.py:170 permissions.py:566 +#: model/document.py:170 permissions.py:569 msgid "{0} {1} not found" msgstr "" @@ -37712,39 +37791,39 @@ msgstr "" msgid "{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first." msgstr "" -#: model/base_document.py:988 +#: model/base_document.py:1007 msgid "{0}, Row {1}" msgstr "" -#: model/base_document.py:993 +#: model/base_document.py:1012 msgid "{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}" msgstr "" -#: core/doctype/doctype/doctype.py:1741 +#: core/doctype/doctype/doctype.py:1737 msgid "{0}: Cannot set Amend without Cancel" msgstr "" -#: core/doctype/doctype/doctype.py:1759 +#: core/doctype/doctype/doctype.py:1755 msgid "{0}: Cannot set Assign Amend if not Submittable" msgstr "" -#: core/doctype/doctype/doctype.py:1757 +#: core/doctype/doctype/doctype.py:1753 msgid "{0}: Cannot set Assign Submit if not Submittable" msgstr "" -#: core/doctype/doctype/doctype.py:1736 +#: core/doctype/doctype/doctype.py:1732 msgid "{0}: Cannot set Cancel without Submit" msgstr "" -#: core/doctype/doctype/doctype.py:1743 +#: core/doctype/doctype/doctype.py:1739 msgid "{0}: Cannot set Import without Create" msgstr "" -#: core/doctype/doctype/doctype.py:1739 +#: core/doctype/doctype/doctype.py:1735 msgid "{0}: Cannot set Submit, Cancel, Amend without Write" msgstr "" -#: core/doctype/doctype/doctype.py:1763 +#: core/doctype/doctype/doctype.py:1759 msgid "{0}: Cannot set import as {1} is not importable" msgstr "" @@ -37752,47 +37831,47 @@ msgstr "" msgid "{0}: Failed to attach new recurring document. To enable attaching document in the auto repeat notification email, enable {1} in Print Settings" msgstr "" -#: core/doctype/doctype/doctype.py:1377 +#: core/doctype/doctype/doctype.py:1373 msgid "{0}: Field '{1}' cannot be set as Unique as it has non-unique values" msgstr "" -#: core/doctype/doctype/doctype.py:1285 +#: core/doctype/doctype/doctype.py:1281 msgid "{0}: Field {1} in row {2} cannot be hidden and mandatory without default" msgstr "" -#: core/doctype/doctype/doctype.py:1244 +#: core/doctype/doctype/doctype.py:1240 msgid "{0}: Field {1} of type {2} cannot be mandatory" msgstr "" -#: core/doctype/doctype/doctype.py:1232 +#: core/doctype/doctype/doctype.py:1228 msgid "{0}: Fieldname {1} appears multiple times in rows {2}" msgstr "" -#: core/doctype/doctype/doctype.py:1362 +#: core/doctype/doctype/doctype.py:1358 msgid "{0}: Fieldtype {1} for {2} cannot be unique" msgstr "" -#: core/doctype/doctype/doctype.py:1698 +#: core/doctype/doctype/doctype.py:1694 msgid "{0}: No basic permissions set" msgstr "" -#: core/doctype/doctype/doctype.py:1712 +#: core/doctype/doctype/doctype.py:1708 msgid "{0}: Only one rule allowed with the same Role, Level and {1}" msgstr "" -#: core/doctype/doctype/doctype.py:1266 +#: core/doctype/doctype/doctype.py:1262 msgid "{0}: Options must be a valid DocType for field {1} in row {2}" msgstr "" -#: core/doctype/doctype/doctype.py:1255 +#: core/doctype/doctype/doctype.py:1251 msgid "{0}: Options required for Link or Table type field {1} in row {2}" msgstr "" -#: core/doctype/doctype/doctype.py:1273 +#: core/doctype/doctype/doctype.py:1269 msgid "{0}: Options {1} must be the same as doctype name {2} for the field {3}" msgstr "" -#: core/doctype/doctype/doctype.py:1727 +#: core/doctype/doctype/doctype.py:1723 msgid "{0}: Permission at level 0 must be set before higher levels are set" msgstr "" @@ -37800,7 +37879,7 @@ msgstr "" msgid "{0}: You can increase the limit for the field if required via {1}" msgstr "" -#: core/doctype/doctype/doctype.py:1219 +#: core/doctype/doctype/doctype.py:1215 msgid "{0}: fieldname cannot be set to reserved keyword {1}" msgstr "" @@ -37818,7 +37897,7 @@ msgstr "" msgid "{0}: {1} vs {2}" msgstr "" -#: core/doctype/doctype/doctype.py:1385 +#: core/doctype/doctype/doctype.py:1381 msgid "{0}:Fieldtype {1} for {2} cannot be indexed" msgstr "" @@ -37838,7 +37917,7 @@ msgstr "" msgid "{count} rows selected" msgstr "" -#: core/doctype/doctype/doctype.py:1439 +#: core/doctype/doctype/doctype.py:1435 msgid "{{{0}}} is not a valid fieldname pattern. It should be {{field_name}}." msgstr "" @@ -37846,11 +37925,11 @@ msgstr "" msgid "{} Complete" msgstr "" -#: utils/data.py:2418 +#: utils/data.py:2412 msgid "{} Invalid python code on line {}" msgstr "" -#: utils/data.py:2427 +#: utils/data.py:2421 msgid "{} Possibly invalid python code.
{}" msgstr "" @@ -37871,7 +37950,7 @@ msgstr "" msgid "{} is not a valid date string." msgstr "" -#: commands/utils.py:519 +#: commands/utils.py:532 msgid "{} not found in PATH! This is required to access the console." msgstr "" @@ -37879,7 +37958,7 @@ msgstr "" msgid "{} not found in PATH! This is required to restore the database." msgstr "" -#: utils/backups.py:441 +#: utils/backups.py:445 msgid "{} not found in PATH! This is required to take a backup." msgstr "" From 3990da0620e2a820a2f152500465179da21dd204 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 30 Jan 2024 09:39:59 +0530 Subject: [PATCH 39/95] feat: show doctype description in list views (#24583) --- frappe/public/js/frappe/list/list_view.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 7ba1898fa4..eb82f3338f 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -465,7 +465,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { ? __("No {0} found with matching filters. Clear filters to see all {0}.", [ __(this.doctype), ]) + : this.meta.description + ? __(this.meta.description) : __("You haven't created a {0} yet", [__(this.doctype)]); + let new_button_label = has_filters_set ? __("Create a new {0}", [__(this.doctype)], "Create a new document from list view") : __( From d53a0ae45eb2ac63d55198b5d604f4f6f663dd94 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 30 Jan 2024 13:09:09 +0530 Subject: [PATCH 40/95] Revert "fix(module_map): only include apps installed on the site - not everything on the bench" (#24605) --- frappe/__init__.py | 39 ++++++--------------------------------- frappe/commands/site.py | 7 ++++--- frappe/installer.py | 4 ++-- 3 files changed, 12 insertions(+), 38 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index ec55af2081..289e5c6e3d 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -202,19 +202,8 @@ if TYPE_CHECKING: # pragma: no cover # end: static analysis hack -def init( - site: str, sites_path: str = ".", new_site: bool = False, force=False, site_ready: bool = True -) -> None: - """ - Initialize frappe for the current site. Reset thread locals `frappe.local` - - :param site: Site name. - :param sites_path: Path to sites directory. - :param new_site: Sets a flag to indicate a new site. - :param force: Force initialization if already previously run. - :param site_ready: Any init during site installation should set this to False. - - """ +def init(site: str, sites_path: str = ".", new_site: bool = False, force=False) -> None: + """Initialize frappe for the current site. Reset thread locals `frappe.local`""" if getattr(local, "initialised", None) and not force: return @@ -272,14 +261,12 @@ def init( local.qb = get_query_builder(local.conf.db_type) local.qb.get_query = get_query setup_redis_cache_connection() + setup_module_map() if not _qb_patched.get(local.conf.db_type): patch_query_execute() patch_query_aggregation() - if site: - setup_module_map(site_ready) - local.initialised = True # Set the user as database name if not set in config @@ -1649,32 +1636,18 @@ def append_hook(target, key, value): target[key].extend(value) -def setup_module_map(site_ready: bool = True): - """ - Rebuild map of all modules (internal). - - :param site_ready: If the site isn't fully ready yet - install is still going on, we can't - fetch apps from site DB. Fallback to fetching all apps on bench for module map temporarily. - """ +def setup_module_map(): + """Rebuild map of all modules (internal).""" if conf.db_name: local.app_modules = cache.get_value("app_modules") local.module_app = cache.get_value("module_app") if not (local.app_modules and local.module_app): local.module_app, local.app_modules = {}, {} - - if site_ready: - apps = get_installed_apps(_ensure_on_bench=True) - else: - apps = get_all_apps() - - for app in apps: + for app in get_all_apps(with_internal_apps=True): local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) - if module in local.module_app: - print(f"WARNING: module `{module}` found in apps `{local.module_app[module]}` and `{app}`") - local.module_app[module] = app local.app_modules[app].append(module) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index da6509e219..3b6fcb32f1 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -76,7 +76,7 @@ def new_site( "Create a new site" from frappe.installer import _new_site - frappe.init(site=site, new_site=True, site_ready=False) + frappe.init(site=site, new_site=True) _new_site( db_name, @@ -417,7 +417,7 @@ def _reinstall( if not yes: click.confirm("This will wipe your database. Are you sure you want to reinstall?", abort=True) try: - frappe.init(site=site, site_ready=False) + frappe.init(site=site) frappe.connect() frappe.clear_cache() installed = frappe.get_installed_apps() @@ -429,7 +429,7 @@ def _reinstall( frappe.db.close() frappe.destroy() - frappe.init(site=site, site_ready=False) + frappe.init(site=site) _new_site( frappe.conf.db_name, @@ -726,6 +726,7 @@ def disable_user(context, email): @pass_context def migrate(context, skip_failing=False, skip_search_index=False): "Run patches, sync schema and rebuild files/translations" + from traceback_with_variables import activate_by_import from frappe.migrate import SiteMigration diff --git a/frappe/installer.py b/frappe/installer.py index 1a4e8cbbb4..2f7138cb48 100644 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -64,7 +64,7 @@ def _new_site( print("--no-mariadb-socket requires db_type to be set to mariadb.") sys.exit(1) - frappe.init(site=site, site_ready=False) + frappe.init(site=site) if not db_name: import hashlib @@ -557,7 +557,7 @@ def make_conf( ) sites_path = frappe.local.sites_path frappe.destroy() - frappe.init(site, sites_path=sites_path, site_ready=False) + frappe.init(site, sites_path=sites_path) def make_site_config( From b59ba82ae64f3eb81a89d7d7554d9d04b430c823 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:48:19 +0100 Subject: [PATCH 41/95] fix(Data Import): translatability --- .../core/doctype/data_import/data_import.js | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js index 9f0e11a7b7..06797a0b40 100644 --- a/frappe/core/doctype/data_import/data_import.js +++ b/frappe/core/doctype/data_import/data_import.js @@ -135,34 +135,29 @@ frappe.ui.form.on("Data Import", { let failed_records = cint(r.message.failed); let total_records = cint(r.message.total_records); - if (!total_records) return; - let action, message; - if (frm.doc.import_type === "Insert New Records") { - action = "imported"; - } else { - action = "updated"; + if (!total_records) { + return; } - if (failed_records === 0) { - let message_args = [action, successful_records]; - if (successful_records === 1) { - message = __("Successfully {0} 1 record.", message_args); - } else { - message = __("Successfully {0} {1} records.", message_args); - } + let message; + if (frm.doc.import_type === "Insert New Records") { + message = __("Successfully imported {0} out of {1} records.", [ + successful_records, + total_records, + ]); } else { - let message_args = [action, successful_records, total_records]; - if (successful_records === 1) { - message = __( - "Successfully {0} {1} record out of {2}. Click on Export Errored Rows, fix the errors and import again.", - message_args + message = __("Successfully updated {0} out of {1} records.", [ + successful_records, + total_records, + ]); + } + + if (failed_records > 0) { + message += + "
" + + __( + "Please click on 'Export Errored Rows', fix the errors and import again." ); - } else { - message = __( - "Successfully {0} {1} records out of {2}. Click on Export Errored Rows, fix the errors and import again.", - message_args - ); - } } // If the job timed out, display an extra hint From ba64285c66e12b2837f66d723d5d857a36cc2565 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Tue, 30 Jan 2024 17:07:47 +0530 Subject: [PATCH 42/95] Revert "fix(data_import): respect the value of show_failed_logs checkbox" This reverts commit 34f03a2de24a2e34985a1e2e5599e287081f3cf2. --- frappe/core/doctype/data_import/data_import.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js index 9f0e11a7b7..a93671e3e3 100644 --- a/frappe/core/doctype/data_import/data_import.js +++ b/frappe/core/doctype/data_import/data_import.js @@ -506,13 +506,7 @@ frappe.ui.form.on("Data Import", { }, show_import_log(frm) { - if (!frm.doc.show_failed_logs) { - frm.toggle_display("import_log_preview", false); - return; - } - frm.toggle_display("import_log_section", false); - frm.toggle_display("import_log_preview", true); if (frm.import_in_progress) { return; From db1e692d4f1ea44aab10a065d7cd31e5256fae6f Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Tue, 30 Jan 2024 17:08:14 +0530 Subject: [PATCH 43/95] chore: reword checkbox Signed-off-by: Akhil Narang --- frappe/core/doctype/data_import/data_import.json | 4 ++-- frappe/core/doctype/data_import/data_import.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/data_import/data_import.json b/frappe/core/doctype/data_import/data_import.json index 97716219a2..02d65c8004 100644 --- a/frappe/core/doctype/data_import/data_import.json +++ b/frappe/core/doctype/data_import/data_import.json @@ -139,7 +139,7 @@ "default": "0", "fieldname": "show_failed_logs", "fieldtype": "Check", - "label": "Show Failed Logs" + "label": "Show Only Failed Logs" }, { "depends_on": "eval:!doc.__islocal && !doc.import_file", @@ -171,7 +171,7 @@ ], "hide_toolbar": 1, "links": [], - "modified": "2023-12-15 12:45:49.452834", + "modified": "2024-01-30 17:08:05.566686", "modified_by": "Administrator", "module": "Core", "name": "Data Import", diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index ac28f9091f..c674122f30 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -38,7 +38,6 @@ class DataImport(Document): submit_after_import: DF.Check template_options: DF.Code | None template_warnings: DF.Code | None - # end: auto-generated types def validate(self): From d838da51ddf3dcc74a883b43567e941eb5ba453e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 30 Jan 2024 20:29:19 +0530 Subject: [PATCH 44/95] fix: short circuit private files perm check For guest users there's no point in checking permissions, they'll eventually fail... instead just fail immediately. --- frappe/utils/response.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 9fe10545d1..5be201499b 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -265,6 +265,9 @@ def download_backup(path): def download_private_file(path: str) -> Response: """Checks permissions and sends back private file""" + if frappe.session.user == "Guest": + raise Forbidden(_("You don't have permission to access this file")) + files = frappe.get_all("File", filters={"file_url": path}, fields="*") # this file might be attached to multiple documents # if the file is accessible from any one of those documents From 40438b400f90fa2900e6e579156afd9afeac4e03 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 30 Jan 2024 21:52:11 +0530 Subject: [PATCH 45/95] ci: set node env as production in tests --- .github/workflows/server-tests.yml | 2 ++ .github/workflows/ui-tests.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index 39dc99374e..9da7244527 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -43,6 +43,8 @@ jobs: needs: checkrun if: ${{ needs.checkrun.outputs.build == 'strawberry' }} timeout-minutes: 60 + env: + NODE_ENV: "production" strategy: fail-fast: false diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 33a7cde8f4..ecda8cc6b1 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -42,6 +42,8 @@ jobs: needs: checkrun if: ${{ needs.checkrun.outputs.build == 'strawberry' && github.repository_owner == 'frappe' }} timeout-minutes: 60 + env: + NODE_ENV: "production" strategy: fail-fast: false From 3abbde328477d1a787a9e4c8b6ccaf3176a31d50 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 30 Jan 2024 17:49:35 +0100 Subject: [PATCH 46/95] fix: shell escaping on external commands --- frappe/database/__init__.py | 10 ---------- frappe/utils/backups.py | 14 ++++++++------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/frappe/database/__init__.py b/frappe/database/__init__.py index 530bd4c700..7cd2dda4a9 100644 --- a/frappe/database/__init__.py +++ b/frappe/database/__init__.py @@ -75,12 +75,7 @@ def get_command( else: bin, bin_name = which("psql"), "psql" - host = frappe.utils.esc(host, "$ ") - user = frappe.utils.esc(user, "$ ") - db_name = frappe.utils.esc(db_name, "$ ") - if password: - password = frappe.utils.esc(password, "$ ") conn_string = f"postgresql://{user}:{password}@{host}:{port}/{db_name}" else: conn_string = f"postgresql://{user}@{host}:{port}/{db_name}" @@ -96,10 +91,6 @@ def get_command( else: bin, bin_name = which("mariadb") or which("mysql"), "mariadb" - host = frappe.utils.esc(host, "$ ") - user = frappe.utils.esc(user, "$ ") - db_name = frappe.utils.esc(db_name, "$ ") - command = [ f"--user={user}", f"--host={host}", @@ -107,7 +98,6 @@ def get_command( ] if password: - password = frappe.utils.esc(password, "$ ") command.append(f"--password={password}") if dump: diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 295aff2b4d..40aba696f5 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -370,6 +370,8 @@ class BackupGenerator: n.write(c.read()) def take_dump(self): + import shlex + import frappe.utils from frappe.utils.change_log import get_app_branch @@ -419,15 +421,15 @@ class BackupGenerator: extra = [] if self.db_type == "mariadb": if self.backup_includes: - extra.extend([f"'{x}'" for x in self.backup_includes]) + extra.extend(self.backup_includes) elif self.backup_excludes: - extra.extend([f"--ignore-table='{self.db_name}.{table}'" for table in self.backup_excludes]) + extra.extend([f"--ignore-table={self.db_name}.{table}" for table in self.backup_excludes]) elif self.db_type == "postgres": if self.backup_includes: - extra.extend([f"--table='public.\"{table}\"'" for table in self.backup_includes]) + extra.extend([f'--table=public."{table}"' for table in self.backup_includes]) elif self.backup_excludes: - extra.extend([f"--exclude-table-data='public.\"{table}\"'" for table in self.backup_excludes]) + extra.extend([f'--exclude-table-data=public."{table}"' for table in self.backup_excludes]) from frappe.database import get_command @@ -446,11 +448,11 @@ class BackupGenerator: exc=frappe.ExecutableNotFound, ) cmd.append(bin) - cmd.extend(args) + cmd.append(shlex.join(args)) command = " ".join(["set -o pipefail;"] + cmd + ["|", gzip_exc, ">>", self.backup_path_db]) if self.verbose: - print(command.replace(frappe.utils.esc(self.password, "$ "), "*" * 10) + "\n") + print(command.replace(shlex.quote(self.password), "*" * 10) + "\n") frappe.utils.execute_in_shell(command, low_priority=True, check_exit_code=True) From dedadbb0d908eb660c5935b91051d520b0cff7e2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 30 Jan 2024 21:21:05 +0530 Subject: [PATCH 47/95] perf: optional faster perm check for files --- frappe/core/doctype/file/file.py | 7 +++++++ frappe/email/receive.py | 2 +- frappe/tests/test_api.py | 17 ++++++++++++++++- frappe/utils/response.py | 7 ++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index de4375ae6c..02cf453d2b 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -756,6 +756,13 @@ class File(Document): self.save_file(content=optimized_content, overwrite=True) self.save() + @property + def unique_url(self) -> str: + """Unique URL contains file ID in URL to speed up permisison checks.""" + from urllib.parse import urlencode + + return self.file_url + "?" + urlencode({"fid": self.name}) + @staticmethod def zip_files(files): zip_file = io.BytesIO() diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 72a2dfce82..74d7f84dac 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -673,7 +673,7 @@ class InboundMail(Email): content = self.content for file in attachments: if file.name in self.cid_map and self.cid_map[file.name]: - content = content.replace(f"cid:{self.cid_map[file.name]}", file.file_url) + content = content.replace(f"cid:{self.cid_map[file.name]}", file.unique_url) return content def is_notification(self): diff --git a/frappe/tests/test_api.py b/frappe/tests/test_api.py index 8d3065982b..8dd68dd644 100644 --- a/frappe/tests/test_api.py +++ b/frappe/tests/test_api.py @@ -15,7 +15,7 @@ from werkzeug.test import TestResponse import frappe from frappe.installer import update_site_config from frappe.tests.utils import FrappeTestCase, patch_hooks -from frappe.utils import cint, get_test_client, get_url +from frappe.utils import cint, get_site_url, get_test_client, get_url try: _site = frappe.local.site @@ -432,6 +432,21 @@ class TestResponse(FrappeAPITestCase): self.assertGreater(cint(response.headers["content-length"]), 0) self.assertEqual(response.headers["content-disposition"], f'filename="{encoded_filename}"') + def test_download_private_file_with_unique_url(self): + test_content = frappe.generate_hash() + file = frappe.get_doc( + { + "doctype": "File", + "file_name": test_content, + "content": test_content, + "is_private": 1, + } + ) + file.insert() + + self.assertEqual(self.get(file.unique_url, {"sid": self.sid}).text, test_content) + self.assertEqual(self.get(file.file_url, {"sid": self.sid}).text, test_content) + def generate_admin_keys(): from frappe.core.doctype.user.user import generate_keys diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 5be201499b..116f3cd611 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -268,7 +268,12 @@ def download_private_file(path: str) -> Response: if frappe.session.user == "Guest": raise Forbidden(_("You don't have permission to access this file")) - files = frappe.get_all("File", filters={"file_url": path}, fields="*") + filters = {"file_url": path} + if frappe.form_dict.fid: + filters["name"] = str(frappe.form_dict.fid) + + files = frappe.get_all("File", filters=filters, fields="*") + # this file might be attached to multiple documents # if the file is accessible from any one of those documents # then it should be downloadable From 53d6d156ec3d2b9638e045230b4aaa8ff8c1ea49 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 31 Jan 2024 12:00:39 +0530 Subject: [PATCH 48/95] chore: cleanup doctype descriptions (#24609) --- frappe/email/doctype/newsletter/newsletter.json | 7 +++---- frappe/geo/doctype/currency/currency.json | 4 ++-- frappe/website/doctype/blog_settings/blog_settings.json | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frappe/email/doctype/newsletter/newsletter.json b/frappe/email/doctype/newsletter/newsletter.json index e7c902697f..e03cc506e6 100644 --- a/frappe/email/doctype/newsletter/newsletter.json +++ b/frappe/email/doctype/newsletter/newsletter.json @@ -3,7 +3,7 @@ "allow_guest_to_view": 1, "allow_rename": 1, "creation": "2013-01-10 16:34:31", - "description": "Create and Send Newsletters", + "description": "Create and send emails to a specific group of subscribers periodically.", "doctype": "DocType", "document_type": "Other", "engine": "InnoDB", @@ -244,8 +244,7 @@ "fieldname": "campaign", "fieldtype": "Link", "label": "Campaign", - "options": "Marketing Campaign", - "reqd": 0 + "options": "Marketing Campaign" } ], "has_web_view": 1, @@ -254,7 +253,7 @@ "index_web_pages_for_search": 1, "is_published_field": "published", "links": [], - "modified": "2023-12-29 18:04:13.270523", + "modified": "2024-01-30 14:05:50.645802", "modified_by": "Administrator", "module": "Email", "name": "Newsletter", diff --git a/frappe/geo/doctype/currency/currency.json b/frappe/geo/doctype/currency/currency.json index 00dfe248c9..9a4df0f117 100644 --- a/frappe/geo/doctype/currency/currency.json +++ b/frappe/geo/doctype/currency/currency.json @@ -4,7 +4,7 @@ "allow_rename": 1, "autoname": "field:currency_name", "creation": "2013-01-28 10:06:02", - "description": "**Currency** Master", + "description": "Currency list stores the currency value, its symbol and fraction unit", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -82,7 +82,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-17 15:37:31.605278", + "modified": "2024-01-30 13:18:12.053557", "modified_by": "Administrator", "module": "Geo", "name": "Currency", diff --git a/frappe/website/doctype/blog_settings/blog_settings.json b/frappe/website/doctype/blog_settings/blog_settings.json index fe5417f812..95236c8102 100644 --- a/frappe/website/doctype/blog_settings/blog_settings.json +++ b/frappe/website/doctype/blog_settings/blog_settings.json @@ -1,7 +1,7 @@ { "actions": [], "creation": "2013-03-11 17:48:16", - "description": "Blog Settings", + "description": "Settings to control blog categories and interactions like comments and likes", "doctype": "DocType", "engine": "InnoDB", "field_order": [ @@ -131,7 +131,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2022-10-18 15:01:36.202010", + "modified": "2024-01-30 14:13:12.477755", "modified_by": "Administrator", "module": "Website", "name": "Blog Settings", From 161bb4250871d23c346b40f1640562b054089c8b Mon Sep 17 00:00:00 2001 From: David Arnold Date: Wed, 31 Jan 2024 09:58:56 +0100 Subject: [PATCH 49/95] fix: deprioritize precedence of db_name argument to connect --- frappe/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 39ebed222d..f5d72fa6ba 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -304,9 +304,9 @@ def connect( local.db = get_db( host=local.conf.db_host, port=local.conf.db_port, - user=db_name or local.conf.db_user, + user=local.conf.db_user or db_name, password=local.conf.db_password, - cur_db_name=db_name or local.conf.db_name, + cur_db_name=local.conf.db_name or db_name, ) if set_admin_as_user: set_user("Administrator") From a25e68a763b84f571e60d372a3d0b0ac2e265ff0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Jan 2024 14:46:27 +0530 Subject: [PATCH 50/95] fix: make rate_limiter respect multitenancy (#24634) * fix: make rate_limiter respect multitenancy * fix: lower rate limit window for password reset * refactor: Use redis_wrapper --- frappe/core/doctype/server_script/test_server_script.py | 1 - frappe/core/doctype/user/user.py | 2 +- frappe/rate_limiter.py | 2 +- frappe/utils/password.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index b83d1edda4..f53d69304a 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -238,7 +238,6 @@ frappe.qb.from_(todo).select(todo.name).where(todo.name == "{todo.name}").run() script.execute_method() def test_server_script_rate_limiting(self): - # why not script1 = frappe.get_doc( doctype="Server Script", name="rate_limited_server_script", diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 640048d93a..6a9aeba075 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -1018,7 +1018,7 @@ def sign_up(email: str, full_name: str, redirect_to: str) -> tuple[int, str]: @frappe.whitelist(allow_guest=True) -@rate_limit(limit=get_password_reset_limit, seconds=24 * 60 * 60) +@rate_limit(limit=get_password_reset_limit, seconds=60 * 60) def reset_password(user: str) -> str: if user == "Administrator": return "not allowed" diff --git a/frappe/rate_limiter.py b/frappe/rate_limiter.py index 332a5a8070..adeaf94a31 100644 --- a/frappe/rate_limiter.py +++ b/frappe/rate_limiter.py @@ -138,7 +138,7 @@ def rate_limit( if not identity: frappe.throw(_("Either key or IP flag is required.")) - cache_key = f"rl:{frappe.form_dict.cmd}:{identity}" + cache_key = frappe.cache.make_key(f"rl:{frappe.form_dict.cmd}:{identity}") value = frappe.cache.get(cache_key) if not value: diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 3ee92eabda..f5f83cef1e 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -215,4 +215,4 @@ def get_encryption_key(): def get_password_reset_limit(): - return frappe.db.get_single_value("System Settings", "password_reset_limit") or 0 + return frappe.get_system_settings("password_reset_limit") or 3 From e7b3a785c1dfa0d3d187f6b33045a3522490bd5b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 31 Jan 2024 16:12:56 +0530 Subject: [PATCH 51/95] fix: typeerror in onboarding_tours.js --- frappe/public/js/onboarding_tours/onboarding_tours.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/onboarding_tours/onboarding_tours.js b/frappe/public/js/onboarding_tours/onboarding_tours.js index 6bd3a62012..a8ee97a586 100644 --- a/frappe/public/js/onboarding_tours/onboarding_tours.js +++ b/frappe/public/js/onboarding_tours/onboarding_tours.js @@ -265,7 +265,7 @@ frappe.ui.init_onboarding_tour = () => { typeof frappe.boot.user.onboarding_status == "undefined" && frappe.boot.user.onboarding_status == {}; let route = frappe.router.current_route; - if (route[0] === "") return; + if (route?.[0] === "") return; let tour_name; let matching_tours = []; From 4d2c4f020bac93bdd648c4d3c1f514891c41d908 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 31 Jan 2024 12:54:20 +0100 Subject: [PATCH 52/95] fix: Check if header/footer html exists before trying to extract - issue: AttributeError: 'NoneType' object has no attribute 'extract', when no letterhead html was present --- frappe/utils/pdf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index be25b8cd48..3e6966ae0a 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -238,7 +238,8 @@ def prepare_header_footer(soup: BeautifulSoup): # extract header and footer for html_id in ("header-html", "footer-html"): - if content := soup.find(id=html_id).extract(): + if content := soup.find(id=html_id): + content = content.extract() # `header/footer-html` are extracted, rendered as html # and passed in wkhtmltopdf options (as '--header/footer-html') # Remove instances of them from main content for render_template From 09919a3e581e9bb3d8cd1bfde0b2081902a95ea1 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:55:41 +0100 Subject: [PATCH 53/95] fix: use the correct doc method --- frappe/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/handler.py b/frappe/handler.py index d889c67b23..d875e9bd57 100644 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -250,7 +250,7 @@ def check_write_permission(doctype: str = None, name: str = None): if doctype and name: try: doc = frappe.get_doc(doctype, name) - doc.has_permission("write") + doc.check_permission("write") except frappe.DoesNotExistError: # doc has not been inserted yet, name is set to "new-some-doctype" check_doctype = True From d2d669b72053de18e8638b54dca0d39a83ab4fa8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Jan 2024 18:09:20 +0530 Subject: [PATCH 54/95] fix: don't notify links if not public --- frappe/desk/doctype/note/note.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py index 9cc14cbc1e..ef64d17f79 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -28,6 +28,9 @@ class Note(Document): # expire this notification in a week (default) self.expire_notification_on = frappe.utils.add_days(self.creation, 7) + if not self.public and self.notify_on_login: + self.notify_on_login = 0 + if not self.content: self.content = "" From ffb9c9cf6ab2911eb00176482d94d99ad96a3ac7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Jan 2024 18:24:15 +0530 Subject: [PATCH 55/95] fix: clear sitemap cache periodically --- frappe/website/router.py | 2 ++ frappe/www/sitemap.py | 61 ++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/frappe/website/router.py b/frappe/website/router.py index 3bffa78fee..332be38cda 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -322,7 +322,9 @@ def clear_routing_cache(): from frappe.website.doctype.web_form.web_form import get_published_web_forms from frappe.website.doctype.web_page.web_page import get_dynamic_web_pages from frappe.website.page_renderers.document_page import _find_matching_document_webview + from frappe.www.sitemap import get_public_pages_from_doctypes _find_matching_document_webview.clear_cache() get_dynamic_web_pages.clear_cache() get_published_web_forms.clear_cache() + get_public_pages_from_doctypes.clear_cache() diff --git a/frappe/www/sitemap.py b/frappe/www/sitemap.py index ebe6846a39..1e8dea4038 100644 --- a/frappe/www/sitemap.py +++ b/frappe/www/sitemap.py @@ -6,6 +6,7 @@ from urllib.parse import quote import frappe from frappe.model.document import get_controller from frappe.utils import get_url, nowdate +from frappe.utils.caching import redis_cache from frappe.website.router import get_pages no_cache = 1 @@ -31,42 +32,40 @@ def get_context(context): return {"links": links} +@redis_cache(ttl=6 * 60 * 60) def get_public_pages_from_doctypes(): """Return pages from doctypes that are publicly accessible.""" - def get_sitemap_routes(): - routes = {} - doctypes_with_web_view = frappe.get_all( - "DocType", - filters={"has_web_view": True, "allow_guest_to_view": True}, - pluck="name", - ) + routes = {} + doctypes_with_web_view = frappe.get_all( + "DocType", + filters={"has_web_view": True, "allow_guest_to_view": True}, + pluck="name", + ) - for doctype in doctypes_with_web_view: - controller = get_controller(doctype) - meta = frappe.get_meta(doctype) - condition_field = meta.is_published_field or controller.website.condition_field + for doctype in doctypes_with_web_view: + controller = get_controller(doctype) + meta = frappe.get_meta(doctype) + condition_field = meta.is_published_field or controller.website.condition_field - if not condition_field: - continue + if not condition_field: + continue - try: - res = frappe.get_all( - doctype, - fields=["route", "name", "modified"], - filters={condition_field: True}, - ) - except Exception as e: - if not frappe.db.is_missing_column(e): - raise e + try: + res = frappe.get_all( + doctype, + fields=["route", "name", "modified"], + filters={condition_field: True}, + ) + except Exception as e: + if not frappe.db.is_missing_column(e): + raise e - for r in res: - routes[r.route] = { - "doctype": doctype, - "name": r.name, - "modified": r.modified, - } + for r in res: + routes[r.route] = { + "doctype": doctype, + "name": r.name, + "modified": r.modified, + } - return routes - - return frappe.cache.get_value("sitemap_routes", get_sitemap_routes) + return routes From ca293c9aa328183eaf9940147c80fd41c6bce636 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Jan 2024 18:37:12 +0530 Subject: [PATCH 56/95] fix: Enqueue deletion of dynamic link after comitting --- frappe/model/delete_doc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index b6ab2546bf..6a92971c72 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -133,6 +133,7 @@ def delete_doc( doctype=doc.doctype, name=doc.name, now=frappe.flags.in_test, + enqueue_after_commit=True, ) # clear cache for Document From 0458544c3b7c69dd9affcb402d9c68d094f78979 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Jan 2024 21:23:04 +0530 Subject: [PATCH 57/95] refactor!: Simplify asset caching (#24649) Few changes: 1. Client side asset caching is needlessly complicated with 2 layer of caching. Browser already caches things, moving them again to localstorage is uselss and counterproductive because of limits on localstorage. 2. `frappe.assets` was an object that should've been a class. 3. lots of old js syntax. --- frappe/public/js/frappe/assets.js | 164 +++++++++--------------------- 1 file changed, 48 insertions(+), 116 deletions(-) diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js index 98b025ebea..53883289a1 100644 --- a/frappe/public/js/frappe/assets.js +++ b/frappe/public/js/frappe/assets.js @@ -19,11 +19,22 @@ frappe.require = function (items, callback) { }); }; -frappe.assets = { - check: function () { +class AssetManager { + constructor() { + this._executed = []; + this._handlers = { + js: function (txt) { + frappe.dom.eval(txt); + }, + css: function (txt) { + frappe.dom.set_style(txt); + }, + }; + } + check() { // if version is different then clear localstorage if (window._version_number != localStorage.getItem("_version_number")) { - frappe.assets.clear_local_storage(); + this.clear_local_storage(); console.log("Cleared App Cache."); } @@ -33,160 +44,79 @@ frappe.assets = { // Evict cache if page is reloaded within 10 seconds. Which could be user trying to // refresh if things feel broken. if ((not_updated_since < 5000 && is_reload()) || not_updated_since > 2 * 86400000) { - frappe.assets.clear_local_storage(); + this.clear_local_storage(); } } else { - frappe.assets.clear_local_storage(); + this.clear_local_storage(); } - frappe.assets.init_local_storage(); - }, + this.init_local_storage(); + } - init_local_storage: function () { + init_local_storage() { localStorage._last_load = new Date(); localStorage._version_number = window._version_number; if (frappe.boot) localStorage.metadata_version = frappe.boot.metadata_version; - }, + } - clear_local_storage: function () { - $.each( - ["_last_load", "_version_number", "metadata_version", "page_info", "last_visited"], - function (i, key) { - localStorage.removeItem(key); - } + clear_local_storage() { + ["_last_load", "_version_number", "metadata_version", "page_info", "last_visited"].forEach( + (key) => localStorage.removeItem(key) ); // clear assets - for (var key in localStorage) { + for (let key in localStorage) { if ( - key.indexOf("desk_assets:") === 0 || - key.indexOf("_page:") === 0 || - key.indexOf("_doctype:") === 0 || - key.indexOf("preferred_breadcrumbs:") === 0 + key.startsWith("_page:") || + key.startsWith("_doctype:") || + key.startsWith("preferred_breadcrumbs:") ) { localStorage.removeItem(key); } } console.log("localStorage cleared"); - }, + } - // keep track of executed assets - executed_: [], + eval_assets(path, content) { + if (!this._executed.includes(path)) { + this._handlers[this.extn(path)](content); + this._executed.push(path); + } + } - // pass on to the handler to set - execute: function (items, callback) { - var to_fetch = []; - for (var i = 0, l = items.length; i < l; i++) { - if (!frappe.assets.exists(items[i])) { - to_fetch.push(items[i]); - } - } - if (to_fetch.length) { - frappe.assets.fetch(to_fetch, function () { - frappe.assets.eval_assets(items, callback); - }); - } else { - frappe.assets.eval_assets(items, callback); - } - }, - - eval_assets: function (items, callback) { - for (var i = 0, l = items.length; i < l; i++) { - // execute js/css if not already. - var path = items[i]; - if (frappe.assets.executed_.indexOf(path) === -1) { - // execute - frappe.assets.handler[frappe.assets.extn(path)](frappe.assets.get(path), path); - frappe.assets.executed_.push(path); - } - } - callback && callback(); - }, - - // check if the asset exists in - // localstorage - exists: function (src) { - if (frappe.assets.executed_.indexOf(src) !== -1) { - return true; - } - if (frappe.boot.developer_mode) { - return false; - } - if (frappe.assets.get(src)) { - return true; - } else { - return false; - } - }, - - // load an asset via - fetch: function (items, callback) { + execute(items, callback) { // this is virtual page load, only get the the source - // *without* the template - - if (items.length === 0) { - callback(); - return; - } + let me = this; const version_string = frappe.boot.developer_mode || window.dev_server ? Date.now() : window._version_number; - async function fetch_item(item) { + async function fetch_item(path) { // Add the version to the URL to bust the cache for non-bundled assets - let url = new URL(item, window.location.origin); + let url = new URL(path, window.location.origin); - if (!item.includes(".bundle.") && !url.searchParams.get("v")) { + if (!path.includes(".bundle.") && !url.searchParams.get("v")) { url.searchParams.append("v", version_string); } const response = await fetch(url.toString()); const body = await response.text(); - frappe.assets.add(item, body); + me.eval_assets(path, body); } frappe.dom.freeze(); const fetch_promises = items.map(fetch_item); Promise.all(fetch_promises).then(() => { frappe.dom.unfreeze(); - callback(); + callback?.(); }); - }, + } - add: function (src, txt) { - if ("localStorage" in window) { - try { - frappe.assets.set(src, txt); - } catch (e) { - // if quota is exceeded, clear local storage and set item - frappe.assets.clear_local_storage(); - frappe.assets.set(src, txt); - } - } - }, - - get: function (src) { - return localStorage.getItem("desk_assets:" + src); - }, - - set: function (src, txt) { - localStorage.setItem("desk_assets:" + src, txt); - }, - - extn: function (src) { + extn(src) { if (src.indexOf("?") != -1) { src = src.split("?").slice(-1)[0]; } return src.split(".").slice(-1)[0]; - }, - - handler: { - js: function (txt, src) { - frappe.dom.eval(txt); - }, - css: function (txt, src) { - frappe.dom.set_style(txt); - }, - }, + } bundled_asset(path, is_rtl = null) { if (!path.startsWith("/assets") && path.includes(".bundle.")) { @@ -197,8 +127,8 @@ frappe.assets = { return path; } return path; - }, -}; + } +} function is_reload() { try { @@ -211,3 +141,5 @@ function is_reload() { return true; } } + +frappe.assets = new AssetManager(); From 3634d11b6ad58aef2270f87abbafc9cd6395bb00 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 10:44:00 +0530 Subject: [PATCH 58/95] fix: Retry contact update if it fails due to conflict (#24654) --- frappe/core/doctype/user/user.py | 53 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 6a9aeba075..aacad57e9a 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -1254,34 +1254,37 @@ def create_contact(user, ignore_links=False, ignore_mandatory=False): except frappe.DuplicateEntryError: pass else: - contact = frappe.get_doc("Contact", contact_name) - contact.first_name = user.first_name - contact.last_name = user.last_name - contact.gender = user.gender + try: + contact = frappe.get_doc("Contact", contact_name) + contact.first_name = user.first_name + contact.last_name = user.last_name + contact.gender = user.gender - # Add mobile number if phone does not exists in contact - if user.phone and not any(new_contact.phone == user.phone for new_contact in contact.phone_nos): - # Set primary phone if there is no primary phone number - contact.add_phone( - user.phone, - is_primary_phone=not any( - new_contact.is_primary_phone == 1 for new_contact in contact.phone_nos - ), - ) + # Add mobile number if phone does not exists in contact + if user.phone and not any(new_contact.phone == user.phone for new_contact in contact.phone_nos): + # Set primary phone if there is no primary phone number + contact.add_phone( + user.phone, + is_primary_phone=not any( + new_contact.is_primary_phone == 1 for new_contact in contact.phone_nos + ), + ) - # Add mobile number if mobile does not exists in contact - if user.mobile_no and not any( - new_contact.phone == user.mobile_no for new_contact in contact.phone_nos - ): - # Set primary mobile if there is no primary mobile number - contact.add_phone( - user.mobile_no, - is_primary_mobile_no=not any( - new_contact.is_primary_mobile_no == 1 for new_contact in contact.phone_nos - ), - ) + # Add mobile number if mobile does not exists in contact + if user.mobile_no and not any( + new_contact.phone == user.mobile_no for new_contact in contact.phone_nos + ): + # Set primary mobile if there is no primary mobile number + contact.add_phone( + user.mobile_no, + is_primary_mobile_no=not any( + new_contact.is_primary_mobile_no == 1 for new_contact in contact.phone_nos + ), + ) - contact.save(ignore_permissions=True) + contact.save(ignore_permissions=True) + except frappe.TimestampMismatchError: + raise frappe.RetryBackgroundJobError def get_restricted_ip_list(user): From 78665d98b2d4a4cff3577854741d69d244c4b6f9 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 10:57:38 +0530 Subject: [PATCH 59/95] fix: Bail if no SMTP acccount can be found Currently we give this weird error instead of just saying no outgoing email account is configured. ``` AttributeError: 'NoneType' object has no attribute 'get_smtp_server' File "frappe/email/queue.py", line 154, in flush email_queue.send() File "frappe/email/doctype/email_queue/email_queue.py", line 160, in send with SendMailContext(self, smtp_server_instance) as ctx: File "frappe/email/doctype/email_queue/email_queue.py", line 236, in __init__ self.smtp_server: SMTPServer = smtp_server_instance or self.email_account_doc.get_smtp_server() ``` --- frappe/email/doctype/email_queue/email_queue.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 91ca2bbdbb..f5da857576 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -131,12 +131,12 @@ class EmailQueue(Document): def attachments_list(self): return json.loads(self.attachments) if self.attachments else [] - def get_email_account(self): + def get_email_account(self, raise_error=False): if self.email_account: return frappe.get_cached_doc("Email Account", self.email_account) return EmailAccount.find_outgoing( - match_by_email=self.sender, match_by_doctype=self.reference_doctype + match_by_email=self.sender, match_by_doctype=self.reference_doctype, _raise_error=raise_error ) def is_to_be_sent(self): @@ -233,7 +233,7 @@ class SendMailContext: smtp_server_instance: SMTPServer = None, ): self.queue_doc: EmailQueue = queue_doc - self.email_account_doc = queue_doc.get_email_account() + self.email_account_doc = queue_doc.get_email_account(raise_error=True) self.smtp_server: SMTPServer = smtp_server_instance or self.email_account_doc.get_smtp_server() @@ -733,7 +733,7 @@ class QueueBuilder: recipients = list(set([r] + self.final_cc() + self.bcc)) q = EmailQueue.new({**queue_data, **{"recipients": recipients}}, ignore_permissions=True) if not smtp_server_instance: - email_account = q.get_email_account() + email_account = q.get_email_account(raise_error=True) smtp_server_instance = email_account.get_smtp_server() with suppress(Exception): From dae99eb53c4606267c847ba7e301395da354004b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 11:07:27 +0530 Subject: [PATCH 60/95] fix: Fetch SMTP server inside context Currently if smtp server creation fails email queue is endlessly retried. It should just fail if there's no outgoing account. --- frappe/email/doctype/email_queue/email_queue.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index f5da857576..aff8782abf 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -158,6 +158,7 @@ class EmailQueue(Document): return with SendMailContext(self, smtp_server_instance) as ctx: + ctx.fetch_smtp_server() message = None for recipient in self.recipients: if recipient.is_mail_sent(): @@ -233,14 +234,16 @@ class SendMailContext: smtp_server_instance: SMTPServer = None, ): self.queue_doc: EmailQueue = queue_doc - self.email_account_doc = queue_doc.get_email_account(raise_error=True) - - self.smtp_server: SMTPServer = smtp_server_instance or self.email_account_doc.get_smtp_server() - + self.smtp_server: SMTPServer = smtp_server_instance self.sent_to_atleast_one_recipient = any( rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent() ) + def fetch_smtp_server(self): + self.email_account_doc = self.queue_doc.get_email_account(raise_error=True) + if not self.smtp_server: + self.smtp_server = self.email_account_doc.get_smtp_server() + def __enter__(self): self.queue_doc.update_status(status="Sending", commit=True) return self From 12a246d697304ea343fb0c88c12f0dbb5a730a65 Mon Sep 17 00:00:00 2001 From: jll-02 <63648645+jll-02@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:15:00 +0100 Subject: [PATCH 61/95] chore(deps): removes unused cariocffi dependency --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5553a52e64..6b27ea77b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,6 @@ dependencies = [ "beautifulsoup4~=4.12.2", "bleach-allowlist~=1.0.3", "bleach[css]~=6.0.0", - "cairocffi==1.5.1", "chardet~=5.1.0", "croniter~=2.0.1", "cryptography~=41.0.3", From 281c1de6391b10cc15f319af8e5551cba2d1de63 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:51:57 +0100 Subject: [PATCH 62/95] fix(Data Export): translate multiselect --- frappe/core/doctype/data_export/data_export.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/data_export/data_export.js b/frappe/core/doctype/data_export/data_export.js index a819ed7fe3..1808578d31 100644 --- a/frappe/core/doctype/data_export/data_export.js +++ b/frappe/core/doctype/data_export/data_export.js @@ -153,7 +153,7 @@ const add_doctype_field_multicheck_control = (doctype, parent_wrapper) => { const options = fields.map((df) => { return { - label: df.label, + label: __(df.label), value: df.fieldname, danger: df.reqd, checked: 1, @@ -163,7 +163,7 @@ const add_doctype_field_multicheck_control = (doctype, parent_wrapper) => { const multicheck_control = frappe.ui.form.make_control({ parent: parent_wrapper, df: { - label: doctype, + label: __(doctype), fieldname: doctype + "_fields", fieldtype: "MultiCheck", options: options, From 833d108614ad2b6a89fe563a04817378ac6ba4eb Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:35:25 +0100 Subject: [PATCH 63/95] fix: remove _lt from frappe.model.std_fields (#24662) --- frappe/model/__init__.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index d3d120c872..18d7244179 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -134,22 +134,22 @@ log_types = ( ) std_fields = [ - {"fieldname": "name", "fieldtype": "Link", "label": _lt("ID")}, - {"fieldname": "owner", "fieldtype": "Link", "label": _lt("Created By"), "options": "User"}, - {"fieldname": "idx", "fieldtype": "Int", "label": _lt("Index")}, - {"fieldname": "creation", "fieldtype": "Datetime", "label": _lt("Created On")}, - {"fieldname": "modified", "fieldtype": "Datetime", "label": _lt("Last Updated On")}, + {"fieldname": "name", "fieldtype": "Link", "label": "ID"}, + {"fieldname": "owner", "fieldtype": "Link", "label": "Created By", "options": "User"}, + {"fieldname": "idx", "fieldtype": "Int", "label": "Index"}, + {"fieldname": "creation", "fieldtype": "Datetime", "label": "Created On"}, + {"fieldname": "modified", "fieldtype": "Datetime", "label": "Last Updated On"}, { "fieldname": "modified_by", "fieldtype": "Link", - "label": _lt("Last Updated By"), + "label": "Last Updated By", "options": "User", }, - {"fieldname": "_user_tags", "fieldtype": "Data", "label": _lt("Tags")}, - {"fieldname": "_liked_by", "fieldtype": "Data", "label": _lt("Liked By")}, - {"fieldname": "_comments", "fieldtype": "Text", "label": _lt("Comments")}, - {"fieldname": "_assign", "fieldtype": "Text", "label": _lt("Assigned To")}, - {"fieldname": "docstatus", "fieldtype": "Int", "label": _lt("Document Status")}, + {"fieldname": "_user_tags", "fieldtype": "Data", "label": "Tags"}, + {"fieldname": "_liked_by", "fieldtype": "Data", "label": "Liked By"}, + {"fieldname": "_comments", "fieldtype": "Text", "label": "Comments"}, + {"fieldname": "_assign", "fieldtype": "Text", "label": "Assigned To"}, + {"fieldname": "docstatus", "fieldtype": "Int", "label": "Document Status"}, ] From 4553de8ba0694105aee7fec37f1056a79eeea6e3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 16:22:07 +0530 Subject: [PATCH 64/95] fix: never show virtual fields in list view (#24666) * fix: never render virtual fields in list view * fix: Hide "in list view" for virtual columns --- frappe/core/doctype/docfield/docfield.json | 3 ++- frappe/core/doctype/docfield/docfield.py | 1 + .../doctype/customize_form_field/customize_form_field.json | 3 ++- .../doctype/customize_form_field/customize_form_field.py | 1 + frappe/public/js/frappe/list/base_list.js | 4 +++- frappe/public/js/frappe/list/list_view.js | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 22b84d1cbc..7b188569d7 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -157,6 +157,7 @@ }, { "default": "0", + "depends_on": "eval:!doc.is_virtual", "fieldname": "in_list_view", "fieldtype": "Check", "label": "In List View", @@ -580,7 +581,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-16 11:26:56.364594", + "modified": "2024-02-01 15:55:44.007917", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/core/doctype/docfield/docfield.py b/frappe/core/doctype/docfield/docfield.py index 01fa56a9ce..ed663c3bba 100644 --- a/frappe/core/doctype/docfield/docfield.py +++ b/frappe/core/doctype/docfield/docfield.py @@ -117,6 +117,7 @@ class DocField(Document): unique: DF.Check width: DF.Data | None # end: auto-generated types + def get_link_doctype(self): """Return the Link doctype for the `docfield` (if applicable). diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index 67c6c8ba95..0dfd7a6c45 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -130,6 +130,7 @@ }, { "default": "0", + "depends_on": "eval:!doc.is_virtual", "fieldname": "in_list_view", "fieldtype": "Check", "label": "In List View" @@ -483,7 +484,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-12-08 15:52:37.525003", + "modified": "2024-02-01 15:56:39.171633", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.py b/frappe/custom/doctype/customize_form_field/customize_form_field.py index 59b0155a98..76ab6535e3 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.py +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.py @@ -110,4 +110,5 @@ class CustomizeFormField(Document): unique: DF.Check width: DF.Data | None # end: auto-generated types + pass diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index da76488b9c..a96ebb9157 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -132,7 +132,9 @@ frappe.views.BaseList = class BaseList { frappe.meta.has_field(doctype, fieldname) || fieldname === "_seen"; - if (!is_valid_field) { + let is_virtual = this.meta.fields.find((df) => df.fieldname == fieldname)?.is_virtual; + + if (!is_valid_field || is_virtual) { return; } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index eb82f3338f..72c9ed8a44 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -374,7 +374,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { if (frappe.has_indicator(this.doctype) && df.fieldname === "status") { return false; } - if (!df.in_list_view) { + if (!df.in_list_view || df.is_virtual) { return false; } return df.fieldname !== this.meta.title_field; From aed358609508182554294cc06029b6ae739e02ba Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 1 Feb 2024 17:10:21 +0530 Subject: [PATCH 65/95] fix: enable syntax highlighting for `PythonExpression` code fields (#24669) --- frappe/public/js/frappe/form/controls/code.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/form/controls/code.js b/frappe/public/js/frappe/form/controls/code.js index 05e8fde29c..4efd92bd8d 100644 --- a/frappe/public/js/frappe/form/controls/code.js +++ b/frappe/public/js/frappe/form/controls/code.js @@ -153,6 +153,7 @@ frappe.ui.form.ControlCode = class ControlCode extends frappe.ui.form.ControlTex JS: "ace/mode/javascript", Python: "ace/mode/python", Py: "ace/mode/python", + PythonExpression: "ace/mode/python", HTML: "ace/mode/html", CSS: "ace/mode/css", Markdown: "ace/mode/markdown", From 4d63a12ab6cbc5b506c5c53350c65228d4f12e0a Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 1 Feb 2024 12:41:41 +0100 Subject: [PATCH 66/95] fix(Data Import): scheduler not needed in dev mode (#24667) --- frappe/core/doctype/data_import/data_import.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index c674122f30..b63b5cd2a7 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -92,7 +92,8 @@ class DataImport(Document): def start_import(self): from frappe.utils.scheduler import is_scheduler_inactive - if is_scheduler_inactive() and not frappe.flags.in_test: + run_now = frappe.flags.in_test or frappe.conf.developer_mode + if is_scheduler_inactive() and not run_now: frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")) job_id = f"data_import::{self.name}" @@ -105,7 +106,7 @@ class DataImport(Document): event="data_import", job_id=job_id, data_import=self.name, - now=frappe.conf.developer_mode or frappe.flags.in_test, + now=run_now, ) return True From aad0c19f3a01c0cc41b383c65c71c92c57abc8b6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 17:57:25 +0530 Subject: [PATCH 67/95] fix: lazy translate circular imports (#24672) * Revert "fix: remove _lt from frappe.model.std_fields (#24662)" This reverts commit 833d108614ad2b6a89fe563a04817378ac6ba4eb. * fix: circular import --- frappe/__init__.py | 40 ++++++++++++++++++++++++++++-- frappe/gettext/extractors/utils.py | 3 ++- frappe/translate.py | 38 ---------------------------- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 289e5c6e3d..ce7ce7699b 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -130,9 +130,45 @@ def _lt(msg: str, lang: str | None = None, context: str | None = None): Note: Result is not guaranteed to equivalent to pure strings for all operations. """ - from frappe.translate import LazyTranslate + return _LazyTranslate(msg, lang, context) - return LazyTranslate(msg, lang, context) + +@functools.total_ordering +class _LazyTranslate: + __slots__ = ("msg", "lang", "context") + + def __init__(self, msg: str, lang: str | None = None, context: str | None = None) -> None: + self.msg = msg + self.lang = lang + self.context = context + + @property + def value(self) -> str: + return _(str(self.msg), self.lang, self.context) + + def __str__(self): + return self.value + + def __add__(self, other): + if isinstance(other, (str, _LazyTranslate)): + return self.value + str(other) + raise NotImplementedError + + def __radd__(self, other): + if isinstance(other, (str, _LazyTranslate)): + return str(other) + self.value + return NotImplementedError + + def __repr__(self) -> str: + return f"'{self.value}'" + + # NOTE: it's required to override these methods and raise error as default behaviour will + # return `False` in all cases. + def __eq__(self, other): + raise NotImplementedError + + def __lt__(self, other): + raise NotImplementedError def as_unicode(text, encoding: str = "utf-8") -> str: diff --git a/frappe/gettext/extractors/utils.py b/frappe/gettext/extractors/utils.py index 4becbbe64e..e088a8409b 100644 --- a/frappe/gettext/extractors/utils.py +++ b/frappe/gettext/extractors/utils.py @@ -1,7 +1,6 @@ import re import frappe -from frappe.model.utils import InvalidIncludePath, render_include TRANSLATE_PATTERN = re.compile( r"_\(\s*" # starts with literal `_(`, ignore following whitespace/newlines @@ -36,6 +35,8 @@ def extract_messages_from_code(code): """ from jinja2 import TemplateError + from frappe.model.utils import InvalidIncludePath, render_include + try: code = frappe.as_unicode(render_include(code)) diff --git a/frappe/translate.py b/frappe/translate.py index 7e270eee09..0f9098876f 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -1029,44 +1029,6 @@ def print_language(language: str): frappe.local.jenv = _jenv -@functools.total_ordering -class LazyTranslate: - __slots__ = ("msg", "lang", "context") - - def __init__(self, msg: str, lang: str | None = None, context: str | None = None) -> None: - self.msg = msg - self.lang = lang - self.context = context - - @property - def value(self) -> str: - return frappe._(str(self.msg), self.lang, self.context) - - def __str__(self): - return self.value - - def __add__(self, other): - if isinstance(other, (str, LazyTranslate)): - return self.value + str(other) - raise NotImplementedError - - def __radd__(self, other): - if isinstance(other, (str, LazyTranslate)): - return str(other) + self.value - return NotImplementedError - - def __repr__(self) -> str: - return f"'{self.value}'" - - # NOTE: it's required to override these methods and raise error as default behaviour will - # return `False` in all cases. - def __eq__(self, other): - raise NotImplementedError - - def __lt__(self, other): - raise NotImplementedError - - # Backward compatibility get_full_dict = get_all_translations load_lang = get_translations_from_apps From c479a038a84f55e956a716a52b0423303b8a1440 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 19:42:54 +0530 Subject: [PATCH 68/95] fix: Avoid enqueueing during install (#24679) This isn't strictly required but if bench isn't running this can break installation. --- frappe/core/doctype/role_profile/role_profile.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/role_profile/role_profile.py b/frappe/core/doctype/role_profile/role_profile.py index c026a4926a..d0fd42edfa 100644 --- a/frappe/core/doctype/role_profile/role_profile.py +++ b/frappe/core/doctype/role_profile/role_profile.py @@ -25,7 +25,11 @@ class RoleProfile(Document): self.name = self.role_profile def on_update(self): - self.queue_action("update_all_users", now=frappe.flags.in_test, enqueue_after_commit=True) + self.queue_action( + "update_all_users", + now=frappe.flags.in_test or frappe.flags.in_install, + enqueue_after_commit=True, + ) def update_all_users(self): """Changes in role_profile reflected across all its user""" From c09f48f5d7421f71149321487aac14ee4a2d2085 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Fri, 2 Feb 2024 07:03:22 +0100 Subject: [PATCH 69/95] feat: comply trackers with utm and add utm_content for a/b testing (#24521) * feat: comply trackers with utm and add utm_content for a/b testing * fix: make all other than source optional this is plausible.io best practice * fix: medium is not specified to be lowercase * fix: ensure proper parameter quoting --- frappe/public/js/frappe/utils/utils.js | 24 ++++++++++++++----- frappe/utils/data.py | 21 +++++++++++----- .../doctype/web_page_view/web_page_view.json | 8 ++++++- .../doctype/web_page_view/web_page_view.py | 2 ++ .../website_analytics/website_analytics.js | 1 + frappe/www/website_script.js | 9 +++---- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 18f93ab187..15785d514f 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1705,6 +1705,8 @@ Object.assign(frappe.utils, { fieldname: "source", label: __("Source"), fieldtype: "Data", + reqd: 1, + description: "The referrer (e.g. google, newsletter)", default: localStorage.getItem("tracker_url:source"), }, { @@ -1719,25 +1721,35 @@ Object.assign(frappe.utils, { fieldname: "medium", label: __("Medium"), fieldtype: "Data", + description: "Marketing medium (e.g. cpc, banner, email)", default: localStorage.getItem("tracker_url:medium"), }, + { + fieldname: "content", + label: __("Content"), + fieldtype: "Data", + description: "Use to differentiate ad variants (e.g. A/B testing)", + default: localStorage.getItem("tracker_url:content"), + }, ], function (data) { let url = data.url; localStorage.setItem("tracker_url:url", data.url); - if (data.source) { - url += "?source=" + data.source; - localStorage.setItem("tracker_url:source", data.source); - } + url += "?utm_source=" + encodeURIComponent(data.source); + localStorage.setItem("tracker_url:source", data.source); if (data.campaign) { - url += "&campaign=" + data.campaign; + url += "&utm_campaign=" + encodeURIComponent(data.campaign); localStorage.setItem("tracker_url:campaign", data.campaign); } if (data.medium) { - url += "&medium=" + data.medium.toLowerCase(); + url += "&utm_medium=" + encodeURIComponent(data.medium); localStorage.setItem("tracker_url:medium", data.medium); } + if (data.medium) { + url += "&utm_content=" + encodeURIComponent(data.content); + localStorage.setItem("tracker_url:content", data.content); + } frappe.utils.copy_to_clipboard(url); diff --git a/frappe/utils/data.py b/frappe/utils/data.py index c1026b7807..612b9f191a 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -2486,18 +2486,27 @@ def is_site_link(link: str) -> bool: return urlparse(link).netloc == urlparse(frappe.utils.get_url()).netloc -def add_trackers_to_url(url: str, source: str, campaign: str, medium: str = "email") -> str: +def add_trackers_to_url( + url: str, + source: str, + campaign: str | None = None, + medium: str | None = None, + content: str | None = None, +) -> str: url_parts = list(urlparse(url)) if url_parts[0] == "mailto": return url - trackers = { - "source": source, - "medium": medium, - } + trackers = {"utm_source": source} + + if medium: + trackers["utm_medium"] = medium if campaign: - trackers["campaign"] = campaign + trackers["utm_campaign"] = campaign + + if content: + trackers["utm_content"] = content query = dict(parse_qsl(url_parts[4])) | trackers diff --git a/frappe/website/doctype/web_page_view/web_page_view.json b/frappe/website/doctype/web_page_view/web_page_view.json index 2e514ffaec..57718c4cef 100644 --- a/frappe/website/doctype/web_page_view/web_page_view.json +++ b/frappe/website/doctype/web_page_view/web_page_view.json @@ -82,6 +82,12 @@ "fieldtype": "Data", "label": "Medium", "read_only": 1 + }, + { + "fieldname": "content", + "fieldtype": "Data", + "label": "Content", + "read_only": 1 } ], "in_create": 1, @@ -111,4 +117,4 @@ "sort_order": "DESC", "states": [], "title_field": "path" -} \ No newline at end of file +} diff --git a/frappe/website/doctype/web_page_view/web_page_view.py b/frappe/website/doctype/web_page_view/web_page_view.py index 7c18d1ff66..05d482e0fd 100644 --- a/frappe/website/doctype/web_page_view/web_page_view.py +++ b/frappe/website/doctype/web_page_view/web_page_view.py @@ -47,6 +47,7 @@ def make_view_log( source=None, campaign=None, medium=None, + content=None, visitor_id=None, ): if not is_tracking_enabled(): @@ -85,6 +86,7 @@ def make_view_log( view.source = source view.campaign = campaign view.medium = (medium or "").lower() + view.content = content view.visitor_id = visitor_id try: diff --git a/frappe/website/report/website_analytics/website_analytics.js b/frappe/website/report/website_analytics/website_analytics.js index ac1445eaed..f163f5da81 100644 --- a/frappe/website/report/website_analytics/website_analytics.js +++ b/frappe/website/report/website_analytics/website_analytics.js @@ -38,6 +38,7 @@ frappe.query_reports["Website Analytics"] = { { value: "source", label: __("Source") }, { value: "campaign", label: __("Campaign") }, { value: "medium", label: __("Medium") }, + { value: "content", label: __("Content") }, ], default: "path", }, diff --git a/frappe/www/website_script.js b/frappe/www/website_script.js index a95b21313e..89d6fcd5c5 100644 --- a/frappe/www/website_script.js +++ b/frappe/www/website_script.js @@ -32,12 +32,13 @@ ga('send', 'pageview'); browser: browser.name, version: browser.version, user_tz: Intl.DateTimeFormat().resolvedOptions().timeZone, - source: query_params.source, - medium: query_params.medium, - campaign: query_params.campaign, + source: query_params.source || query_params.utm_source, + medium: query_params.medium || query_params.utm_medium, + campaign: query_params.campaign || query_params.utm_campaign, + content: query_params.content || query_params.utm_content, visitor_id: result.visitorId }) }) }) } -{% endif %} \ No newline at end of file +{% endif %} From d26c67df75a95ef43d329eadd48d7998ea656856 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 13:37:46 +0530 Subject: [PATCH 70/95] fix: virtual fields are never writable (#24693) --- frappe/public/js/frappe/model/perm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/model/perm.js b/frappe/public/js/frappe/model/perm.js index 77b92b046e..3358d67e55 100644 --- a/frappe/public/js/frappe/model/perm.js +++ b/frappe/public/js/frappe/model/perm.js @@ -203,7 +203,7 @@ $.extend(frappe.perm, { // permission if (p) { - if (p.write && !df.disabled) { + if (p.write && !df.disabled && !df.is_virtual) { status = "Write"; } else if (p.read) { status = "Read"; From 380b33d49ee9eac3962399b6d6a4b20a6a195c8a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 15:08:16 +0530 Subject: [PATCH 71/95] fix: Dashboard view realtime error (#24698) filter_area doesn't exist on DashboardView, so this inherited function breaks down. --- frappe/public/js/frappe/list/list_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 72c9ed8a44..8b37c37836 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1520,7 +1520,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } avoid_realtime_update() { - if (this.filter_area.is_being_edited()) { + if (this.filter_area?.is_being_edited()) { return true; } // this is set when a bulk operation is called from a list view which might update the list view From 11baf6e03c7d37325d02b454f63468a8f7d2a57b Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:03:55 +0100 Subject: [PATCH 72/95] fix(login): Escape translated strings (#24431) * Use `tojson` instead of `json` Jinja filter --- frappe/templates/includes/login/login.js | 62 ++++++++++++------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 3cff2e4652..204df64dff 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -21,7 +21,8 @@ login.bind_events = function () { args.usr = frappe.utils.xss_sanitise(($("#login_email").val() || "").trim()); args.pwd = $("#login_password").val(); if (!args.usr || !args.pwd) { - frappe.msgprint('{{ _("Both login and password required") }}'); + {# striptags is used to remove newlines, e is used for escaping #} + frappe.msgprint("{{ _('Both login and password required') | striptags | e }}"); return false; } login.call(args, null, "/login"); @@ -36,7 +37,7 @@ login.bind_events = function () { args.redirect_to = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to")); args.full_name = frappe.utils.xss_sanitise(($("#signup_fullname").val() || "").trim()); if (!args.email || !validate_email(args.email) || !args.full_name) { - login.set_status('{{ _("Valid email and name required") }}', 'red'); + login.set_status({{ _("Valid email and name required") | tojson }}, 'red'); return false; } login.call(args); @@ -49,7 +50,7 @@ login.bind_events = function () { args.cmd = "frappe.core.doctype.user.user.reset_password"; args.user = ($("#forgot_email").val() || "").trim(); if (!args.user) { - login.set_status('{{ _("Valid Login id required.") }}', 'red'); + login.set_status({{ _("Valid Login id required.") | tojson }}, 'red'); return false; } login.call(args); @@ -62,14 +63,14 @@ login.bind_events = function () { args.cmd = "frappe.www.login.send_login_link"; args.email = ($("#login_with_email_link_email").val() || "").trim(); if (!args.email) { - login.set_status('{{ _("Valid Login id required.") }}', 'red'); + login.set_status({{ _("Valid Login id required.") | tojson }}, 'red'); return false; } login.call(args).then(() => { - login.set_status('{{ _("Login link sent to your email") }}', 'blue'); + login.set_status({{ _("Login link sent to your email") | tojson }}, 'blue'); $("#login_with_email_link_email").val(""); }).catch(() => { - login.set_status('{{ _("Send login link") }}', 'blue'); + login.set_status({{ _("Send login link") | tojson }}, 'blue'); }); return false; @@ -79,10 +80,10 @@ login.bind_events = function () { var input = $($(this).attr("toggle")); if (input.attr("type") == "password") { input.attr("type", "text"); - $(this).text('{{ _("Hide") }}') + $(this).text({{ _("Hide") | tojson }}) } else { input.attr("type", "password"); - $(this).text('{{ _("Show") }}') + $(this).text({{ _("Show") | tojson }}) } }); @@ -93,7 +94,7 @@ login.bind_events = function () { args.usr = ($("#login_email").val() || "").trim(); args.pwd = $("#login_password").val(); if (!args.usr || !args.pwd) { - login.set_status('{{ _("Both login and password required") }}', 'red'); + login.set_status({{ _("Both login and password required") | tojson }}, 'red'); return false; } login.call(args); @@ -168,7 +169,7 @@ login.signup = function () { // Login login.call = function (args, callback, url="/") { - login.set_status('{{ _("Verifying...") }}', 'blue'); + login.set_status({{ _("Verifying...") | tojson }}, 'blue'); return frappe.call({ type: "POST", @@ -227,13 +228,13 @@ login.login_handlers = (function () { var login_handlers = { 200: function (data) { if (data.message == 'Logged In') { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status({{ _("Success") | tojson }}, 'green'); document.body.innerHTML = `{% include "templates/includes/splash_screen.html" %}`; window.location.href = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to")) || data.home_page; } else if (data.message == 'Password Reset') { window.location.href = frappe.utils.sanitise_redirect(data.redirect_to); } else if (data.message == "No App") { - login.set_status("{{ _('Success') }}", 'green'); + login.set_status({{ _("Success") | tojson }}, 'green'); if (localStorage) { var last_visited = localStorage.getItem("last_visited") @@ -252,13 +253,13 @@ login.login_handlers = (function () { } } else if (window.location.hash === '#forgot') { if (data.message === 'not found') { - login.set_status('{{ _("Not a valid user") }}', 'red'); + login.set_status({{ _("Not a valid user") | tojson }}, 'red'); } else if (data.message == 'not allowed') { - login.set_status('{{ _("Not Allowed") }}', 'red'); + login.set_status({{ _("Not Allowed") | tojson }}, 'red'); } else if (data.message == 'disabled') { - login.set_status('{{ _("Not Allowed: Disabled User") }}', 'red'); + login.set_status({{ _("Not Allowed: Disabled User") | tojson }}, 'red'); } else { - login.set_status('{{ _("Instructions Emailed") }}', 'green'); + login.set_status({{ _("Instructions Emailed") | tojson }}, 'green'); } @@ -266,7 +267,7 @@ login.login_handlers = (function () { if (cint(data.message[0]) == 0) { login.set_status(data.message[1], 'red'); } else { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status({{ _("Success") | tojson }}, 'green'); frappe.msgprint(data.message[1]) } //login.set_status(__(data.message), 'green'); @@ -274,7 +275,7 @@ login.login_handlers = (function () { //OTP verification if (data.verification && data.message != 'Logged In') { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status({{ _("Success") | tojson }}, 'green'); document.cookie = "tmp_id=" + data.tmp_id; @@ -287,10 +288,10 @@ login.login_handlers = (function () { } } }, - 401: get_error_handler('{{ _("Invalid Login. Try again.") }}'), - 417: get_error_handler('{{ _("Oops! Something went wrong.") }}'), - 404: get_error_handler('{{ _("User does not exist.")}}'), - 500: get_error_handler('{{ _("Something went wrong.") }}') + 401: get_error_handler({{ _("Invalid Login. Try again.") | tojson }}), + 417: get_error_handler({{ _("Oops! Something went wrong.") | tojson }}), + 404: get_error_handler({{ _("User does not exist.") | tojson }}), + 500: get_error_handler({{ _("Something went wrong.") | tojson }}) }; return login_handlers; @@ -322,7 +323,8 @@ var verify_token = function (event) { args.otp = $("#login_token").val(); args.tmp_id = frappe.get_cookie('tmp_id'); if (!args.otp) { - frappe.msgprint('{{ _("Login token required") }}'); + {# striptags is used to remove newlines, e is used for escaping #} + frappe.msgprint("{{ _('Login token required') | striptags | e }}"); return false; } login.call(args); @@ -336,11 +338,11 @@ var request_otp = function (r) { `
- {{ _("Verification") }} + {{ _("Verification") | e }}
- - + +
` ); @@ -354,11 +356,11 @@ var continue_otp_app = function (setup, qrcode) { var qrcode_div = $('
'); if (setup) { - direction = $('
').attr('id', 'qr_info').html('{{ _("Enter Code displayed in OTP App.") }}'); + direction = $('
').attr('id', 'qr_info').text({{ _("Enter Code displayed in OTP App.") | tojson }}); qrcode_div.append(direction); $('#otp_div').prepend(qrcode_div); } else { - direction = $('
').attr('id', 'qr_info').html('{{ _("OTP setup using OTP App was not completed. Please contact Administrator.") }}'); + direction = $('
').attr('id', 'qr_info').text({{ _("OTP setup using OTP App was not completed. Please contact Administrator.") | tojson }}); qrcode_div.append(direction); $('#otp_div').prepend(qrcode_div); } @@ -372,7 +374,7 @@ var continue_sms = function (setup, prompt) { sms_div.append(prompt) $('#otp_div').prepend(sms_div); } else { - direction = $('
').attr('id', 'qr_info').html(prompt || '{{ _("SMS was not sent. Please contact Administrator.") }}'); + direction = $('
').attr('id', 'qr_info').html(prompt || {{ _("SMS was not sent. Please contact Administrator.") | tojson }}); sms_div.append(direction); $('#otp_div').prepend(sms_div) } @@ -386,7 +388,7 @@ var continue_email = function (setup, prompt) { email_div.append(prompt) $('#otp_div').prepend(email_div); } else { - var direction = $('
').attr('id', 'qr_info').html(prompt || '{{ _("Verification code email not sent. Please contact Administrator.") }}'); + var direction = $('
').attr('id', 'qr_info').html(prompt || {{ _("Verification code email not sent. Please contact Administrator.") | tojson }}); email_div.append(direction); $('#otp_div').prepend(email_div); } From dd839e4d500c42f860cc8db46fb1a9ee5445f24d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 17:33:58 +0530 Subject: [PATCH 73/95] fix: lower default retention periods (#24697) These can still be configured to different number. We are just changing defaults. Why? - Scheduled job log - errors get logged in error log anyway. Success log isn't that useful of 90 days? - Prepared report - most reports are expired in a day or max a week. Rarely last month's report makes any sense today. --- frappe/hooks.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index 36a7748588..c5f19ebfb2 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -532,15 +532,15 @@ standard_help_items = [ # log doctype cleanups to automatically add in log settings default_log_clearing_doctypes = { - "Error Log": 30, - "Activity Log": 90, + "Error Log": 14, "Email Queue": 30, - "Scheduled Job Log": 90, - "Route History": 90, - "Submission Queue": 30, - "Prepared Report": 30, + "Scheduled Job Log": 7, + "Submission Queue": 7, + "Prepared Report": 14, "Webhook Request Log": 30, - "Integration Request": 90, "Unhandled Email": 30, "Reminder": 30, + "Integration Request": 90, + "Activity Log": 90, + "Route History": 90, } From 6341ef3ac70e078ac3d2c6a65171abfcdc929d2a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 18:31:03 +0530 Subject: [PATCH 74/95] fix: kanban hide "add column" for std fields (#24707) * fix: build events * fix(kanban): Don't show custom column option on standard fields --- .../public/js/frappe/build_events/build_events.bundle.js | 2 +- .../public/js/frappe/views/kanban/kanban_board.bundle.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/build_events/build_events.bundle.js b/frappe/public/js/frappe/build_events/build_events.bundle.js index a191156bfa..5635502507 100644 --- a/frappe/public/js/frappe/build_events/build_events.bundle.js +++ b/frappe/public/js/frappe/build_events/build_events.bundle.js @@ -17,7 +17,7 @@ frappe.realtime.on("build_event", (data) => { if (parts.length === 2) { let filename = parts[0].split("/").slice(-1)[0]; - frappe.assets.executed_ = frappe.assets.executed_.filter( + frappe.assets._executed = frappe.assets._executed.filter( (asset) => !asset.includes(`${filename}.bundle`) ); } diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js index d3690571dd..bca9889859 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js @@ -377,8 +377,13 @@ frappe.provide("frappe.views"); } function bind_add_column() { - if (!self.board_perms.write) { + let doctype = self.cur_list.doctype; + let fieldname = self.cur_list.board.field_name; + const is_custom_field = frappe.meta.get_docfield(doctype, fieldname)?.is_custom_field; + + if (!self.board_perms.write || !is_custom_field) { // If no write access to board, editing board (by adding column) should be blocked + // If standard field then users can't add options self.$kanban_board.find(".add-new-column").remove(); return; } From 7487df22c9f48bedc58cb63a3a3818e633cd2c91 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 18:43:43 +0530 Subject: [PATCH 75/95] refactor: use frappe.get_system_settings because it's cached and doesn't hit frappe.db at all. --- frappe/auth.py | 4 +--- .../address_template/address_template.py | 2 +- frappe/core/doctype/communication/mixins.py | 2 +- frappe/core/doctype/user/user.py | 14 +++++--------- frappe/desk/notifications.py | 2 +- .../auto_email_report/auto_email_report.py | 2 +- .../doctype/google_calendar/google_calendar.py | 2 +- frappe/twofactor.py | 18 ++++++++---------- frappe/utils/password_strength.py | 4 +--- 9 files changed, 20 insertions(+), 30 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 1e64e17ebb..521b0a6289 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -274,9 +274,7 @@ class LoginManager: if self.user in frappe.STANDARD_USERS: return False - reset_pwd_after_days = cint( - frappe.db.get_single_value("System Settings", "force_user_to_reset_password") - ) + reset_pwd_after_days = cint(frappe.get_system_settings("force_user_to_reset_password")) if reset_pwd_after_days: last_password_reset_date = ( diff --git a/frappe/contacts/doctype/address_template/address_template.py b/frappe/contacts/doctype/address_template/address_template.py index 601fb54302..2c9b27e585 100644 --- a/frappe/contacts/doctype/address_template/address_template.py +++ b/frappe/contacts/doctype/address_template/address_template.py @@ -28,7 +28,7 @@ class AddressTemplate(Document): if not self.is_default and not self._get_previous_default(): self.is_default = 1 - if frappe.db.get_single_value("System Settings", "setup_complete"): + if frappe.get_system_settings("setup_complete"): frappe.msgprint(_("Setting this Address Template as default as there is no other default")) def on_update(self): diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index 2c05570cdb..222778f28e 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -146,7 +146,7 @@ class CommunicationEmailMixin: return get_formatted_email(self.mail_sender_fullname(), mail=self.mail_sender()) def get_content(self, print_format=None): - if print_format and frappe.db.get_single_value("System Settings", "attach_view_link"): + if print_format and frappe.get_system_settings("attach_view_link"): return self.content + self.get_attach_link(print_format) return self.content diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index aacad57e9a..ec95d54094 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -725,12 +725,8 @@ class User(Document): 3. If allow_login_using_user_name is set, you can use username while finding the user. """ - login_with_mobile = cint( - frappe.db.get_single_value("System Settings", "allow_login_using_mobile_number") - ) - login_with_username = cint( - frappe.db.get_single_value("System Settings", "allow_login_using_user_name") - ) + login_with_mobile = cint(frappe.get_system_settings("allow_login_using_mobile_number")) + login_with_username = cint(frappe.get_system_settings("allow_login_using_user_name")) or_filters = [{"name": user_name}] if login_with_mobile: @@ -840,8 +836,8 @@ def update_password( else: user = res["user"] - logout_all_sessions = cint(logout_all_sessions) or frappe.db.get_single_value( - "System Settings", "logout_on_password_reset" + logout_all_sessions = cint(logout_all_sessions) or frappe.get_system_settings( + "logout_on_password_reset" ) _update_password(user, new_password, logout_all_sessions=cint(logout_all_sessions)) @@ -933,7 +929,7 @@ def _get_user_for_update_password(key, old_password): result.user, last_reset_password_key_generated_on = user or (None, None) if result.user: reset_password_link_expiry = cint( - frappe.db.get_single_value("System Settings", "reset_password_link_expiry_duration") + frappe.get_system_settings("reset_password_link_expiry_duration") ) if ( reset_password_link_expiry diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 4c728bdee9..090a3f6817 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -25,7 +25,7 @@ def get_notifications(): "open_count_doctype": {}, "targets": {}, } - if frappe.flags.in_install or not frappe.db.get_single_value("System Settings", "setup_complete"): + if frappe.flags.in_install or not frappe.get_system_settings("setup_complete"): return out config = get_notification_config() diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 6fe2596d7f..da93e7bb7e 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -92,7 +92,7 @@ class AutoEmailReport(Document): max_reports_per_user = ( cint(frappe.local.conf.max_reports_per_user) # kept for backward compatibilty - or cint(frappe.db.get_single_value("System Settings", "max_auto_email_report_per_user")) + or cint(frappe.get_system_settings("max_auto_email_report_per_user")) or 20 ) diff --git a/frappe/integrations/doctype/google_calendar/google_calendar.py b/frappe/integrations/doctype/google_calendar/google_calendar.py index 8430e5c80c..2afd3222c5 100644 --- a/frappe/integrations/doctype/google_calendar/google_calendar.py +++ b/frappe/integrations/doctype/google_calendar/google_calendar.py @@ -239,7 +239,7 @@ def check_google_calendar(account, google_calendar): # If no Calendar ID create a new Calendar calendar = { "summary": account.calendar_name, - "timeZone": frappe.db.get_single_value("System Settings", "time_zone"), + "timeZone": frappe.get_system_settings("time_zone"), } created_calendar = google_calendar.calendars().insert(body=calendar).execute() frappe.db.set_value( diff --git a/frappe/twofactor.py b/frappe/twofactor.py index 6b23e3c0e4..a53ec27d06 100644 --- a/frappe/twofactor.py +++ b/frappe/twofactor.py @@ -43,11 +43,9 @@ def toggle_two_factor_auth(state, roles=None): def two_factor_is_enabled(user=None): """Return True if 2FA is enabled.""" - enabled = cint(frappe.db.get_single_value("System Settings", "enable_two_factor_auth")) + enabled = cint(frappe.get_system_settings("enable_two_factor_auth")) if enabled: - bypass_two_factor_auth = cint( - frappe.db.get_single_value("System Settings", "bypass_2fa_for_retricted_ip_users") - ) + bypass_two_factor_auth = cint(frappe.get_system_settings("bypass_2fa_for_retricted_ip_users")) if bypass_two_factor_auth and user: user_doc = frappe.get_doc("User", user) restrict_ip_list = ( @@ -144,7 +142,7 @@ def get_otpsecret_for_(user): def get_verification_method(): - return frappe.db.get_single_value("System Settings", "two_factor_method") + return frappe.get_system_settings("two_factor_method") def confirm_otp_token(login_manager, otp=None, tmp_id=None): @@ -190,7 +188,7 @@ def confirm_otp_token(login_manager, otp=None, tmp_id=None): def get_verification_obj(user, token, otp_secret): - otp_issuer = frappe.db.get_single_value("System Settings", "otp_issuer_name") + otp_issuer = frappe.get_system_settings("otp_issuer_name") verification_method = get_verification_method() verification_obj = None if verification_method == "SMS": @@ -263,7 +261,7 @@ def process_2fa_for_email(user, token, otp_secret, otp_issuer, method="Email"): def get_email_subject_for_2fa(kwargs_dict): """Get email subject for 2fa.""" subject_template = _("Login Verification Code from {}").format( - frappe.db.get_single_value("System Settings", "otp_issuer_name") + frappe.get_system_settings("otp_issuer_name") ) return frappe.render_template(subject_template, kwargs_dict) @@ -281,7 +279,7 @@ def get_email_body_for_2fa(kwargs_dict): def get_email_subject_for_qr_code(kwargs_dict): """Get QRCode email subject.""" subject_template = _("One Time Password (OTP) Registration Code from {}").format( - frappe.db.get_single_value("System Settings", "otp_issuer_name") + frappe.get_system_settings("otp_issuer_name") ) return frappe.render_template(subject_template, kwargs_dict) @@ -299,7 +297,7 @@ def get_link_for_qrcode(user, totp_uri): key = frappe.generate_hash(length=20) key_user = f"{key}_user" key_uri = f"{key}_uri" - lifespan = int(frappe.db.get_single_value("System Settings", "lifespan_qrcode_image")) or 240 + lifespan = int(frappe.get_system_settings("lifespan_qrcode_image")) or 240 frappe.cache.set_value(key_uri, totp_uri, expires_in_sec=lifespan) frappe.cache.set_value(key_user, user, expires_in_sec=lifespan) return get_url(f"/qrcode?k={key}") @@ -433,7 +431,7 @@ def should_remove_barcode_image(barcode): """Check if it's time to delete barcode image from server.""" if isinstance(barcode, str): barcode = frappe.get_doc("File", barcode) - lifespan = frappe.db.get_single_value("System Settings", "lifespan_qrcode_image") or 240 + lifespan = frappe.get_system_settings("lifespan_qrcode_image") or 240 if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan): return True return False diff --git a/frappe/utils/password_strength.py b/frappe/utils/password_strength.py index 70ca375938..5b77eca4dd 100644 --- a/frappe/utils/password_strength.py +++ b/frappe/utils/password_strength.py @@ -50,9 +50,7 @@ default_feedback: "PasswordStrengthFeedback" = { def get_feedback(score: int, sequence: list) -> "PasswordStrengthFeedback": """Return the feedback dictionary consisting of ("warning","suggestions") for the given sequences.""" global default_feedback - minimum_password_score = int( - frappe.db.get_single_value("System Settings", "minimum_password_score") or 2 - ) + minimum_password_score = int(frappe.get_system_settings("minimum_password_score") or 2) # Starting feedback if len(sequence) == 0: From 7580125bae0e94171655b3219136e5fd0ae7afbb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 2 Feb 2024 18:57:47 +0530 Subject: [PATCH 76/95] fix: Filter auto repeat doctypes --- frappe/automation/doctype/auto_repeat/auto_repeat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 956bb0c9c3..e8db131ebb 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -550,7 +550,7 @@ def get_auto_repeat_doctypes(doctype, txt, searchfield, start, page_len, filters docs += [r.name for r in res] docs = set(list(docs)) - return [[d] for d in docs] + return [[d] for d in docs if txt in d] @frappe.whitelist() From 60c1812cc248c126971a60fbd0b9e905f4860ac0 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Fri, 2 Feb 2024 22:26:01 +0530 Subject: [PATCH 77/95] fix(run-tests): don't try to access `frappe.flags` before `frappe.init()` Broke in #24432 Signed-off-by: Akhil Narang --- frappe/commands/utils.py | 5 ++--- frappe/test_runner.py | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index a6eedf102a..333c37669f 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -772,9 +772,6 @@ def run_tests( click.secho(f"bench --site {site} set-config allow_tests true", fg="green") return - frappe.flags.skip_before_tests = skip_before_tests - frappe.flags.skip_test_records = skip_test_records - ret = frappe.test_runner.main( site, app, @@ -789,6 +786,8 @@ def run_tests( doctype_list_path=doctype_list_path, failfast=failfast, case=case, + skip_test_records=skip_test_records, + skip_before_tests=skip_before_tests, ) if len(ret.failures) == 0 and len(ret.errors) == 0: diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 9c072ae8e4..62e5dd599a 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -51,6 +51,8 @@ def main( doctype_list_path=None, failfast=False, case=None, + skip_test_records=False, + skip_before_tests=False, ): global unittest_runner @@ -58,6 +60,9 @@ def main( if not frappe.db: frappe.connect() + frappe.flags.skip_before_tests = skip_before_tests + frappe.flags.skip_test_records = skip_test_records + if doctype_list_path: app, doctype_list_path = doctype_list_path.split(os.path.sep, 1) with open(frappe.get_app_path(app, doctype_list_path)) as f: From 492584db93f9f9984d7af2d52e8b7b6dc5d64451 Mon Sep 17 00:00:00 2001 From: Govind Jangid Date: Sat, 3 Feb 2024 06:36:19 +0530 Subject: [PATCH 78/95] fix: date format & pre commit test --- .../auto_email_report/auto_email_report.py | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 2b82719232..fe2f25ca7b 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -2,9 +2,9 @@ # License: MIT. See LICENSE import calendar +import datetime from datetime import timedelta from email.utils import formataddr -import datetime import frappe from frappe import _ @@ -15,18 +15,18 @@ from frappe.utils import ( add_to_date, cint, format_time, + get_first_day, + get_first_day_of_week, get_link_to_form, + get_quarter_start, get_url_to_report, + get_year_start, + getdate, global_date_format, now, now_datetime, today, validate_email_address, - get_first_day_of_week, - get_first_day, - get_quarter_start, - get_year_start, - getdate, ) from frappe.utils.csvutils import to_csv from frappe.utils.xlsxutils import make_xlsx @@ -215,7 +215,7 @@ class AutoEmailReport(Document): self.filters = frappe.parse_json(self.filters) to_date = today() - + if self.use_first_day_of_period: from_date = to_date if self.dynamic_date_period == "Daily": @@ -244,7 +244,7 @@ class AutoEmailReport(Document): from_date = add_to_date(to_date, **{from_date_value[0]: from_date_value[1]}) self.set_date_filters(from_date, to_date) - + def set_date_filters(self, from_date, to_date): self.filters[self.from_date_field] = from_date self.filters[self.to_date_field] = to_date @@ -361,19 +361,22 @@ def update_field_types(columns): col.options = "" return columns + DATE_FORMAT = "%Y-%m-%d" + + def get_half_year_start(as_str=False): - """ - Returns the first day of the current half-year based on the current date. - """ - today_date = getdate(today()) + """ + Returns the first day of the current half-year based on the current date. + """ + today_date = getdate(today()) - half_year = 1 if today_date.month <= 6 else 2 + half_year = 1 if today_date.month <= 6 else 2 - year = today_date.year if half_year == 1 else today_date.year + 1 - month = 1 if half_year == 1 else 7 - day = 1 + year = today_date.year if half_year == 1 else today_date.year + 1 + month = 1 if half_year == 1 else 7 + day = 1 - result_date = datetime.date(year, month, day) + result_date = datetime.date(year, month, day) - return result_date if not as_str else result_date.strftime(DATE_FORMAT) + return result_date if not as_str else result_date.strftime(DATE_FORMAT) From f98af2e67af2e773080099e24f4aaefa6d8346d6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Sat, 3 Feb 2024 12:35:09 +0530 Subject: [PATCH 79/95] fix: redirect and open new doctype dialog if route to /doctype/new --- frappe/public/js/form_builder/store.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/form_builder/store.js b/frappe/public/js/form_builder/store.js index 91afafa707..7c0328362a 100644 --- a/frappe/public/js/form_builder/store.js +++ b/frappe/public/js/form_builder/store.js @@ -85,8 +85,13 @@ export const useStore = defineStore("form-builder-store", () => { async function fetch() { doc.value = frm.value.doc; - if (doctype.value.startsWith("new-doctype-") && !doc.value.fields) { - doc.value.fields = [get_df("Data", "", __("Title"))]; + if (doctype.value.startsWith("new-doctype-") && !doc.value.fields?.length) { + frappe.model.with_doctype("DocType").then(() => { + frappe.listview_settings["DocType"].new_doctype_dialog(); + }); + // redirect to /doctype + frappe.set_route("List", "DocType"); + return; } if (!get_docfields.value.length) { From 0314502742ebc2677919959871564aa0cf4881e2 Mon Sep 17 00:00:00 2001 From: "Indrajith.vs" <91895505+Gubbu77@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:40:50 +0530 Subject: [PATCH 80/95] fix(Custom Field): Button Rename Fieldname visible before saving (#24712) --- .../doctype/custom_field/custom_field.js | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.js b/frappe/custom/doctype/custom_field/custom_field.js index 031d53de20..d0c61b6d8c 100644 --- a/frappe/custom/doctype/custom_field/custom_field.js +++ b/frappe/custom/doctype/custom_field/custom_field.js @@ -112,28 +112,30 @@ frappe.ui.form.on("Custom Field", { } }, add_rename_field(frm) { - frm.add_custom_button(__("Rename Fieldname"), () => { - frappe.prompt( - { - fieldtype: "Data", - label: __("Fieldname"), - fieldname: "fieldname", - reqd: 1, - default: frm.doc.fieldname, - }, - function (data) { - frappe.call({ - method: "frappe.custom.doctype.custom_field.custom_field.rename_fieldname", - args: { - custom_field: frm.doc.name, - fieldname: data.fieldname, - }, - }); - }, - __("Rename Fieldname"), - __("Rename") - ); - }); + if (!frm.is_new()) { + frm.add_custom_button(__("Rename Fieldname"), () => { + frappe.prompt( + { + fieldtype: "Data", + label: __("Fieldname"), + fieldname: "fieldname", + reqd: 1, + default: frm.doc.fieldname, + }, + function (data) { + frappe.call({ + method: "frappe.custom.doctype.custom_field.custom_field.rename_fieldname", + args: { + custom_field: frm.doc.name, + fieldname: data.fieldname, + }, + }); + }, + __("Rename Fieldname"), + __("Rename") + ); + }); + } }, }); From 25692a3c8192857d9d67b767078c473752a18cb2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 3 Feb 2024 12:48:47 +0530 Subject: [PATCH 81/95] fix: Set default for search result limit if empty (#24713) --- frappe/core/doctype/system_settings/system_settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index db1511c893..013172a572 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -132,6 +132,9 @@ class SystemSettings(Document): self.validate_backup_limit() self.validate_file_extensions() + if not self.link_field_results_limit: + self.link_field_results_limit = 10 + if self.link_field_results_limit > 50: self.link_field_results_limit = 50 label = _(self.meta.get_label("link_field_results_limit")) From cfd5b91b6ea86643ab226f0892a67bf2926b67a6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Sat, 3 Feb 2024 14:32:55 +0530 Subject: [PATCH 82/95] fix: data import table UI fix --- .../public/js/frappe/data_import/import_preview.js | 2 +- frappe/public/scss/desk/data_import.scss | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/data_import/import_preview.js b/frappe/public/js/frappe/data_import/import_preview.js index 4b0f18e3e1..2dcd22acf3 100644 --- a/frappe/public/js/frappe/data_import/import_preview.js +++ b/frappe/public/js/frappe/data_import/import_preview.js @@ -34,7 +34,7 @@ frappe.data_import.ImportPreview = class ImportPreview {
-
+
diff --git a/frappe/public/scss/desk/data_import.scss b/frappe/public/scss/desk/data_import.scss index 2d40b82b71..4a82f22f06 100644 --- a/frappe/public/scss/desk/data_import.scss +++ b/frappe/public/scss/desk/data_import.scss @@ -14,4 +14,16 @@ .table-preview { margin-top: 12px; + + .datatable .dt-scrollable .dt-row:last-child .dt-cell { + border-bottom: 1px solid var(--border-color); + } + + .dt-row:last-child:not(.dt-row-filter) { + border-bottom: none; + } + + .datatable .dt-header .dt-row-header { + background-color: unset; + } } From 8b0c20246c7c3ef65d52c9f6d1dfaf375490cba6 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:23:24 +0100 Subject: [PATCH 83/95] fix: respect `null` as number value Null is the intentional absence of a value. It represents a variable that has been explicitly set to have no value. --- frappe/public/js/frappe/form/formatters.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 8e98c1042c..c068502fc7 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -49,6 +49,10 @@ frappe.form.formatters = { return __(frappe.form.formatters["Data"](value, df)); }, Float: function (value, docfield, options, doc) { + if (value === null) { + return ""; + } + // don't allow 0 precision for Floats, hence or'ing with null var precision = docfield.precision || @@ -73,12 +77,20 @@ frappe.form.formatters = { } }, Int: function (value, docfield, options) { + if (value === null) { + return ""; + } + if (cstr(docfield.options).trim() === "File Size") { return frappe.form.formatters.FileSize(value); } return frappe.form.formatters._right(value == null ? "" : cint(value), options); }, Percent: function (value, docfield, options) { + if (value === null) { + return ""; + } + const precision = docfield.precision || cint(frappe.boot.sysdefaults && frappe.boot.sysdefaults.float_precision) || @@ -105,6 +117,10 @@ frappe.form.formatters = {
`; }, Currency: function (value, docfield, options, doc) { + if (value === null) { + return ""; + } + var currency = frappe.meta.get_field_currency(docfield, doc); let precision; From 8f44392ea3949bfe33e86a9524e3e124cd79290f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:25:51 +0100 Subject: [PATCH 84/95] fix(widget dialog): translatability --- .../public/js/frappe/widgets/widget_dialog.js | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js index 0a67d29b4e..f9d1625aeb 100644 --- a/frappe/public/js/frappe/widgets/widget_dialog.js +++ b/frappe/public/js/frappe/widgets/widget_dialog.js @@ -32,12 +32,45 @@ class WidgetDialog { } get_title() { - // DO NOT REMOVE: Comment to load translation - // __("New Chart") __("New Shortcut") __("Edit Chart") __("Edit Shortcut") + if (this.editing) { + switch (this.type) { + case "chart": + return __("Edit Chart"); + case "shortcut": + return __("Edit Shortcut"); + case "links": + return __("Edit Links"); + case "number_card": + return __("Edit Number Card"); + case "onboarding": + return __("Edit Onboarding"); + case "quick_list": + return __("Edit Quick List"); + case "custom_block": + return __("Edit Custom Block"); + default: + return __("Edit {0}", [__(frappe.model.unscrub(this.type))]); + } + } - let action = this.editing ? "Edit" : "Add"; - let label = (action = action + " " + frappe.model.unscrub(this.type)); - return __(label); + switch (this.type) { + case "chart": + return __("New Chart"); + case "shortcut": + return __("New Shortcut"); + case "links": + return __("New Links"); + case "number_card": + return __("New Number Card"); + case "onboarding": + return __("New Onboarding"); + case "quick_list": + return __("New Quick List"); + case "custom_block": + return __("New Custom Block"); + default: + return __("New {0}", [__(frappe.model.unscrub(this.type))]); + } } get_fields() { From 6c8a08d955b0769cce1084019cd9d6d1129a1fac Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Mon, 5 Feb 2024 06:19:09 +0100 Subject: [PATCH 85/95] refactor: skip permlevel check if all levels are 0 (#24727) * refactor: skip permlevel check if all levels are 0 * chore: fix outdated docstring * chore: equality instead of le --- frappe/model/document.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/frappe/model/document.py b/frappe/model/document.py index 8ba9b0efd4..72d593de8b 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -679,23 +679,15 @@ class Document(BaseDocument): return same def apply_fieldlevel_read_permissions(self): - """Remove values the user is not allowed to read (called when loading in desk)""" - + """Remove values the user is not allowed to read.""" if frappe.session.user == "Administrator": return - has_higher_permlevel = False - all_fields = self.meta.fields.copy() for table_field in self.meta.get_table_fields(): all_fields += frappe.get_meta(table_field.options).fields or [] - for df in all_fields: - if df.permlevel > 0: - has_higher_permlevel = True - break - - if not has_higher_permlevel: + if all(df.permlevel == 0 for df in all_fields): return has_access_to = self.get_permlevel_access("read") From c02f5d587684a7f4e14edbc775b314c734060a13 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 5 Feb 2024 11:00:44 +0530 Subject: [PATCH 86/95] fix: Skip fulltext indexes during sync (#24728) These are not managed by framework's migration system so we shouldn't delete them. --- frappe/database/mariadb/database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 4a37ab6cc3..f91fe749bf 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -383,6 +383,7 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): WHERE Column_name = "{fieldname}" AND Seq_in_index = 1 AND Non_unique={int(not unique)} + AND Index_type != 'FULLTEXT' """, as_dict=True, ) From 1783cf7bb913babd30434f0338c93182b8422dc2 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:16:28 +0530 Subject: [PATCH 87/95] fix: title link check (#24731) * fix: listview filter * chore: use vanilla js --------- Co-authored-by: Ankush Menat --- frappe/public/js/frappe/form/controls/link.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 5fafd39c5d..93f932a6f7 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -90,7 +90,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat return frappe.boot?.translated_doctypes || [].includes(this.get_options()); } is_title_link() { - return frappe.boot?.link_title_doctypes || [].includes(this.get_options()); + return (frappe.boot?.link_title_doctypes || []).includes(this.get_options()); } async set_link_title(value) { const doctype = this.get_options(); From 12bbfd24e6da6ea3ebd942cb03e1a9ddb78828c5 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 5 Feb 2024 11:19:23 +0530 Subject: [PATCH 88/95] fix: Show proper error message for prepared report failure (#24733) --- frappe/desk/query_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 952d30a274..e0dfcfe3f4 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -291,9 +291,9 @@ def get_prepared_report_result(report, filters, dn="", user=None): try: if data := json.loads(doc.get_prepared_data().decode("utf-8")): report_data = get_report_data(doc, data) - except Exception: + except Exception as e: doc.log_error("Prepared report render failed") - frappe.msgprint(_("Prepared report render failed")) + frappe.msgprint(_("Prepared report render failed") + f": {str(e)}") doc = None return report_data | {"prepared_report": True, "doc": doc} From dc6d71e84b7eb9a75b06a311b89aeaba762ad93f Mon Sep 17 00:00:00 2001 From: bhavesh95863 Date: Sun, 4 Feb 2024 13:34:39 +0530 Subject: [PATCH 89/95] fix: no of rows displayed based on report type (cherry picked from commit db4d36f527ace8f4a678ebe39daceaa9f9579a73) --- .../email/doctype/auto_email_report/auto_email_report.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.json b/frappe/email/doctype/auto_email_report/auto_email_report.json index f208626500..41beac3e3a 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.json +++ b/frappe/email/doctype/auto_email_report/auto_email_report.json @@ -88,6 +88,7 @@ }, { "default": "100", + "depends_on": "eval:doc.report_type=='Report Builder'", "fieldname": "no_of_rows", "fieldtype": "Int", "label": "No of Rows (Max 500)" @@ -211,15 +212,15 @@ }, { "default": "0", + "depends_on": "eval: doc.dynamic_date_period != 'Daily'", "description": "To begin the date range at the start of the chosen period. For example, if 'Year' is selected as the period, the report will start from January 1st of the current year.", "fieldname": "use_first_day_of_period", "fieldtype": "Check", - "depends_on": "eval: doc.dynamic_date_period != 'Daily'", "label": "Use First Day of Period" } ], "links": [], - "modified": "2024-01-29 11:42:27.433958", + "modified": "2024-02-04 13:31:08.624648", "modified_by": "Administrator", "module": "Email", "name": "Auto Email Report", @@ -254,4 +255,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} +} \ No newline at end of file From b4b4d8f22fa0d7424b3e5ab8064acdc7d924dfea Mon Sep 17 00:00:00 2001 From: bhavesh95863 Date: Mon, 5 Feb 2024 04:08:19 +0530 Subject: [PATCH 90/95] fix: hide number counter for new document view shortcut (cherry picked from commit bb46d23772cfa9ad2e23a24325b16ed17fdfee6c) --- frappe/public/js/frappe/widgets/shortcut_widget.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/widgets/shortcut_widget.js b/frappe/public/js/frappe/widgets/shortcut_widget.js index 18d05043e3..72e7e152e6 100644 --- a/frappe/public/js/frappe/widgets/shortcut_widget.js +++ b/frappe/public/js/frappe/widgets/shortcut_widget.js @@ -79,7 +79,8 @@ export default class ShortcutWidget extends Widget { }); let filters = frappe.utils.process_filter_expression(this.stats_filter); - if (this.type == "DocType" && filters) { + + if (this.type == "DocType" && this.doc_view != "New" && filters) { frappe.db .count(this.link_to, { filters: filters, From 3c183344aa501cce26b2f5df33db1d321fcc1135 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 22:16:06 +0530 Subject: [PATCH 91/95] feat: profile requests using recorder WIP: - [x] Basic working feature - [ ] Make this optional, this has insanely high overhead. - [ ] Specify requests/function filter to profile/record. This will allow better recording in production sites. - [ ] Make SQL profiling optional too --- frappe/core/doctype/recorder/recorder.json | 16 ++++++++++++++-- frappe/core/doctype/recorder/recorder.py | 1 + frappe/recorder.py | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/recorder/recorder.json b/frappe/core/doctype/recorder/recorder.json index 391f808f31..72291dbfe2 100644 --- a/frappe/core/doctype/recorder/recorder.json +++ b/frappe/core/doctype/recorder/recorder.json @@ -20,7 +20,9 @@ "section_break_sgro", "form_dict", "section_break_9jhm", - "sql_queries" + "sql_queries", + "section_break_optn", + "profile" ], "fields": [ { @@ -107,6 +109,16 @@ "fieldtype": "Data", "hidden": 1, "label": "Event Type" + }, + { + "fieldname": "section_break_optn", + "fieldtype": "Section Break" + }, + { + "fieldname": "profile", + "fieldtype": "Code", + "label": "cProfile Output", + "read_only": 1 } ], "hide_toolbar": 1, @@ -114,7 +126,7 @@ "index_web_pages_for_search": 1, "is_virtual": 1, "links": [], - "modified": "2024-01-03 16:45:47.110048", + "modified": "2024-02-01 22:13:26.505174", "modified_by": "Administrator", "module": "Core", "name": "Recorder", diff --git a/frappe/core/doctype/recorder/recorder.py b/frappe/core/doctype/recorder/recorder.py index 347a237743..26ccfcf378 100644 --- a/frappe/core/doctype/recorder/recorder.py +++ b/frappe/core/doctype/recorder/recorder.py @@ -24,6 +24,7 @@ class Recorder(Document): method: DF.Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] number_of_queries: DF.Int path: DF.Data | None + profile: DF.Code | None request_headers: DF.Code | None sql_queries: DF.Table[RecorderQuery] time: DF.Datetime | None diff --git a/frappe/recorder.py b/frappe/recorder.py index 3094f83ad6..bb47304fe5 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -1,9 +1,12 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import cProfile import datetime import functools import inspect +import io import json +import pstats import re import time from collections import Counter @@ -148,6 +151,8 @@ class Recorder: self.uuid = frappe.generate_hash(length=10) self.time = datetime.datetime.now() self.calls = [] + self.profiler = cProfile.Profile() + self.profiler.enable() if frappe.request: self.path = frappe.request.path self.cmd = frappe.local.form_dict.cmd or "" @@ -176,6 +181,13 @@ class Recorder: self.calls.append(data) def dump(self): + self.profiler.disable() + + profiler_output = io.StringIO() + pstats.Stats(self.profiler, stream=profiler_output).strip_dirs().sort_stats( + "cumulative" + ).print_stats() + request_data = { "uuid": self.uuid, "path": self.path, @@ -197,6 +209,8 @@ class Recorder: request_data["calls"] = self.calls request_data["headers"] = self.headers request_data["form_dict"] = self.form_dict + request_data["profile"] = profiler_output.getvalue() + profiler_output.close() frappe.cache.hset(RECORDER_REQUEST_HASH, self.uuid, request_data) From e494cb4f6e3b3c1b306546759b2e2ff4522c68a3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 3 Feb 2024 12:16:25 +0530 Subject: [PATCH 92/95] feat: configurable recording Configurable behaviours: - Record SQL queries - Explain queries - Record stack frames - Run profiler - Filter some requests --- frappe/core/doctype/recorder/recorder_list.js | 88 ++++++++++++-- frappe/recorder.py | 107 ++++++++++++++---- 2 files changed, 163 insertions(+), 32 deletions(-) diff --git a/frappe/core/doctype/recorder/recorder_list.js b/frappe/core/doctype/recorder/recorder_list.js index a0eadae260..528ebc5089 100644 --- a/frappe/core/doctype/recorder/recorder_list.js +++ b/frappe/core/doctype/recorder/recorder_list.js @@ -88,18 +88,90 @@ frappe.listview_settings["Recorder"] = { }, setup_recorder_controls(listview) { + let me = this; listview.page.set_primary_action(listview.enabled ? __("Stop") : __("Start"), () => { - frappe.call({ - method: listview.enabled ? "frappe.recorder.stop" : "frappe.recorder.start", - callback: function () { - listview.refresh(); - }, - }); - listview.enabled = !listview.enabled; - this.refresh_controls(listview); + if (listview.enabled) { + me.stop_recorder(listview); + } else { + me.start_recorder(listview); + } }); }, + stop_recorder(listview) { + let me = this; + frappe.xcall("frappe.recorder.stop", {}).then(() => { + listview.refresh(); + listview.enabled = false; + me.refresh_controls(listview); + }); + }, + + start_recorder(listview) { + let me = this; + frappe.prompt( + [ + { + fieldtype: "Section Break", + fieldname: "sql_section", + label: "SQL", + }, + { + fieldname: "record_sql", + fieldtype: "Check", + label: "Record SQL Queries", + default: 1, + }, + { + fieldname: "explain", + fieldtype: "Check", + label: "Generate EXPLAIN for SQL queries", + default: 1, + }, + { + fieldname: "capture_stack", + fieldtype: "Check", + label: "Capture callstack of SQL queries", + default: 1, + }, + { + fieldtype: "Section Break", + fieldname: "python_section", + label: "Python", + }, + { + fieldname: "profile", + fieldtype: "Check", + label: "Run cProfile", + default: 0, + description: + "Warning: cProfile adds a lot of overhead. For best results, disable stack capturing when using cProfile.", + }, + { + fieldtype: "Section Break", + fieldname: "filter_section", + }, + { + fieldname: "filter", + fieldtype: "Data", + label: "Filter Path", + default: "/", + description: + "This will be used for filtering paths which will be recorded. You can use this to avoid slowing down other traffic. e.g. /api/method/erpnext", + }, + ], + (values) => { + frappe.xcall("frappe.recorder.start", values).then(() => { + listview.refresh(); + listview.enabled = true; + me.refresh_controls(listview); + }); + }, + __("Configure Recorder"), + __("Start Recordig") + ); + }, + update_indicators(listview) { if (listview.enabled) { listview.page.set_indicator(__("Active"), "green"); diff --git a/frappe/recorder.py b/frappe/recorder.py index bb47304fe5..b9301c30ac 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -11,6 +11,7 @@ import re import time from collections import Counter from collections.abc import Callable +from dataclasses import dataclass import sqlparse @@ -19,17 +20,37 @@ from frappe import _ from frappe.database.database import is_query_type RECORDER_INTERCEPT_FLAG = "recorder-intercept" +RECORDER_CONFIG_FLAG = "recorder-config" RECORDER_REQUEST_SPARSE_HASH = "recorder-requests-sparse" RECORDER_REQUEST_HASH = "recorder-requests" TRACEBACK_PATH_PATTERN = re.compile(".*/apps/") +RECORDER_AUTO_DISABLE = 5 * 60 -def sql(*args, **kwargs): +@dataclass +class RecorderConfig: + record_sql: bool = True + capture_stack: bool = True + profile: bool = False + explain: bool = True + filter: str = "/" + + def store(self): + frappe.cache.set_value(RECORDER_CONFIG_FLAG, self, expires_in_sec=RECORDER_AUTO_DISABLE) + + @classmethod + def retrieve(cls): + return frappe.cache.get_value(RECORDER_CONFIG_FLAG) or cls() + + +def record_sql(*args, **kwargs): start_time = time.monotonic() result = frappe.db._sql(*args, **kwargs) end_time = time.monotonic() - stack = list(get_current_stack_frames()) + stack = [] + if frappe.local._recorder.config.capture_stack: + stack = list(get_current_stack_frames()) data = { "query": str(frappe.db.last_query), @@ -72,6 +93,7 @@ def post_process(): frappe.db.rollback() frappe.db.begin(read_only=True) # Explicitly start read only transaction + config = RecorderConfig.retrieve() result = list(frappe.cache.hgetall(RECORDER_REQUEST_HASH).values()) for request in result: @@ -82,7 +104,7 @@ def post_process(): call["query"] = formatted_query # Collect EXPLAIN for executed query - if is_query_type(formatted_query, ("select", "update", "delete")): + if config.explain and is_query_type(formatted_query, ("select", "update", "delete")): # Only SELECT/UPDATE/DELETE queries can be "EXPLAIN"ed try: call["explain_result"] = frappe.db.sql(f"EXPLAIN {formatted_query}", as_dict=True) @@ -148,11 +170,17 @@ def dump(): class Recorder: def __init__(self): + self.config = RecorderConfig.retrieve() self.uuid = frappe.generate_hash(length=10) self.time = datetime.datetime.now() self.calls = [] - self.profiler = cProfile.Profile() - self.profiler.enable() + self._patched_sql = False + + self.profiler = None + if self.config.profile: + self.profiler = cProfile.Profile() + self.profiler.enable() + if frappe.request: self.path = frappe.request.path self.cmd = frappe.local.form_dict.cmd or "" @@ -175,18 +203,32 @@ class Recorder: self.headers = None self.form_dict = None - _patch() + if self.config.record_sql: + self._patch_sql() + self._patched_sql = True def register(self, data): self.calls.append(data) - def dump(self): - self.profiler.disable() + def cleanup(self): + if self.profiler: + self.profiler.disable() + if self._patched_sql: + self._unpatch_sql() - profiler_output = io.StringIO() - pstats.Stats(self.profiler, stream=profiler_output).strip_dirs().sort_stats( - "cumulative" - ).print_stats() + def process_profiler(self): + if self.config.profile or self.profiler: + self.profiler.disable() + profiler_output = io.StringIO() + pstats.Stats(self.profiler, stream=profiler_output).strip_dirs().sort_stats( + "cumulative" + ).print_stats() + profile = profiler_output.getvalue() + profiler_output.close() + return profile + + def dump(self): + profiler_output = self.process_profiler() request_data = { "uuid": self.uuid, @@ -209,26 +251,28 @@ class Recorder: request_data["calls"] = self.calls request_data["headers"] = self.headers request_data["form_dict"] = self.form_dict - request_data["profile"] = profiler_output.getvalue() - profiler_output.close() + request_data["profile"] = profiler_output frappe.cache.hset(RECORDER_REQUEST_HASH, self.uuid, request_data) + if self.config.record_sql: + self._unpatch_sql() -def _patch(): - frappe.db._sql = frappe.db.sql - frappe.db.sql = sql + @staticmethod + def _patch_sql(): + frappe.db._sql = frappe.db.sql + frappe.db.sql = record_sql - -def _unpatch(): - frappe.db.sql = frappe.db._sql + @staticmethod + def _unpatch_sql(): + frappe.db.sql = frappe.db._sql def do_not_record(function): @functools.wraps(function) def wrapper(*args, **kwargs): if hasattr(frappe.local, "_recorder"): + frappe.local._recorder.cleanup() del frappe.local._recorder - frappe.db.sql = frappe.db._sql return function(*args, **kwargs) return wrapper @@ -254,8 +298,23 @@ def status(*args, **kwargs): @frappe.whitelist() @do_not_record @administrator_only -def start(*args, **kwargs): - frappe.cache.set_value(RECORDER_INTERCEPT_FLAG, 1, expires_in_sec=60 * 60) +def start( + record_sql: bool = True, + profile: bool = False, + capture_stack: bool = True, + explain: bool = True, + filter: str = "/", + *args, + **kwargs, +): + frappe.cache.set_value(RECORDER_INTERCEPT_FLAG, 1, expires_in_sec=RECORDER_AUTO_DISABLE) + RecorderConfig( + record_sql=int(record_sql), + profile=int(profile), + capture_stack=int(capture_stack), + explain=int(explain), + filter=filter, + ).store() @frappe.whitelist() @@ -301,7 +360,7 @@ def record_queries(func: Callable): frappe.local._recorder.path = f"Function call: {func.__module__}.{func.__qualname__}" ret = func(*args, **kwargs) dump() - _unpatch() + Recorder._unpatch_sql() post_process() print("Recorded queries, open recorder to view them.") return ret From 009b06d97447da4017f3109b90c775e4368be415 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 3 Feb 2024 14:46:55 +0530 Subject: [PATCH 93/95] feat: Jobs/Request filtering --- frappe/core/doctype/recorder/recorder_list.js | 66 +++++++++++++------ frappe/recorder.py | 66 +++++++++++-------- 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/frappe/core/doctype/recorder/recorder_list.js b/frappe/core/doctype/recorder/recorder_list.js index 528ebc5089..8ec9f2ae40 100644 --- a/frappe/core/doctype/recorder/recorder_list.js +++ b/frappe/core/doctype/recorder/recorder_list.js @@ -10,12 +10,7 @@ frappe.listview_settings["Recorder"] = { } listview.page.add_button(__("Clear"), () => { - frappe.call({ - method: "frappe.recorder.delete", - callback: function () { - listview.refresh(); - }, - }); + frappe.xcall("frappe.recorder.delete").then(listview.refresh); }); listview.page.add_menu_item(__("Import"), () => { @@ -111,6 +106,51 @@ frappe.listview_settings["Recorder"] = { let me = this; frappe.prompt( [ + { + fieldtype: "Section Break", + fieldname: "req_job_section", + }, + { + fieldtype: "Column Break", + fieldname: "web_request_columns", + label: "Web Requests", + }, + { + fieldname: "record_requests", + fieldtype: "Check", + label: "Record Web Requests", + default: 1, + }, + { + fieldname: "request_filter", + fieldtype: "Data", + label: "Request path filter", + default: "/", + depends_on: "record_requests", + description: + "This will be used for filtering paths which will be recorded. You can use this to avoid slowing down other traffic. e.g. /api/method/erpnext", + }, + { + fieldtype: "Column Break", + fieldname: "background_col", + label: "Background Jobs", + }, + + { + fieldname: "record_jobs", + fieldtype: "Check", + label: "Record Background Jobs", + default: 1, + }, + { + fieldname: "jobs_filter", + fieldtype: "Data", + label: "Background Jobs filter", + default: "", + depends_on: "record_jobs", + description: + "This will be used for filtering jobs which will be recorded. You can use this to avoid slowing down other jobs. e.g. email_queue.pull", + }, { fieldtype: "Section Break", fieldname: "sql_section", @@ -119,7 +159,7 @@ frappe.listview_settings["Recorder"] = { { fieldname: "record_sql", fieldtype: "Check", - label: "Record SQL Queries", + label: "Record SQL queries", default: 1, }, { @@ -147,18 +187,6 @@ frappe.listview_settings["Recorder"] = { description: "Warning: cProfile adds a lot of overhead. For best results, disable stack capturing when using cProfile.", }, - { - fieldtype: "Section Break", - fieldname: "filter_section", - }, - { - fieldname: "filter", - fieldtype: "Data", - label: "Filter Path", - default: "/", - description: - "This will be used for filtering paths which will be recorded. You can use this to avoid slowing down other traffic. e.g. /api/method/erpnext", - }, ], (values) => { frappe.xcall("frappe.recorder.start", values).then(() => { diff --git a/frappe/recorder.py b/frappe/recorder.py index b9301c30ac..bcc3f55b9e 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -29,11 +29,18 @@ RECORDER_AUTO_DISABLE = 5 * 60 @dataclass class RecorderConfig: - record_sql: bool = True - capture_stack: bool = True - profile: bool = False - explain: bool = True - filter: str = "/" + record_requests: bool = True # Record web request + record_jobs: bool = True # record background jobs + record_sql: bool = True # Record SQL queries + capture_stack: bool = True # Recod call stack of SQL queries + profile: bool = False # Run cProfile + explain: bool = True # Provide explain output of SQL queries + request_filter: str = "/" # Filter request paths + jobs_filter: str = "" # Filter background jobs + + def __post_init__(self): + if not (self.record_jobs or self.record_requests): + frappe.throw("You must record one of jobs or requests") def store(self): frappe.cache.set_value(RECORDER_CONFIG_FLAG, self, expires_in_sec=RECORDER_AUTO_DISABLE) @@ -171,24 +178,23 @@ def dump(): class Recorder: def __init__(self): self.config = RecorderConfig.retrieve() - self.uuid = frappe.generate_hash(length=10) - self.time = datetime.datetime.now() self.calls = [] self._patched_sql = False - self.profiler = None - if self.config.profile: - self.profiler = cProfile.Profile() - self.profiler.enable() + self._recording = True - if frappe.request: + if ( + self.config.record_requests + and frappe.request + and self.config.request_filter in frappe.request.path + ): self.path = frappe.request.path self.cmd = frappe.local.form_dict.cmd or "" self.method = frappe.request.method self.headers = dict(frappe.local.request.headers) self.form_dict = frappe.local.form_dict self.event_type = "HTTP Request" - elif frappe.job: + elif self.config.record_jobs and frappe.job and self.config.jobs_filter in frappe.job.method: self.event_type = "Background Job" self.path = frappe.job.method self.cmd = None @@ -196,17 +202,20 @@ class Recorder: self.headers = None self.form_dict = None else: - self.event_type = None - self.path = None - self.cmd = None - self.method = None - self.headers = None - self.form_dict = None + self._recording = False + return + + self.uuid = frappe.generate_hash(length=10) + self.time = datetime.datetime.now() if self.config.record_sql: self._patch_sql() self._patched_sql = True + if self.config.profile: + self.profiler = cProfile.Profile() + self.profiler.enable() + def register(self, data): self.calls.append(data) @@ -228,6 +237,8 @@ class Recorder: return profile def dump(self): + if not self._recording: + return profiler_output = self.process_profiler() request_data = { @@ -242,11 +253,6 @@ class Recorder: "event_type": self.event_type, } frappe.cache.hset(RECORDER_REQUEST_SPARSE_HASH, self.uuid, request_data) - frappe.publish_realtime( - event="recorder-dump-event", - message=json.dumps(request_data, default=str), - user="Administrator", - ) request_data["calls"] = self.calls request_data["headers"] = self.headers @@ -299,22 +305,28 @@ def status(*args, **kwargs): @do_not_record @administrator_only def start( + record_jobs: bool = True, + record_requests: bool = True, record_sql: bool = True, profile: bool = False, capture_stack: bool = True, explain: bool = True, - filter: str = "/", + request_filter: str = "/", + jobs_filter: str = "", *args, **kwargs, ): - frappe.cache.set_value(RECORDER_INTERCEPT_FLAG, 1, expires_in_sec=RECORDER_AUTO_DISABLE) RecorderConfig( + record_requests=int(record_requests), + record_jobs=int(record_jobs), record_sql=int(record_sql), profile=int(profile), capture_stack=int(capture_stack), explain=int(explain), - filter=filter, + request_filter=request_filter, + jobs_filter=jobs_filter, ).store() + frappe.cache.set_value(RECORDER_INTERCEPT_FLAG, 1, expires_in_sec=RECORDER_AUTO_DISABLE) @frappe.whitelist() From 9107627dac250f67057fed95b3739c1326dd7125 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 3 Feb 2024 15:48:06 +0530 Subject: [PATCH 94/95] fix: recorder timezone bug use system timezone to avoid showing incorrect timestamp --- frappe/core/doctype/recorder/recorder_list.js | 10 +++++--- frappe/recorder.py | 25 ++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/frappe/core/doctype/recorder/recorder_list.js b/frappe/core/doctype/recorder/recorder_list.js index 8ec9f2ae40..35498032ed 100644 --- a/frappe/core/doctype/recorder/recorder_list.js +++ b/frappe/core/doctype/recorder/recorder_list.js @@ -127,8 +127,9 @@ frappe.listview_settings["Recorder"] = { label: "Request path filter", default: "/", depends_on: "record_requests", - description: - "This will be used for filtering paths which will be recorded. You can use this to avoid slowing down other traffic. e.g. /api/method/erpnext", + description: `This will be used for filtering paths which will be recorded. + You can use this to avoid slowing down other traffic. + e.g. /api/method/erpnext. Leave it empty to record every request.`, }, { fieldtype: "Column Break", @@ -148,8 +149,9 @@ frappe.listview_settings["Recorder"] = { label: "Background Jobs filter", default: "", depends_on: "record_jobs", - description: - "This will be used for filtering jobs which will be recorded. You can use this to avoid slowing down other jobs. e.g. email_queue.pull", + description: `This will be used for filtering jobs which will be recorded. + You can use this to avoid slowing down other jobs. e.g. email_queue.pull. + Leave it empty to record every job.`, }, { fieldtype: "Section Break", diff --git a/frappe/recorder.py b/frappe/recorder.py index bcc3f55b9e..a0b4c17880 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import cProfile -import datetime import functools import inspect import io @@ -18,6 +17,7 @@ import sqlparse import frappe from frappe import _ from frappe.database.database import is_query_type +from frappe.utils import now_datetime RECORDER_INTERCEPT_FLAG = "recorder-intercept" RECORDER_CONFIG_FLAG = "recorder-config" @@ -49,6 +49,10 @@ class RecorderConfig: def retrieve(cls): return frappe.cache.get_value(RECORDER_CONFIG_FLAG) or cls() + @staticmethod + def delete(): + frappe.cache.delete_value(RECORDER_CONFIG_FLAG) + def record_sql(*args, **kwargs): start_time = time.monotonic() @@ -120,6 +124,8 @@ def post_process(): mark_duplicates(request) frappe.cache.hset(RECORDER_REQUEST_HASH, request["uuid"], request) + config.delete() + def mark_duplicates(request): exact_duplicates = Counter([call["query"] for call in request["calls"]]) @@ -166,7 +172,7 @@ def normalize_query(query: str) -> str: def record(force=False): if __debug__: if frappe.cache.get_value(RECORDER_INTERCEPT_FLAG) or force: - frappe.local._recorder = Recorder() + frappe.local._recorder = Recorder(force=force) def dump(): @@ -176,12 +182,17 @@ def dump(): class Recorder: - def __init__(self): + def __init__(self, force=False): self.config = RecorderConfig.retrieve() self.calls = [] self._patched_sql = False self.profiler = None self._recording = True + self.force = force + self.cmd = None + self.method = None + self.headers = None + self.form_dict = None if ( self.config.record_requests @@ -201,12 +212,14 @@ class Recorder: self.method = None self.headers = None self.form_dict = None - else: + elif not self.force: self._recording = False return + else: + self.event_type = "Function Call" self.uuid = frappe.generate_hash(length=10) - self.time = datetime.datetime.now() + self.time = now_datetime() if self.config.record_sql: self._patch_sql() @@ -248,7 +261,7 @@ class Recorder: "time": self.time, "queries": len(self.calls), "time_queries": float("{:0.3f}".format(sum(call["duration"] for call in self.calls))), - "duration": float(f"{(datetime.datetime.now() - self.time).total_seconds() * 1000:0.3f}"), + "duration": float(f"{(now_datetime() - self.time).total_seconds() * 1000:0.3f}"), "method": self.method, "event_type": self.event_type, } From 985c95e9007f89744d478d57d1f94658f6ecc124 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:12:25 +0530 Subject: [PATCH 95/95] fix: check is_translatable link (#24739) --- frappe/public/js/frappe/form/controls/link.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 93f932a6f7..d5485fab37 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 frappe.boot?.translated_doctypes || [].includes(this.get_options()); + return (frappe.boot?.translated_doctypes || []).includes(this.get_options()); } is_title_link() { return (frappe.boot?.link_title_doctypes || []).includes(this.get_options());