diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py
index 3ddc070fc5..c356da366f 100644
--- a/frappe/core/doctype/data_import/importer.py
+++ b/frappe/core/doctype/data_import/importer.py
@@ -13,6 +13,7 @@ from frappe.core.doctype.version.version import get_diff
from frappe.model import no_value_fields
from frappe.utils import cint, cstr, duration_to_seconds, flt, update_progress_bar
from frappe.utils.csvutils import get_csv_content_from_google_sheets, read_csv_content
+from frappe.utils.data import escape_html
from frappe.utils.xlsxutils import (
read_xls_file_from_attached_file,
read_xlsx_file_from_attached_file,
@@ -727,7 +728,9 @@ class Row:
elif df.fieldtype == "Link":
exists = self.link_exists(value, df)
if not exists:
- msg = _("Value {0} missing for {1}").format(frappe.bold(value), frappe.bold(df.options))
+ msg = _("Value {0} missing for {1}").format(
+ frappe.bold(escape_html(cstr(value))), frappe.bold(df.options)
+ )
self.warnings.append(
{
"row": self.row_number,
@@ -746,7 +749,8 @@ class Row:
"col": col.column_number,
"field": df_as_json(df),
"message": _("Value {0} must in {1} format").format(
- frappe.bold(value), frappe.bold(get_user_format(col.date_format))
+ frappe.bold(escape_html(cstr(value))),
+ frappe.bold(get_user_format(col.date_format)),
),
}
)
@@ -761,7 +765,8 @@ class Row:
"col": col.column_number,
"field": df_as_json(df),
"message": _("Value {0} must in {1} format").format(
- frappe.bold(value), frappe.bold(get_user_format(col.date_format))
+ frappe.bold(escape_html(cstr(value))),
+ frappe.bold(get_user_format(col.date_format)),
),
}
)
@@ -774,7 +779,7 @@ class Row:
"col": col.column_number,
"field": df_as_json(df),
"message": _("Value {0} must be in the valid duration format: d h m s").format(
- frappe.bold(value)
+ frappe.bold(escape_html(cstr(value)))
),
}
)
@@ -1045,7 +1050,7 @@ class Column:
]
not_exists = list(set(values) - set(exists))
if not_exists:
- missing_values = ", ".join(not_exists)
+ missing_values = ", ".join(escape_html(v) for v in not_exists)
message = _("The following values do not exist for {0}: {1}")
self.warnings.append(
{
@@ -1088,7 +1093,7 @@ class Column:
invalid = values - set(options)
if invalid:
valid_values = ", ".join(frappe.bold(o) for o in options)
- invalid_values = ", ".join(frappe.bold(i) for i in invalid)
+ invalid_values = ", ".join(frappe.bold(escape_html(i)) for i in invalid)
message = _("The following values are invalid: {0}. Values must be one of {1}")
self.warnings.append(
{
diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py
index 7ea6f2f039..68e4afc37f 100644
--- a/frappe/core/doctype/version/version.py
+++ b/frappe/core/doctype/version/version.py
@@ -199,6 +199,7 @@ def get_diff(old, new, for_child=False, compare_cancelled=False):
field_meta.options,
{"name": ("in", (old_value, new_value))},
["name", title_field],
+ ignore_ifnull=True,
)
for r in result:
if r[0] == old_value:
diff --git a/frappe/database/schema.py b/frappe/database/schema.py
index 9bd0a1e45b..d29bff1615 100644
--- a/frappe/database/schema.py
+++ b/frappe/database/schema.py
@@ -5,7 +5,7 @@ from frappe import _
from frappe.utils import cint, cstr, flt
from frappe.utils.defaults import get_not_null_defaults
-# This matches anything that isn't [a-zA-Z0-9_]
+# This matches anything that isn't Unicode Word Characters, Numbers and Underscore.
SPECIAL_CHAR_PATTERN = re.compile(r"[\W]", flags=re.UNICODE)
VARCHAR_CAST_PATTERN = re.compile(r"varchar\(([\d]+)\)")
diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py
index 6e0041e2b2..4a88211ef8 100644
--- a/frappe/email/doctype/email_queue/email_queue.py
+++ b/frappe/email/doctype/email_queue/email_queue.py
@@ -188,16 +188,18 @@ class EmailQueue(Document):
if ctx.smtp_server.session.has_extn("SIZE"):
if max_size := ctx.smtp_server.session.esmtp_features.get("size"):
max_size = int(max_size)
- msg_size = len(msg)
- if msg_size > max_size:
- msg_size_mb = msg_size / (1024 * 1024)
- max_size_mb = max_size / (1024 * 1024)
- frappe.throw(
- _(
- "Email size {0:.2f} MB exceeds the maximum allowed size of {1:.2f} MB"
- ).format(msg_size_mb, max_size_mb)
- )
+ if max_size > 0:
+ msg_size = len(msg)
+
+ if msg_size > max_size:
+ msg_size_mb = msg_size / (1024 * 1024)
+ max_size_mb = max_size / (1024 * 1024)
+ frappe.throw(
+ _(
+ "Email size {0:.2f} MB exceeds the maximum allowed size of {1:.2f} MB"
+ ).format(msg_size_mb, max_size_mb)
+ )
return msg
diff --git a/frappe/locale/bs.po b/frappe/locale/bs.po
index d301c4afd0..f88053f5bd 100644
--- a/frappe/locale/bs.po
+++ b/frappe/locale/bs.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:45\n"
+"PO-Revision-Date: 2026-04-06 15:04\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Bosnian\n"
"MIME-Version: 1.0\n"
@@ -9692,7 +9692,7 @@ msgstr "Ovdje unesi statičke parametre URL-a (npr. pošiljatelj=ERPNext, korisn
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Unesite naziv polja za valutu ili keširanu vrijednost (npr. Company:company:default_currency)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23504,7 +23504,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "SQL Uslovi. Primjer: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27373,7 +27373,7 @@ msgstr "Automatsko Ponavljanje za ovaj dokument je onemogućeno."
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Masovno ažuriranje nije moglo da se desi zbog {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/locale/hr.po b/frappe/locale/hr.po
index 82b6eb8bb1..55f61d2754 100644
--- a/frappe/locale/hr.po
+++ b/frappe/locale/hr.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:45\n"
+"PO-Revision-Date: 2026-04-06 15:04\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Croatian\n"
"MIME-Version: 1.0\n"
@@ -9692,7 +9692,7 @@ msgstr "Ovdje unesi statičke parametre URL-a (npr. pošiljatelj=ERPNext, korisn
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Unesite naziv polja za valutu ili predmemoriranu vrijednost (npr. Company:company:default_valute)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23504,7 +23504,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "SQL Uvjeti. Primjer: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27373,7 +27373,7 @@ msgstr "Automatsko Ponavljanje za ovaj dokument je onemogućeno."
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Masovno ažuriranje nije se moglo dogoditi zbog {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/locale/ru.po b/frappe/locale/ru.po
index d6248b4bad..0dc8b1e076 100644
--- a/frappe/locale/ru.po
+++ b/frappe/locale/ru.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:45\n"
+"PO-Revision-Date: 2026-04-06 15:03\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Russian\n"
"MIME-Version: 1.0\n"
@@ -9694,7 +9694,7 @@ msgstr "Введите здесь статические параметры url
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Введите имя поля валюты или кэшированное значение (например, Company:company:default_currency)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23506,7 +23506,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "Условия SQL. Пример: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27375,7 +27375,7 @@ msgstr "Автоповтор для этого документа был отк
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Массовое обновление не удалось выполнить из-за {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/locale/sr.po b/frappe/locale/sr.po
index 8eef42db72..42632c259d 100644
--- a/frappe/locale/sr.po
+++ b/frappe/locale/sr.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:45\n"
+"PO-Revision-Date: 2026-04-06 15:03\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Serbian (Cyrillic)\n"
"MIME-Version: 1.0\n"
@@ -9690,7 +9690,7 @@ msgstr "Унесите статичке URL параметре (нпр. sender=E
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Унесите назив поља за валуту или кеширану вредност (нпр. Company:company:default_currency)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23502,7 +23502,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "SQL услови. Пример: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27371,7 +27371,7 @@ msgstr "Аутоматско понављање за овај документ
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Масовно ажурирање није могло бити извршено због {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/locale/sr_CS.po b/frappe/locale/sr_CS.po
index f8ca732b70..e926d3c8c8 100644
--- a/frappe/locale/sr_CS.po
+++ b/frappe/locale/sr_CS.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:46\n"
+"PO-Revision-Date: 2026-04-06 15:04\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Serbian (Latin)\n"
"MIME-Version: 1.0\n"
@@ -9691,7 +9691,7 @@ msgstr "Unesite statičke URL parametre (npr. sender=ERPNext, username=ERPNext,
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Unesite naziv polja za valutu ili keširanu vrednost (npr. Company:company:default_currency)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23503,7 +23503,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "SQL uslovi. Primer: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27372,7 +27372,7 @@ msgstr "Automatsko ponavljanje za ovaj dokument je onemogućeno."
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Masovno ažuriranje nije moglo biti izvršeno zbog {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/locale/sv.po b/frappe/locale/sv.po
index 1b4eff3cbe..c3dc0e8bf2 100644
--- a/frappe/locale/sv.po
+++ b/frappe/locale/sv.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-04-05 09:44+0000\n"
-"PO-Revision-Date: 2026-04-05 14:45\n"
+"PO-Revision-Date: 2026-04-06 15:03\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Swedish\n"
"MIME-Version: 1.0\n"
@@ -9691,7 +9691,7 @@ msgstr "Ange statiska url parametrar här (T.ex.. Avsändare = System, användar
#: frappe/public/js/form_builder/components/FieldProperties.vue:66
msgid "Enter the fieldname of the currency field or a cached value (e.g. Company:company:default_currency)."
-msgstr ""
+msgstr "Ange fältnamn för valutafält eller cachat värde (t.ex. Company:company:default_currency)."
#. Description of the 'Message Parameter' (Data) field in DocType 'SMS
#. Settings'
@@ -23503,7 +23503,7 @@ msgstr "SQL"
#. Description of the 'Condition' (Small Text) field in DocType 'Bulk Update'
#: frappe/desk/doctype/bulk_update/bulk_update.json
msgid "SQL Conditions. Example: {\"status\" : \"open\", \"priority\" : \"medium\"}"
-msgstr ""
+msgstr "SQL Villkor. Exempel: {\"status\" : \"open\", \"priority\" : \"medium\"}"
#. Label of the sql_explain_html (HTML) field in DocType 'Recorder Query'
#: frappe/core/doctype/recorder/recorder.js:85
@@ -27372,7 +27372,7 @@ msgstr "Återkommande för detta dokument är inaktiverad."
#: frappe/desk/doctype/bulk_update/bulk_update.py:43
msgid "The Bulk Update could not happen due to {0}"
-msgstr ""
+msgstr "Mass uppdatering kunde inte ske på grund av {0}"
#: frappe/public/js/frappe/form/grid.js:1310
msgid "The CSV format is case sensitive"
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index e27968fac2..1aea6a02a9 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -380,15 +380,17 @@ frappe.Application = class Application {
logout() {
var me = this;
me.logged_out = true;
- return frappe.call({
- method: "logout",
- callback: function (r) {
- if (r.exc) {
- return;
- }
+ frappe.confirm(__("Are you sure you want to log out?"), function () {
+ return frappe.call({
+ method: "logout",
+ callback: function (r) {
+ if (r.exc) {
+ return;
+ }
- me.redirect_to_login();
- },
+ me.redirect_to_login();
+ },
+ });
});
}
handle_session_expired() {
diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js
index 12f7d68503..d0e30386df 100644
--- a/frappe/public/js/frappe/form/controls/link.js
+++ b/frappe/public/js/frappe/form/controls/link.js
@@ -242,7 +242,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
) {
html +=
'
' +
- __(frappe.utils.escape_html(frappe.utils.html2text(d.description))) +
+ __(frappe.utils.html2text(frappe.utils.escape_html(d.description))) +
"";
}
return $(`