diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 3bba9a2600..a69e01616d 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -2,9 +2,11 @@ set -e cd ~ || exit -echo "Setting Up Bench..." - +echo "::group::Install Bench" pip install frappe-bench +echo "::endgroup::" + +echo "::group::Init Bench" bench -v init frappe-bench --skip-assets --python "$(which python)" --frappe-path "${GITHUB_WORKSPACE}" cd ./frappe-bench || exit @@ -13,9 +15,9 @@ if [ "$TYPE" == "ui" ] then bench -v setup requirements --node; fi +echo "::endgroup::" -echo "Setting Up Sites & Database..." - +echo "::group::Create Test Site" mkdir ~/frappe-bench/sites/test_site cp "${GITHUB_WORKSPACE}/.github/helper/db/$DB.json" ~/frappe-bench/sites/test_site/site_config.json @@ -35,9 +37,9 @@ then echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres; echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres; fi +echo "::endgroup::" -echo "Setting Up Procfile..." - +echo "::group::Modify processes" sed -i 's/^watch:/# watch:/g' Procfile sed -i 's/^schedule:/# schedule:/g' Procfile @@ -51,11 +53,11 @@ if [ "$TYPE" == "ui" ] then sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile fi - -echo "Starting Bench..." +echo "::endgroup::" bench start &> ~/frappe-bench/bench_start.log & +echo "::group::Install site" if [ "$TYPE" == "server" ] then CI=Yes bench build --app frappe & @@ -69,3 +71,4 @@ then # wait till assets are built successfully wait $build_pid fi +echo "::endgroup::" diff --git a/.github/helper/install_dependencies.sh b/.github/helper/install_dependencies.sh index 574144b823..f382b61dda 100644 --- a/.github/helper/install_dependencies.sh +++ b/.github/helper/install_dependencies.sh @@ -3,6 +3,7 @@ set -e echo "Setting Up System Dependencies..." +echo "::group::apt packages" sudo apt update sudo apt remove mysql-server mysql-client sudo apt install libcups2-dev redis-server mariadb-client-10.6 @@ -12,3 +13,4 @@ install_wkhtmltopdf() { sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb } install_wkhtmltopdf & +echo "::endgroup::" diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index dff04a5693..a8f97a798c 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -36,9 +36,8 @@ context("Awesome Bar", () => { cy.get(".title-text").should("contain", "To Do"); cy.wait(200); // Wait a bit longer before checking the filter. cy.get('[data-original-title="ID"] > input').should("have.value", "%test%"); - }); - it("filter preserved, now finds something else", () => { + // filter preserved, now finds something else cy.visit("/app/todo"); cy.get(".title-text").should("contain", "To Do"); cy.wait(200); // Wait a bit longer before checking the filter. diff --git a/cypress/integration/list_paging.js b/cypress/integration/list_paging.js index 494ca6ae74..2ce347828a 100644 --- a/cypress/integration/list_paging.js +++ b/cypress/integration/list_paging.js @@ -12,7 +12,7 @@ context("List Paging", () => { it("test load more with count selection buttons", () => { cy.visit("/app/todo/view/report"); - cy.get(".filter-x-button").click(); + cy.clear_filters(); cy.get(".list-paging-area .list-count").should("contain.text", "20 of"); cy.get(".list-paging-area .btn-more").click(); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 66defa88f7..cf94335a74 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -450,7 +450,7 @@ Cypress.Commands.add("click_menu_button", (name) => { Cypress.Commands.add("clear_filters", () => { cy.get(".filter-x-button").click({ force: true }); - cy.wait(500); + cy.wait(1000); }); Cypress.Commands.add("click_modal_primary_button", (btn_name) => { diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index dc40fb0336..b37ebda9ee 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -1,4 +1,14 @@ frappe.ui.form.on("User", { + setup: function (frm) { + frm.set_query("default_workspace", () => { + return { + filters: { + for_user: ["in", [null, frappe.session.user]], + title: ["!=", "Welcome Workspace"], + }, + }; + }); + }, before_load: function (frm) { let update_tz_options = function () { frm.fields_dict.time_zone.set_data(frappe.all_timezones); diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 894216a3e0..9bdfbf875f 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -71,6 +71,8 @@ "send_me_a_copy", "allowed_in_mentions", "user_emails", + "workspace_section", + "default_workspace", "sb2", "defaults", "sb3", @@ -711,6 +713,19 @@ "label": "Role Profiles", "options": "User Role Profile", "permlevel": 1 + }, + { + "description": "If left empty, the default workspace will be the last visited workspace", + "fieldname": "default_workspace", + "fieldtype": "Link", + "label": "Default Workspace", + "options": "Workspace" + }, + { + "collapsible": 1, + "fieldname": "workspace_section", + "fieldtype": "Section Break", + "label": "Workspace" } ], "icon": "fa fa-user", @@ -773,7 +788,7 @@ "link_fieldname": "user" } ], - "modified": "2024-02-11 13:16:29.574666", + "modified": "2024-03-20 17:15:19.200383", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 32e398a32a..3d38348121 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -63,6 +63,7 @@ class User(Document): birth_date: DF.Date | None block_modules: DF.Table[BlockModule] bypass_restrict_ip_check_if_2fa_enabled: DF.Check + default_workspace: DF.Link | None defaults: DF.Table[DefaultValue] desk_theme: DF.Literal["Light", "Dark", "Automatic"] document_follow_frequency: DF.Literal["Hourly", "Daily", "Weekly"] diff --git a/frappe/desk/doctype/todo/todo_list.js b/frappe/desk/doctype/todo/todo_list.js index c80e3445ca..b21f412801 100644 --- a/frappe/desk/doctype/todo/todo_list.js +++ b/frappe/desk/doctype/todo/todo_list.js @@ -3,12 +3,6 @@ frappe.listview_settings["ToDo"] = { add_fields: ["reference_type", "reference_name"], onload: function (me) { - if (!Object.keys(frappe.route_options).length) { - frappe.route_options = { - allocated_to: frappe.session.user, - status: "Open", - }; - } me.page.set_title(__("To Do")); }, diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 499f106b5a..86dfccd47c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -4,6 +4,9 @@ """build query for doclistview and return results""" import json +from functools import lru_cache + +from sql_metadata import Parser import frappe import frappe.permissions @@ -102,7 +105,10 @@ def validate_fields(data): wildcard = update_wildcard_field_param(data) for field in list(data.fields or []): - fieldname = extract_fieldname(field) + fieldname = extract_fieldnames(field)[0] + if not fieldname: + raise_invalid_field(fieldname) + if is_standard(fieldname): continue @@ -182,23 +188,21 @@ def is_standard(fieldname): return fieldname in default_fields or fieldname in optional_fields or fieldname in child_table_fields -def extract_fieldname(field): - for text in (",", "/*", "#"): - if text in field: - raise_invalid_field(field) +@lru_cache +def extract_fieldnames(field): + from frappe.database.schema import SPECIAL_CHAR_PATTERN - fieldname = field - for sep in (" as ", " AS "): - if sep in fieldname: - fieldname = fieldname.split(sep, 1)[0] + if not SPECIAL_CHAR_PATTERN.findall(field): + return [field] - # certain functions allowed, extract the fieldname from the function - if fieldname.startswith("count(") or fieldname.startswith("sum(") or fieldname.startswith("avg("): - if not fieldname.strip().endswith(")"): - raise_invalid_field(field) - fieldname = fieldname.split("(", 1)[1][:-1] + columns = Parser(f"select {field} from _dummy").columns - return fieldname + if not columns: + f = field.lower() + if ("count(" in f or "sum(" in f or "avg(" in f) and "*" in f: + return ["*"] + + return columns def get_meta_and_docfield(fieldname, data): @@ -249,13 +253,13 @@ def get_parenttype_and_fieldname(field, data): parts = field.split(".") parenttype = parts[0] fieldname = parts[1] - if parenttype.startswith("`tab"): - # `tabChild DocType`.`fieldname` - parenttype = parenttype[4:-1] - fieldname = fieldname.strip("`") + df = frappe.get_meta(data.doctype).get_field(parenttype) + if not df and parenttype.startswith("tab"): + # tabChild DocType.fieldname + parenttype = parenttype[3:] else: # tablefield.fieldname - parenttype = frappe.get_meta(data.doctype).get_field(parenttype).options + parenttype = df.options else: parenttype = data.doctype fieldname = field.strip("`") diff --git a/frappe/locale/de.po b/frappe/locale/de.po index 9a365a29be..ad54318728 100644 --- a/frappe/locale/de.po +++ b/frappe/locale/de.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2024-03-17 09:33+0000\n" -"PO-Revision-Date: 2024-03-18 10:48\n" +"PO-Revision-Date: 2024-03-20 11:37\n" "Last-Translator: developers@frappe.io\n" "Language-Team: German\n" "MIME-Version: 1.0\n" @@ -11322,7 +11322,7 @@ msgstr "Wenn Sie die automatische Beantwortung für ein eingehendes E-Mail-Konto #: integrations/doctype/push_notification_settings/push_notification_settings.json msgctxt "Push Notification Settings" msgid "Enabling this will register your site on a central relay server to send push notifications for all installed apps through Firebase Cloud Messaging. This server only stores user tokens and error logs, and no messages are saved. " -msgstr "" +msgstr "Falls aktiviert, wird Ihre Website auf einem zentralen Relay-Server registriert, um Push-Benachrichtigungen für alle installierten Apps über Firebase Cloud Messaging zu senden. Dieser Server speichert nur Benutzer-Tokens und Fehlerprotokolle, und es werden keine Nachrichten gespeichert. " #. Description of the 'Queue in Background (BETA)' (Check) field in DocType #. 'Customize Form' @@ -19374,80 +19374,80 @@ msgstr "Zusammenführung ist nur möglich zwischen Gruppen oder Knoten" #: public/js/frappe/views/communication.js:111 www/message.html:3 #: www/message.html:25 msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text Editor field in DocType 'Activity Log' #: core/doctype/activity_log/activity_log.json msgctxt "Activity Log" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Section Break field in DocType 'Auto Email Report' #. Label of a Text Editor field in DocType 'Auto Email Report' #: email/doctype/auto_email_report/auto_email_report.json msgctxt "Auto Email Report" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text field in DocType 'Auto Repeat' #: automation/doctype/auto_repeat/auto_repeat.json msgctxt "Auto Repeat" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text Editor field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #: __init__.py:614 public/js/frappe/ui/messages.js:265 msgctxt "Default title of the message dialog" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Code field in DocType 'Email Queue' #: email/doctype/email_queue/email_queue.json msgctxt "Email Queue" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text Editor field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Section Break field in DocType 'Notification' #. Label of a Code field in DocType 'Notification' #: email/doctype/notification/notification.json msgctxt "Notification" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text Editor field in DocType 'Notification Log' #: desk/doctype/notification_log/notification_log.json msgctxt "Notification Log" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Small Text field in DocType 'SMS Log' #: core/doctype/sms_log/sms_log.json msgctxt "SMS Log" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Data field in DocType 'Success Action' #: core/doctype/success_action/success_action.json msgctxt "Success Action" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a Text field in DocType 'Workflow Document State' #: workflow/doctype/workflow_document_state/workflow_document_state.json msgctxt "Workflow Document State" msgid "Message" -msgstr "Botschaft" +msgstr "Nachricht" #. Label of a HTML Editor field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json @@ -19471,13 +19471,13 @@ msgstr "Mitteilungsbeispiele" #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Message ID" -msgstr "Message-ID" +msgstr "Nachrichten-ID" #. Label of a Small Text field in DocType 'Email Queue' #: email/doctype/email_queue/email_queue.json msgctxt "Email Queue" msgid "Message ID" -msgstr "Message-ID" +msgstr "Nachrichten-ID" #. Label of a Data field in DocType 'SMS Settings' #: core/doctype/sms_settings/sms_settings.json @@ -19513,7 +19513,7 @@ msgstr "Nachricht, die nach erfolgreichem Abschluss angezeigt werden soll" #: email/doctype/unhandled_email/unhandled_email.json msgctxt "Unhandled Email" msgid "Message-id" -msgstr "Nachrichten ID" +msgstr "Nachrichten-ID" #. Label of a Code field in DocType 'Data Import Log' #: core/doctype/data_import_log/data_import_log.json @@ -21271,12 +21271,12 @@ msgstr "Nein {0} mail" #: public/js/form_builder/utils.js:117 msgid "No." -msgstr "Nein." +msgstr "Nr." #: public/js/frappe/form/grid_row.js:252 msgctxt "Title of the 'row number' column" msgid "No." -msgstr "Nein." +msgstr "Nr." #. Label of a Check field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json @@ -36633,7 +36633,7 @@ msgstr "Ja" #: public/js/frappe/utils/user.js:33 msgctxt "Name of the current user. For example: You edited this 5 hours ago." msgid "You" -msgstr "Benutzer" +msgstr "Sie" #: public/js/frappe/form/footer/form_timeline.js:462 msgid "You Liked" @@ -37448,7 +37448,7 @@ msgstr "Kommentar" #: public/js/frappe/form/templates/timeline_message_box.html:33 msgid "commented" -msgstr "kommentierte" +msgstr "kommentierte(n)" #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' @@ -39080,7 +39080,7 @@ msgstr "{0} d" #: public/js/frappe/utils/pretty_date.js:60 msgid "{0} days ago" -msgstr "vor {0} Tag(en)" +msgstr "vor {0} Tagen" #: website/doctype/website_settings/website_settings.py:96 #: website/doctype/website_settings/website_settings.py:116 @@ -39325,11 +39325,11 @@ msgstr "{0} hat dich in einem Kommentar in {1} {2} erwähnt" #: public/js/frappe/utils/pretty_date.js:50 msgid "{0} minutes ago" -msgstr "vor {0} Minute(n)" +msgstr "vor {0} Minuten" #: public/js/frappe/utils/pretty_date.js:68 msgid "{0} months ago" -msgstr "vor {0} Monate(n)" +msgstr "vor {0} Monaten" #: model/document.py:1594 msgid "{0} must be after {1}" @@ -39508,7 +39508,7 @@ msgstr "{0} W" #: public/js/frappe/utils/pretty_date.js:64 msgid "{0} weeks ago" -msgstr "vor {0} Woche(n)" +msgstr "vor {0} Wochen" #: public/js/frappe/utils/pretty_date.js:39 msgid "{0} y" diff --git a/frappe/locale/es.po b/frappe/locale/es.po index 2321cf6ceb..592b39bc23 100644 --- a/frappe/locale/es.po +++ b/frappe/locale/es.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2024-03-17 09:33+0000\n" -"PO-Revision-Date: 2024-03-18 10:48\n" +"PO-Revision-Date: 2024-03-20 11:37\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Spanish\n" "MIME-Version: 1.0\n" @@ -1091,7 +1091,7 @@ msgstr "Acción Fallida" #: desk/doctype/onboarding_step/onboarding_step.json msgctxt "Onboarding Step" msgid "Action Label" -msgstr "" +msgstr "Etiqueta de Acción" #. Label of a Int field in DocType 'Success Action' #: core/doctype/success_action/success_action.json @@ -1392,16 +1392,16 @@ msgstr "Añadir Suscriptores" #: public/js/frappe/list/bulk_operations.js:395 msgid "Add Tags" -msgstr "" +msgstr "Añadir etiquetas" #: public/js/frappe/list/list_view.js:1897 msgctxt "Button in list view actions menu" msgid "Add Tags" -msgstr "" +msgstr "Añadir etiquetas" #: public/js/frappe/views/communication.js:399 msgid "Add Template" -msgstr "" +msgstr "Añadir plantilla" #. Label of a Check field in DocType 'Report' #: core/doctype/report/report.json @@ -1758,7 +1758,7 @@ msgstr "Alertas y Notificaciones" #: printing/doctype/letter_head/letter_head.json msgctxt "Letter Head" msgid "Align" -msgstr "" +msgstr "Alinear" #. Label of a Check field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json @@ -2206,7 +2206,7 @@ msgstr "Permitido" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Allowed File Extensions" -msgstr "" +msgstr "Extensiones de Archivos permitidas" #. Label of a Check field in DocType 'User' #: core/doctype/user/user.json @@ -3387,25 +3387,25 @@ msgstr "" #: custom/doctype/custom_field/custom_field.json msgctxt "Custom Field" msgid "Autocomplete" -msgstr "" +msgstr "Autocompletar" #. 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 "Autocomplete" -msgstr "" +msgstr "Autocompletar" #. Option for the 'Type' (Select) field in DocType 'DocField' #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Autocomplete" -msgstr "" +msgstr "Autocompletar" #. Option for the 'Naming Rule' (Select) field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Autoincrement" -msgstr "" +msgstr "Autoincremento" #. Option for the 'Communication Type' (Select) field in DocType #. 'Communication' @@ -4442,7 +4442,7 @@ msgstr "CC" #: core/doctype/recorder/recorder.json msgctxt "Recorder" msgid "CMD" -msgstr "" +msgstr "CMD" #: public/js/frappe/color_picker/color_picker.js:20 msgid "COLOR PICKER" @@ -4797,15 +4797,15 @@ msgstr "No se puede cancelar antes de enviar. Ver transmisión {0}" #: public/js/frappe/list/bulk_operations.js:264 msgid "Cannot cancel {0}." -msgstr "" +msgstr "No se puede cancelar {0}." #: model/document.py:848 msgid "Cannot change docstatus from 0 (Draft) to 2 (Cancelled)" -msgstr "" +msgstr "No se puede cambiar el estado del documento de 0 (Borrador) a 2 (Cancelado)" #: model/document.py:862 msgid "Cannot change docstatus from 1 (Submitted) to 0 (Draft)" -msgstr "" +msgstr "No se puede cambiar el estado del documento de 1 (Enviado) a 0 (Borrador)" #: public/js/workflow_builder/utils.js:170 msgid "Cannot change state of Cancelled Document ({0} State)" @@ -4873,7 +4873,7 @@ msgstr "No se puede eliminar {0}, ya que contiene sub-grupos" #: desk/doctype/dashboard/dashboard.py:48 msgid "Cannot edit Standard Dashboards" -msgstr "" +msgstr "No se pueden editar los Tableros estándar" #: email/doctype/notification/notification.py:121 msgid "Cannot edit Standard Notification. To edit, please disable this and duplicate it" @@ -4949,7 +4949,7 @@ msgstr "" #: public/js/frappe/list/bulk_operations.js:261 msgid "Cannot submit {0}." -msgstr "" +msgstr "No se puede enviar {0}." #: desk/doctype/workspace/workspace.py:345 msgid "Cannot update private workspace of other users" @@ -5187,7 +5187,7 @@ msgstr "Gráficos" #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Chat" -msgstr "" +msgstr "Chat" #. Option for the 'Field Type' (Select) field in DocType 'Custom Field' #: custom/doctype/custom_field/custom_field.json @@ -5237,7 +5237,7 @@ msgstr "Verificar URL de Solicitud" #: email/doctype/newsletter/newsletter.js:18 msgid "Check broken links" -msgstr "" +msgstr "Comprobar enlaces rotos" #: printing/page/print_format_builder/print_format_builder_column_selector.html:1 msgid "Check columns to select, drag to set order." @@ -5260,7 +5260,7 @@ msgstr "Seleccione esta opción si desea obligar al usuario a seleccionar una se #: email/doctype/newsletter/newsletter.js:20 msgid "Checking broken links..." -msgstr "" +msgstr "Comprobando enlaces rotos..." #: public/js/frappe/desk.js:214 msgid "Checking one moment" @@ -5325,11 +5325,11 @@ msgstr "" #: public/js/frappe/form/controls/color.js:5 msgid "Choose a color" -msgstr "" +msgstr "Elegir color" #: public/js/frappe/form/controls/icon.js:5 msgid "Choose an icon" -msgstr "" +msgstr "Elegir icono" #. Description of the 'Two Factor Authentication method' (Select) field in #. DocType 'System Settings' @@ -5461,7 +5461,7 @@ msgstr "Haga clic para establecer Filtros Dinámicos" #: desk/doctype/number_card/number_card.js:270 #: website/doctype/web_form/web_form.js:252 msgid "Click to Set Filters" -msgstr "" +msgstr "Clic para establecer filtros" #: public/js/frappe/list/list_view.js:678 msgid "Click to sort by {0}" @@ -6395,25 +6395,25 @@ msgstr "Conexión perdida. Algunas características pueden no funcionar." #: public/js/frappe/form/dashboard.js:54 msgid "Connections" -msgstr "" +msgstr "Conexiones" #. Label of a Tab Break field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Connections" -msgstr "" +msgstr "Conexiones" #. Label of a Tab Break field in DocType 'Module Def' #: core/doctype/module_def/module_def.json msgctxt "Module Def" msgid "Connections" -msgstr "" +msgstr "Conexiones" #. Label of a Tab Break field in DocType 'User' #: core/doctype/user/user.json msgctxt "User" msgid "Connections" -msgstr "" +msgstr "Conexiones" #. Label of a Code field in DocType 'System Console' #: desk/doctype/system_console/system_console.json @@ -6838,7 +6838,7 @@ msgstr "Crear contactos a partir de correos electrónicos entrantes" #. Title of an Onboarding Step #: custom/onboarding_step/custom_field/custom_field.json msgid "Create Custom Fields" -msgstr "" +msgstr "Crear Campos Personalizados" #: public/js/frappe/views/workspace/workspace.js:931 msgid "Create Duplicate" @@ -6947,7 +6947,7 @@ msgstr "Creado" #: core/doctype/submission_queue/submission_queue.json msgctxt "Submission Queue" msgid "Created At" -msgstr "" +msgstr "Creado el" #: model/meta.py:51 public/js/frappe/list/list_sidebar_group_by.js:73 #: public/js/frappe/model/meta.js:203 public/js/frappe/model/model.js:113 @@ -7196,14 +7196,14 @@ msgstr "URL Base Personalizada" #: desk/doctype/workspace_custom_block/workspace_custom_block.json msgctxt "Workspace Custom Block" msgid "Custom Block Name" -msgstr "" +msgstr "Nombre de bloque personalizado" #. 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 "Custom Blocks" -msgstr "" +msgstr "Bloques personalizados" #. Label of a Code field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json @@ -7317,7 +7317,7 @@ msgstr "HTML Personalizado" #. Name of a DocType #: desk/doctype/custom_html_block/custom_html_block.json msgid "Custom HTML Block" -msgstr "" +msgstr "Bloque HTML personalizado" #. Label of a HTML field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json @@ -7390,7 +7390,7 @@ msgstr "Menú Lateral Personalizado" #: core/workspace/build/build.json msgctxt "Translation" msgid "Custom Translation" -msgstr "" +msgstr "Traducción personalizada" #: custom/doctype/custom_field/custom_field.py:373 msgid "Custom field renamed to {0} successfully." @@ -7472,7 +7472,7 @@ msgstr "" #: public/js/frappe/views/dashboard/dashboard_view.js:37 msgid "Customize Dashboard" -msgstr "" +msgstr "Personalizar el Tablero" #. Name of a DocType #: automation/doctype/auto_repeat/auto_repeat.js:33 @@ -8180,14 +8180,14 @@ msgstr "Encabezado Predeterminado" #: core/doctype/amended_document_naming_settings/amended_document_naming_settings.json msgctxt "Amended Document Naming Settings" msgid "Default Naming" -msgstr "" +msgstr "Nomenclatura por defecto" #. Option for the 'Default Amendment Naming' (Select) field in DocType #. 'Document Naming Settings' #: core/doctype/document_naming_settings/document_naming_settings.json msgctxt "Document Naming Settings" msgid "Default Naming" -msgstr "" +msgstr "Nomenclatura por defecto" #: email/doctype/email_account/email_account.py:209 msgid "Default Outgoing" @@ -8293,13 +8293,13 @@ msgstr "Valor predeterminado" #: custom/doctype/customize_form/customize_form.json msgctxt "Customize Form" msgid "Default View" -msgstr "" +msgstr "Vista predeterminada" #. Label of a Select field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Default View" -msgstr "" +msgstr "Vista predeterminada" #: core/doctype/doctype/doctype.py:1325 msgid "Default for 'Check' type of field {0} must be either '0' or '1'" @@ -8506,13 +8506,13 @@ msgstr "Estado del envío" #: templates/includes/oauth_confirmation.html:14 msgid "Deny" -msgstr "" +msgstr "Denegar" #. Option for the 'Sign ups' (Select) field in DocType 'Social Login Key' #: integrations/doctype/social_login_key/social_login_key.json msgctxt "Social Login Key" msgid "Deny" -msgstr "" +msgstr "Denegar" #. Label of a Data field in DocType 'Contact' #: contacts/doctype/contact/contact.json @@ -8800,7 +8800,7 @@ msgstr "Deshabilitar la notificación del Registro de Cambios" #: desk/doctype/list_view_settings/list_view_settings.json msgctxt "List View Settings" msgid "Disable Comment Count" -msgstr "" +msgstr "Desactivar recuento de comentarios" #. Label of a Check field in DocType 'Blog Post' #: website/doctype/blog_post/blog_post.json @@ -9013,7 +9013,7 @@ msgstr "Mostrar Depende de" #: core/doctype/docfield/docfield.json msgctxt "DocField" msgid "Display Depends On (JS)" -msgstr "" +msgstr "La visualización depende de (JS)" #. Label of a Check field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -10011,7 +10011,7 @@ msgstr "Duplicar" #: printing/doctype/print_format_field_template/print_format_field_template.py:53 msgid "Duplicate Entry" -msgstr "" +msgstr "Entrada duplicada" #: public/js/frappe/list/list_filter.js:137 msgid "Duplicate Filter Name" @@ -10028,7 +10028,7 @@ msgstr "Duplicar Área de Trabajo" #: public/js/frappe/form/form.js:208 msgid "Duplicate current row" -msgstr "" +msgstr "Duplicar fila actual" #: public/js/frappe/views/workspace/workspace.js:996 msgid "Duplicate of {0} named as {1} is created successfully" @@ -10242,7 +10242,7 @@ msgstr "Editar formato" #: public/js/frappe/form/quick_entry.js:280 msgid "Edit Full Form" -msgstr "" +msgstr "Editar formulario completo" #: printing/page/print_format_builder/print_format_builder_field.html:26 msgid "Edit HTML" @@ -10313,7 +10313,7 @@ msgstr "Editar Área de Trabajo" #: desk/doctype/note/note.js:11 msgid "Edit mode" -msgstr "" +msgstr "Modo edición" #: printing/page/print_format_builder/print_format_builder.js:713 msgid "Edit to add content" @@ -10700,7 +10700,7 @@ msgstr "Correo Electrónico Enviado" #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" msgid "Email Sent At" -msgstr "" +msgstr "Correo enviado el" #. Label of a Section Break field in DocType 'Auto Email Report' #: email/doctype/auto_email_report/auto_email_report.json @@ -10879,7 +10879,7 @@ msgstr "Habilite la API de Google en la configuración de Google." #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" msgid "Enable Google indexing" -msgstr "" +msgstr "Habilitar la indexación de Google" #: email/doctype/email_account/email_account.py:202 msgid "Enable Incoming" @@ -11128,14 +11128,14 @@ msgstr "Ejecución programada habilitada para la secuencia de comandos {0}" #: custom/doctype/customize_form/customize_form.json msgctxt "Customize Form" msgid "Enables Calendar and Gantt views." -msgstr "" +msgstr "Habilita las vistas Calendario y Gantt." #. Description of the 'Is Calendar and Gantt' (Check) field in DocType #. 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" msgid "Enables Calendar and Gantt views." -msgstr "" +msgstr "Habilita las vistas Calendario y Gantt." #: email/doctype/email_account/email_account.js:232 msgid "Enabling auto reply on an incoming email account will send automated replies to all the synchronized emails. Do you wish to continue?" @@ -11374,61 +11374,61 @@ msgstr "Iguales" #: desk/page/backups/backups.js:35 model/base_document.py:723 #: model/base_document.py:729 public/js/frappe/ui/messages.js:22 msgid "Error" -msgstr "" +msgstr "Error" #. Option for the 'Delivery Status' (Select) field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" msgid "Error" -msgstr "" +msgstr "Error" #. Option for the 'Status' (Select) field in DocType 'Data Import' #: core/doctype/data_import/data_import.json msgctxt "Data Import" msgid "Error" -msgstr "" +msgstr "Error" #. Option for the 'Status' (Select) field in DocType 'Email Queue' #. Label of a Code field in DocType 'Email Queue' #: email/doctype/email_queue/email_queue.json msgctxt "Email Queue" msgid "Error" -msgstr "" +msgstr "Error" #. Label of a Code field in DocType 'Email Queue Recipient' #: email/doctype/email_queue_recipient/email_queue_recipient.json msgctxt "Email Queue Recipient" msgid "Error" -msgstr "" +msgstr "Error" #. Label of a Code field in DocType 'Error Log' #: core/doctype/error_log/error_log.json msgctxt "Error Log" msgid "Error" -msgstr "" +msgstr "Error" #. Label of a Code field in DocType 'Integration Request' #: integrations/doctype/integration_request/integration_request.json msgctxt "Integration Request" msgid "Error" -msgstr "" +msgstr "Error" #. Option for the 'Status' (Select) field in DocType 'Prepared Report' #: core/doctype/prepared_report/prepared_report.json msgctxt "Prepared Report" msgid "Error" -msgstr "" +msgstr "Error" #: public/js/frappe/web_form/web_form.js:240 msgctxt "Title of error message in web form" msgid "Error" -msgstr "" +msgstr "Error" #. Label of a Text field in DocType 'Webhook Request Log' #: integrations/doctype/webhook_request_log/webhook_request_log.json msgctxt "Webhook Request Log" msgid "Error" -msgstr "" +msgstr "Error" #: www/error.html:34 msgid "Error Code: {0}" @@ -11443,7 +11443,7 @@ msgstr "Registro de Errores" #: core/workspace/build/build.json msgctxt "Error Log" msgid "Error Logs" -msgstr "" +msgstr "Registros de errores" #. Label of a Text field in DocType 'Prepared Report' #: core/doctype/prepared_report/prepared_report.json @@ -11457,11 +11457,11 @@ msgstr "Error al conectarse a la aplicación QZ Tray...

Debe tener la ap #: email/doctype/email_domain/email_domain.py:32 msgid "Error connecting via IMAP/POP3: {e}" -msgstr "" +msgstr "Error al conectar vía IMAP/POP3: {e}" #: email/doctype/email_domain/email_domain.py:33 msgid "Error connecting via SMTP: {e}" -msgstr "" +msgstr "Error de conexión vía SMTP: {e}" #: email/doctype/email_domain/email_domain.py:100 msgid "Error has occurred in {0}" @@ -11648,7 +11648,7 @@ msgstr "Excel" #: public/js/frappe/form/controls/password.js:91 msgid "Excellent" -msgstr "" +msgstr "Excelente" #. Label of a Text field in DocType 'Data Import Log' #: core/doctype/data_import_log/data_import_log.json @@ -13265,7 +13265,7 @@ msgstr "Forzar al usuario a restablecer la contraseña" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Force Web Capture Mode for Uploads" -msgstr "" +msgstr "Forzar el modo de captura web para las cargas" #: www/login.html:35 msgid "Forgot Password?" @@ -13844,7 +13844,7 @@ msgstr "Regresa" #: desk/doctype/notification_settings/notification_settings.js:17 msgid "Go to Notification Settings List" -msgstr "" +msgstr "Ir a la Lista de Ajustes de Notificaciones" #. Option for the 'Action' (Select) field in DocType 'Onboarding Step' #: desk/doctype/onboarding_step/onboarding_step.json @@ -18704,7 +18704,7 @@ msgstr "" #: public/js/frappe/ui/notifications/notifications.js:308 msgid "Looks like you haven’t received any notifications." -msgstr "" +msgstr "Parece que no has recibido ninguna notificación." #: public/js/frappe/form/sidebar/assign_to.js:190 msgid "Low" @@ -20832,7 +20832,7 @@ msgstr "Sin nombre especificado para {0}" #: public/js/frappe/ui/notifications/notifications.js:308 msgid "No New notifications" -msgstr "" +msgstr "No hay nuevas notificaciones" #: core/doctype/doctype/doctype.py:1682 msgid "No Permissions Specified" @@ -21000,7 +21000,7 @@ msgstr "No hay nuevos contactos de Google sincronizados." #: public/js/frappe/ui/toolbar/navbar.html:46 msgid "No new notifications" -msgstr "" +msgstr "No hay nuevas notificaciones" #: printing/page/print_format_builder/print_format_builder.js:415 msgid "No of Columns" @@ -26559,7 +26559,7 @@ msgstr "Restablecer gráfico" #: public/js/frappe/views/dashboard/dashboard_view.js:38 msgid "Reset Dashboard Customizations" -msgstr "" +msgstr "Restablecer personalizaciones del Tablero" #: public/js/frappe/list/list_settings.js:227 msgid "Reset Fields" @@ -26598,7 +26598,7 @@ msgstr "" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Reset Password Template" -msgstr "" +msgstr "Plantilla para Restablecer Contraseña" #: core/page/permission_manager/permission_manager.js:109 msgid "Reset Permissions for {0}?" @@ -27688,7 +27688,7 @@ msgstr "Guardar como" #: public/js/frappe/views/dashboard/dashboard_view.js:62 msgid "Save Customizations" -msgstr "" +msgstr "Guardar Personalización" #: public/js/frappe/list/list_sidebar.html:73 msgid "Save Filter" @@ -29067,7 +29067,7 @@ msgstr "Sesión expirada" #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Session Expiry (idle timeout)" -msgstr "" +msgstr "Expiración de la sesión (tiempo de inactivad)" #: core/doctype/system_settings/system_settings.py:111 msgid "Session Expiry must be in format {0}" diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 18c3d2782c..f1a36d79b2 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -44,7 +44,6 @@ FIELD_COMMA_PATTERN = re.compile(r"[0-9a-zA-Z]+\s*,") STRICT_FIELD_PATTERN = re.compile(r".*/\*.*") STRICT_UNION_PATTERN = re.compile(r".*\s(union).*\s") ORDER_GROUP_PATTERN = re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*") -FN_PARAMS_PATTERN = re.compile(r".*?\((.*)\).*") SPECIAL_FIELD_CHARS = frozenset(("(", "`", ".", "'", '"', "*")) @@ -619,6 +618,8 @@ class DatabaseQuery: - Query: fields=["*"] - Result: fields=["title", ...] // will also include Frappe's meta field like `name`, `owner`, etc. """ + from frappe.desk.reportview import extract_fieldnames + if self.flags.ignore_permissions: return @@ -631,23 +632,18 @@ class DatabaseQuery: ) for i, field in enumerate(self.fields): - if "distinct" in field.lower(): - # field: 'count(distinct `tabPhoto`.name) as total_count' - # column: 'tabPhoto.name' - if _fn := FN_PARAMS_PATTERN.findall(field): - column = _fn[0].replace("distinct ", "").replace("DISTINCT ", "").replace("`", "") - # field: 'distinct name' - # column: 'name' - else: - column = field.split(" ", 2)[1].replace("`", "") - else: - # field: 'count(`tabPhoto`.name) as total_count' - # column: 'tabPhoto.name' - column = field.split("(")[-1].split(")", 1)[0] - column = strip_alias(column).replace("`", "") + # field: 'count(distinct `tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + # field: 'count(`tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + columns = extract_fieldnames(field) + if not columns: + continue - if column == "*" and not in_function("*", field): - asterisk_fields.append(i) + column = columns[0] + if column == "*" and "*" in field: + if not in_function("*", field): + asterisk_fields.append(i) continue # handle pseudo columns @@ -686,21 +682,12 @@ class DatabaseQuery: elif "(" in field: if "*" in field: continue - elif _params := FN_PARAMS_PATTERN.findall(field): - params = (x.strip() for x in _params[0].split(",")) - for param in params: - if not ( - not param - or param in permitted_fields - or param.isnumeric() - or "'" in param - or '"' in param - ): + else: + for column in columns: + if column not in permitted_fields: self.remove_field(i) break continue - self.remove_field(i) - # remove if access not allowed else: self.remove_field(i) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 650ccb01ec..735f8097b5 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -351,7 +351,12 @@ frappe.views.Workspace = class Workspace { get_page_to_show() { let default_page; - if ( + if (frappe.boot.user.default_workspace) { + default_page = { + name: frappe.boot.user.default_workspace.name, + public: frappe.boot.user.default_workspace.public, + }; + } else if ( localStorage.current_page && this.all_pages.filter((page) => page.title == localStorage.current_page).length != 0 ) { diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 8cd233431a..51df9d8983 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -1173,6 +1173,11 @@ class TestDBQuery(FrappeTestCase): data = get() self.assertEqual(len(data["values"]), 1) + def test_select_star_expansion(self): + count = frappe.get_list("Language", ["SUM(1)", "COUNT(*)"], as_list=1, order_by=None)[0] + self.assertEqual(count[0], frappe.db.count("Language")) + self.assertEqual(count[1], frappe.db.count("Language")) + class TestReportView(FrappeTestCase): @run_only_if(db_type_is.MARIADB) # TODO: postgres name casting is messed up diff --git a/frappe/tests/test_reportview.py b/frappe/tests/test_reportview.py index 4a24514acf..5224ca9414 100644 --- a/frappe/tests/test_reportview.py +++ b/frappe/tests/test_reportview.py @@ -2,7 +2,7 @@ # License: MIT. See LICENSE import frappe -from frappe.desk.reportview import export_query +from frappe.desk.reportview import export_query, extract_fieldnames from frappe.tests.utils import FrappeTestCase @@ -32,3 +32,58 @@ class TestReportview(FrappeTestCase): for row in reader: self.assertEqual(int(row["Is Single"]), 1) self.assertEqual(row["Module"], "Core") + + def test_extract_fieldname(self): + self.assertEqual( + extract_fieldnames("count(distinct `tabPhoto`.name) as total_count")[0], "tabPhoto.name" + ) + + self.assertEqual(extract_fieldnames("owner")[0], "owner") + self.assertEqual(extract_fieldnames("from")[0], "from") + + self.assertEqual(extract_fieldnames("module")[0], "module") + + self.assertEqual(extract_fieldnames("count(`tabPhoto`.name) as total_count")[0], "tabPhoto.name") + + self.assertEqual(extract_fieldnames("count(distinct `tabPhoto`.name)")[0], "tabPhoto.name") + + self.assertEqual(extract_fieldnames("count(`tabPhoto`.name)")[0], "tabPhoto.name") + + self.assertEqual( + extract_fieldnames("count(distinct `tabJob Applicant`.name) as total_count")[0], + "tabJob Applicant.name", + ) + + self.assertEqual( + extract_fieldnames("(1 / nullif(locate('a', `tabAddress`.`name`), 0)) as `_relevance`")[0], + "tabAddress.name", + ) + + self.assertEqual( + extract_fieldnames("(1 / nullif(locate('(a)', `tabAddress`.`name`), 0)) as `_relevance`")[0], + "tabAddress.name", + ) + + self.assertEqual(extract_fieldnames("EXTRACT(MONTH FROM date_column) AS month")[0], "date_column") + + self.assertEqual(extract_fieldnames("COUNT(*) AS count")[0], "*") + + self.assertEqual( + extract_fieldnames("first_name + ' ' + last_name AS full_name"), ["first_name", "last_name"] + ) + + self.assertEqual( + extract_fieldnames("CONCAT(first_name, ' ', last_name) AS full_name"), + ["first_name", "last_name"], + ) + + self.assertEqual( + extract_fieldnames("CONCAT(id, '/', name, '/', age, '/', marks) AS student"), + ["id", "name", "age", "marks"], + ) + + self.assertEqual(extract_fieldnames("tablefield.fiedname")[0], "tablefield.fiedname") + + self.assertEqual(extract_fieldnames("`tabChild DocType`.`fiedname`")[0], "tabChild DocType.fiedname") + + self.assertEqual(extract_fieldnames("sum(1)"), []) diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index 7f54d1f9c0..eefe59f9bc 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -176,6 +176,11 @@ class TestSearch(FrappeTestCase): frappe.db.set_value("Language", "es", "idx", 10) self.assertEqual("es", search(txt="es")[0]["value"]) + def test_search_with_paren(self): + search = partial(search_link, doctype="Language", filters=None, page_length=10) + result = search(txt="(txt)") + self.assertEqual(result, []) + @frappe.validate_and_sanitize_search_inputs def get_data(doctype, txt, searchfield, start, page_len, filters): diff --git a/frappe/tests/utils.py b/frappe/tests/utils.py index cf49ec7271..f9d8ee54b5 100644 --- a/frappe/tests/utils.py +++ b/frappe/tests/utils.py @@ -28,7 +28,7 @@ class FrappeTestCase(unittest.TestCase): TEST_SITE = "test_site" SHOW_TRANSACTION_COMMIT_WARNINGS = False - maxDiff = None # prints long diffs but useful in CI + maxDiff = 10_000 # prints long diffs but useful in CI @classmethod def setUpClass(cls) -> None: diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 027c5dbaf5..8069f22497 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -226,6 +226,7 @@ class UserPermissions: "send_me_a_copy", "user_type", "onboarding_status", + "default_workspace", ], as_dict=True, ) @@ -233,6 +234,10 @@ class UserPermissions: if not self.can_read: self.build_permissions() + if d.get("default_workspace"): + public = frappe.get_cached_value("Workspace", d.default_workspace, "public") + d.default_workspace = {"name": d.default_workspace, "public": public} + d.name = self.name d.onboarding_status = frappe.parse_json(d.onboarding_status) d.roles = self.get_roles() diff --git a/frappe/www/message.html b/frappe/www/message.html index 9039e13186..244675d9f5 100644 --- a/frappe/www/message.html +++ b/frappe/www/message.html @@ -42,7 +42,7 @@ html, body {