Merge branch 'develop' into refactor-like-unlike

This commit is contained in:
barredterra 2024-03-25 21:31:39 +01:00
commit 21780d2d2e
21 changed files with 114 additions and 35 deletions

View file

@ -186,6 +186,7 @@
},
{
"default": "0",
"depends_on": "eval:!in_list(['Tab Break', 'Table'], doc.fieldtype)",
"fieldname": "allow_in_quick_entry",
"fieldtype": "Check",
"label": "Allow in Quick Entry"
@ -581,7 +582,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-02-01 15:55:44.007917",
"modified": "2024-03-21 17:35:32.602933",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",
@ -591,4 +592,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"states": []
}
}

View file

@ -40,6 +40,7 @@
"column_break_uhqk",
"login_with_email_link",
"login_with_email_link_expiry",
"rate_limit_email_link_login",
"brute_force_security",
"allow_consecutive_login_attempts",
"column_break_34",
@ -656,12 +657,19 @@
"fieldname": "store_attached_pdf_document",
"fieldtype": "Check",
"label": "Store Attached PDF Document"
},
{
"depends_on": "login_with_email_link",
"description": "You can set a high value here if multiple users will be logging in from the same network.",
"fieldname": "rate_limit_email_link_login",
"fieldtype": "Int",
"label": "Rate limit for email link login"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2024-03-14 15:18:01.465057",
"modified": "2024-03-22 15:43:48.347441",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",

View file

@ -82,6 +82,7 @@ class SystemSettings(Document):
]
otp_issuer_name: DF.Data | None
password_reset_limit: DF.Int
rate_limit_email_link_login: DF.Int
reset_password_link_expiry_duration: DF.Duration | None
reset_password_template: DF.Link | None
rounding_method: DF.Literal["Banker's Rounding (legacy)", "Banker's Rounding", "Commercial Rounding"]

View file

@ -171,7 +171,8 @@ class User(Document):
self.validate_username()
self.remove_disabled_roles()
self.validate_user_email_inbox()
ask_pass_update()
if self.user_emails:
ask_pass_update()
self.validate_allowed_modules()
self.validate_user_image()
self.set_time_zone()

View file

@ -162,7 +162,7 @@ def search_widget(
formatted_fields = [f"`tab{meta.name}`.`{f.strip()}`" for f in fields]
# Insert title field query after name
if meta.show_title_field_in_link:
if meta.show_title_field_in_link and meta.title_field:
formatted_fields.insert(1, f"`tab{meta.name}`.{meta.title_field} as `label`")
order_by_based_on_meta = get_order_by(doctype, meta)

View file

@ -156,7 +156,7 @@ def get_context(doc):
def enqueue_webhook(doc, webhook) -> None:
request_url = headers = data = None
request_url = headers = data = r = None
try:
webhook: Webhook = frappe.get_doc("Webhook", webhook.get("name"))
request_url = webhook.request_url

View file

@ -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-20 11:37\n"
"PO-Revision-Date: 2024-03-22 11:38\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Spanish\n"
"MIME-Version: 1.0\n"
@ -13910,7 +13910,7 @@ msgstr "Google"
#: website/doctype/website_settings/website_settings.json
msgctxt "Website Settings"
msgid "Google Analytics ID"
msgstr ""
msgstr "ID de Google Analytics"
#. Label of a Check field in DocType 'Website Settings'
#: website/doctype/website_settings/website_settings.json
@ -15014,19 +15014,19 @@ msgstr ""
#. Name of a DocType
#: email/doctype/imap_folder/imap_folder.json
msgid "IMAP Folder"
msgstr ""
msgstr "Carpeta IMAP"
#. Label of a Data field in DocType 'Communication'
#: core/doctype/communication/communication.json
msgctxt "Communication"
msgid "IMAP Folder"
msgstr ""
msgstr "Carpeta IMAP"
#. Label of a Table field in DocType 'Email Account'
#: email/doctype/email_account/email_account.json
msgctxt "Email Account"
msgid "IMAP Folder"
msgstr ""
msgstr "Carpeta IMAP"
#. Label of a Data field in DocType 'Activity Log'
#: core/doctype/activity_log/activity_log.json
@ -15892,7 +15892,7 @@ msgstr "Incluir símbolos, números y letras mayúsculas en la contraseña"
#: email/doctype/email_account/email_account.json
msgctxt "Email Account"
msgid "Incoming (POP/IMAP) Settings"
msgstr ""
msgstr "Configuración entrante (POP/IMAP)"
#. Label of a Data field in DocType 'Email Account'
#: email/doctype/email_account/email_account.json
@ -28056,7 +28056,7 @@ msgstr "Buscar en un tipo de documento."
#: public/js/frappe/ui/toolbar/navbar.html:29
msgid "Search or type a command ({0})"
msgstr ""
msgstr "Buscar o escribir un comando ({0})"
#: templates/includes/search_box.html:8
msgid "Search results for"

View file

@ -1089,11 +1089,6 @@ class DatabaseQuery:
f"`tab{self.doctype}`.`{sort_field or 'modified'}` {sort_order or 'desc'}"
)
# draft docs always on top
if hasattr(self.doctype_meta, "is_submittable") and self.doctype_meta.is_submittable:
if self.order_by:
args.order_by = f"`tab{self.doctype}`.docstatus asc, {args.order_by}"
def validate_order_by_and_group_by(self, parameters: str):
"""Check order by, group by so that atleast one column is selected and does not have subquery"""
if not parameters:

View file

@ -283,6 +283,7 @@ function format_content_for_timeline(content) {
// limits content to 40 characters
// escapes HTML
// and makes it bold
content = frappe.utils.html2text(content);
content = frappe.ellipsis(content, 40) || '""';
content = frappe.utils.escape_html(content);
return content.bold();

View file

@ -462,15 +462,22 @@ frappe.views.BaseList = class BaseList {
}
get_args() {
let filters = this.get_filters_for_args();
let group_by = this.get_group_by();
let group_by_required =
Array.isArray(filters) &&
filters.some((filter) => {
return filter[0] !== this.doctype;
});
return {
doctype: this.doctype,
fields: this.get_fields(),
filters: this.get_filters_for_args(),
filters,
order_by: this.sort_selector && this.sort_selector.get_sql_string(),
start: this.start,
page_length: this.page_length,
view: this.view,
group_by: this.get_group_by(),
group_by: group_by_required ? group_by : null,
};
}

View file

@ -740,6 +740,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
${right}
</div>
</div>
<div class="list-row-border"></div>
</div>
`;
}

View file

@ -54,6 +54,7 @@ frappe.views.CommunicationComposer = class {
fieldtype: "MultiSelect",
reqd: 0,
fieldname: "recipients",
default: this.get_default_recipients("recipients"),
},
{
fieldtype: "Button",
@ -72,11 +73,13 @@ frappe.views.CommunicationComposer = class {
label: __("CC"),
fieldtype: "MultiSelect",
fieldname: "cc",
default: this.get_default_recipients("cc"),
},
{
label: __("BCC"),
fieldtype: "MultiSelect",
fieldname: "bcc",
default: this.get_default_recipients("bcc"),
},
{
label: __("Schedule Send At"),
@ -199,6 +202,14 @@ frappe.views.CommunicationComposer = class {
return fields;
}
get_default_recipients(fieldname) {
if (this.frm?.events.get_email_recipients) {
return (this.frm.events.get_email_recipients(this.frm, fieldname) || []).join(", ");
} else {
return "";
}
}
guess_language() {
// when attach print for print format changes try to guess language
// if print format has language then set that else boot lang.

View file

@ -743,6 +743,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
.finally(() => {
this.hide_loading_screen();
this.update_url_with_filters();
this.report_settings.after_refresh?.(this);
});
}

View file

@ -1471,7 +1471,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
if (this.add_totals_row) {
const total_data = this.get_columns_totals(this.data);
total_data["name"] = __("Totals").bold();
total_data["name"] = __("Total");
total_data.is_total_row = true;
rows_in_order.push(total_data);
}

View file

@ -59,22 +59,30 @@
}
.list-row-container {
border-bottom: 1px solid $border-color;
display: flex;
flex-direction: column;
outline: none;
padding: 4px 5px;
padding: 0 var(--padding-xs);
&:focus {
.list-row {
background-color: var(--highlight-color);
}
}
.list-row-border {
border-bottom: 1px solid $border-color;
margin: 0 10px;
}
&:last-child .list-row-border {
display: none;
}
}
.list-row {
padding: 15px 15px 15px 0px;
height: 38px;
height: 40px;
cursor: pointer;
transition: color 0.2s;
-webkit-transition: color 0.2s;
@ -149,10 +157,13 @@
}
}
.select-like,
.file-select {
padding-left: 11px;
}
.select-like {
padding: 10px 0 10px 11px;
}
}
.list-row-head {
@ -160,8 +171,7 @@
cursor: default;
background-color: var(--subtle-fg);
height: 30px;
padding: 8px, 10px, 8px, 0px;
margin: 8px 5px 0px 5px;
margin: 0.5rem var(--padding-xs);
border-radius: var(--border-radius-md);
.list-check-all {
@ -246,6 +256,7 @@ input.list-row-checkbox {
margin-top: 0px;
margin-bottom: 0px;
--checkbox-right-margin: calc(var(--checkbox-size) / 2 + #{$level-margin-right});
background-color: var(--card-bg);
}
input.list-check-all {

View file

@ -110,7 +110,7 @@
.page-form {
margin: 0;
padding: var(--padding-sm);
padding: var(--padding-xs);
display: flex;
flex-wrap: wrap;
background-color: var(--card-bg);

View file

@ -164,6 +164,23 @@
frappe._messages = {{ translated_messages }};
frappe.web_form_doc = {{ web_form_doc | json }};
frappe.reference_doc = {{ reference_doc | json }};
function in_iframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
if (in_iframe()) {
// hide everything except the form and fix styles
$('nav').hide();
$('.web-form-header').hide();
$('.page-footer').hide();
$('footer').hide();
$('.page-breadcrumbs').hide();
$('.web-form').css('border', 'none').css('padding', 'unset');
$('.page_content').css('padding-left', 'unset').css('padding-right', 'unset');
}
</script>
{{ include_script("web_form.bundle.js") }}

View file

@ -24,6 +24,16 @@ frappe.ui.form.on("Web Form", {
},
refresh: function (frm) {
// get iframe url for web form
frm.sidebar
.add_user_action(__("Copy Embed Code"))
.attr("href", "#")
.on("click", () => {
const url = frappe.urllib.get_full_url(frm.doc.route);
const code = `<iframe src="${url}" style="border: none; width: 100%; height: inherit;"></iframe>`;
frappe.utils.copy_to_clipboard(code, __("Embed code copied"));
});
if (frm.doc.is_standard && !frappe.boot.developer_mode) {
frm.disable_form();
frappe.show_alert(

View file

@ -1,7 +1,7 @@
{% extends "templates/web.html" %}
{% set title = heading or "Contact Us" %}
{% block header %}<h1>{{ heading or "Contact Us" }}</h1>{% endblock %}
{% block header %}<h1>{{ heading or _("Contact Us") }}</h1>{% endblock %}
{% block page_content %}
<style>
@ -23,10 +23,10 @@
<select name="subject" class="form-control">
{% if query_options -%}
{% for option in query_options.split("\n") -%}
<option value="{{ option }}">{{ option }}</option>
<option value="{{ option }}">{{ _(option) }}</option>
{%- endfor %}
{% else %}
<option value="General">General</option>
<option value="General">{{ _("General") }}</option>
{% endif %}
</select>
</div>

View file

@ -34,20 +34,29 @@ def send_message(sender, message, subject="Website Query"):
if forward_to_email := frappe.db.get_single_value("Contact Us Settings", "forward_to_email"):
frappe.sendmail(recipients=forward_to_email, reply_to=sender, content=message, subject=subject)
reply = _(
"""Thank you for reaching out to us. We will get back to you at the earliest.
Your query:
{0}"""
).format(message)
frappe.sendmail(
recipients=sender,
content=f"<div style='white-space: pre-wrap'>Thank you for reaching out to us. We will get back to you at the earliest.\n\n\nYour query:\n\n{message}</div>",
subject="We've received your query!",
content=f"<div style='white-space: pre-wrap'>{reply}</div>",
subject=_("We've received your query!"),
)
# for clearing outgoing email error message
frappe.clear_last_message()
system_language = frappe.db.get_single_value("System Settings", "language")
# add to to-do ?
frappe.get_doc(
doctype="Communication",
sender=sender,
subject=_("New Message from Website Contact Page"),
subject=_("New Message from Website Contact Page", system_language),
sent_or_received="Received",
content=message,
status="Open",

View file

@ -155,8 +155,12 @@ def _generate_temporary_login_link(email: str, expiry: int):
return get_url(f"/api/method/frappe.www.login.login_via_key?key={key}")
def get_login_with_email_link_ratelimit() -> int:
return frappe.get_system_settings("rate_limit_email_link_login") or 5
@frappe.whitelist(allow_guest=True, methods=["GET"])
@rate_limit(limit=5, seconds=60 * 60)
@rate_limit(limit=get_login_with_email_link_ratelimit, seconds=60 * 60)
def login_via_key(key: str):
cache_key = f"one_time_login_key:{key}"
email = frappe.cache.get_value(cache_key)