From 3c58fff01922b1607e4de793b4c3bda1b167d188 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 Oct 2022 22:04:42 +0530 Subject: [PATCH 01/39] fix: correcrt import ref: https://github.com/pallets/jinja/issues/1626 --- frappe/www/search.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/www/search.py b/frappe/www/search.py index d8a939cb15..8eac7b5cd6 100644 --- a/frappe/www/search.py +++ b/frappe/www/search.py @@ -1,4 +1,4 @@ -from jinja2 import utils +import markupsafe import frappe from frappe import _ @@ -10,7 +10,7 @@ from frappe.utils.global_search import web_search def get_context(context): context.no_cache = 1 if frappe.form_dict.q: - query = str(utils.escape(sanitize_html(frappe.form_dict.q))) + query = str(markupsafe.escape(sanitize_html(frappe.form_dict.q))) context.title = _("Search Results for") context.query = query context.route = "/search" From 45645c3f733eab607ae3b853183a70f32c0b6ebe Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 Oct 2022 22:26:32 +0530 Subject: [PATCH 02/39] feat: consistent, translatable timeline messages (#17526) reapply accidentally reverted commit [skip ci] --- cypress/integration/timeline.js | 4 +- frappe/patches.txt | 1 + .../v14_0/update_attachment_comment.py | 33 ++++ .../js/frappe/form/footer/form_timeline.js | 152 ++++++++++-------- .../version_timeline_content_builder.js | 137 +++++++++++----- frappe/translations/de.csv | 4 + frappe/utils/file_manager.py | 17 +- 7 files changed, 231 insertions(+), 117 deletions(-) create mode 100644 frappe/patches/v14_0/update_attachment_comment.py diff --git a/cypress/integration/timeline.js b/cypress/integration/timeline.js index 7835819334..c6076088fb 100644 --- a/cypress/integration/timeline.js +++ b/cypress/integration/timeline.js @@ -72,14 +72,14 @@ context("Timeline", () => { cy.click_listview_row_item(0); //To check if the submission of the documemt is visible in the timeline content - cy.get(".timeline-content").should("contain", "Frappe submitted this document"); + cy.get(".timeline-content").should("contain", "You submitted this document"); cy.get('[id="page-Custom Submittable DocType"] .page-actions') .findByRole("button", { name: "Cancel" }) .click(); cy.get_open_dialog().findByRole("button", { name: "Yes" }).click(); //To check if the cancellation of the documemt is visible in the timeline content - cy.get(".timeline-content").should("contain", "Frappe cancelled this document"); + cy.get(".timeline-content").should("contain", "You cancelled this document"); //Deleting the document cy.visit("/app/custom-submittable-doctype"); diff --git a/frappe/patches.txt b/frappe/patches.txt index 24b07012da..278a351093 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -214,3 +214,4 @@ execute:frappe.delete_doc('Page', 'background_jobs', ignore_missing=True, force= frappe.patches.v14_0.drop_unused_indexes frappe.patches.v15_0.drop_modified_index frappe.patches.v14_0.add_manage_subscriptions_in_navbar_settings +frappe.patches.v14_0.update_attachment_comment diff --git a/frappe/patches/v14_0/update_attachment_comment.py b/frappe/patches/v14_0/update_attachment_comment.py new file mode 100644 index 0000000000..042579d86d --- /dev/null +++ b/frappe/patches/v14_0/update_attachment_comment.py @@ -0,0 +1,33 @@ +import frappe + + +def execute(): + frappe.db.auto_commit_on_many_writes = 1 + + # Strip everything except link to attachment and icon from comments of type "Attached" + for name, content in frappe.get_all( + "Comment", filters={"comment_type": "Attachment"}, fields=["name", "content"], as_list=True + ): + if not content: + continue + + start = content.find("") + end = content.find("") if end == -1 else end + if end != -1: + content = content[: end + 4] + + frappe.db.set_value("Comment", name, "content", content, update_modified=False) + + # Strip "Removed " from comments of type "Attachment Removed" + for name, content in frappe.get_all( + "Comment", + filters={"comment_type": "Attachment Removed"}, + fields=["name", "content"], + as_list=True, + ): + if content and content.startswith("Removed "): + frappe.db.set_value("Comment", name, "content", content[8:], update_modified=False) diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index fe4b096166..d70da5f030 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -1,7 +1,11 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt import BaseTimeline from "./base_timeline"; -import { get_version_timeline_content } from "./version_timeline_content_builder"; +import { + get_version_timeline_content, + get_user_link, + get_user_message, +} from "./version_timeline_content_builder"; class FormTimeline extends BaseTimeline { make() { @@ -106,54 +110,50 @@ class FormTimeline extends BaseTimeline { render_timeline_items() { super.render_timeline_items(); - this.set_document_info(); + this.add_web_page_view_count(); frappe.utils.bind_actions_with_object(this.timeline_items_wrapper, this); } - set_document_info() { - // TODO: handle creation via automation - const creation = comment_when(this.frm.doc.creation); - let creation_message = frappe.utils.is_current_user(this.frm.doc.owner) - ? __("You created this {0}", [creation], "Form timeline") - : __( - "{0} created this {1}", - [this.get_user_link(this.frm.doc.owner), creation], - "Form timeline" - ); - - const modified = comment_when(this.frm.doc.modified); - let modified_message = frappe.utils.is_current_user(this.frm.doc.modified_by) - ? __("You edited this {0}", [modified], "Form timeline") - : __( - "{0} edited this {1}", - [this.get_user_link(this.frm.doc.modified_by), modified], - "Form timeline" - ); - + add_web_page_view_count() { if (this.frm.doc.route && cint(frappe.boot.website_tracking_enabled)) { - let route = this.frm.doc.route; - frappe.utils.get_page_view_count(route).then((res) => { - let page_view_count_message = __("{0} Page views", [res.message], "Form timeline"); - this.add_timeline_item( - { - content: `${creation_message} • ${modified_message} • ${page_view_count_message}`, - hide_timestamp: true, - }, - true - ); - }); - } else { - this.add_timeline_item( - { - content: `${creation_message} • ${modified_message}`, + frappe.utils.get_page_view_count(this.frm.doc.route).then((res) => { + this.add_timeline_item({ + content: __("{0} Web page views", [res.message], "Form timeline"), hide_timestamp: true, - }, - true - ); + }); + }); } } + get_creation_message() { + const user_link = get_user_link(this.frm.doc.owner); + + return { + creation: this.frm.doc.creation, + content: get_user_message( + this.frm.doc.owner, + __("You created this", null, "Form timeline"), + __("{0} created this", [user_link], "Form timeline") + ), + }; + } + + get_modified_message() { + const user_link = get_user_link(this.frm.doc.modified_by); + + return { + creation: this.frm.doc.modified, + content: get_user_message( + this.frm.doc.modified_by, + __("You last edited this", null, "Form timeline"), + __("{0} last edited this", [user_link], "Form timeline") + ), + }; + } + prepare_timeline_contents() { + this.timeline_items.push(this.get_creation_message()); + this.timeline_items.push(this.get_modified_message()); this.timeline_items.push(...this.get_communication_timeline_contents()); this.timeline_items.push(...this.get_auto_messages_timeline_contents()); this.timeline_items.push(...this.get_comment_timeline_contents()); @@ -172,29 +172,24 @@ class FormTimeline extends BaseTimeline { } } - get_user_link(user) { - const user_display_text = (frappe.user_info(user).fullname || "").bold(); - return frappe.utils.get_form_link("User", user, true, user_display_text); - } - get_view_timeline_contents() { let view_timeline_contents = []; (this.doc_info.views || []).forEach((view) => { const view_time = comment_when(view.creation); - let view_message = frappe.utils.is_current_user(view.owner) - ? __("You viewed this {0}", [view_time], "Form timeline") - : __( - "{0} viewed this {1}", - [this.get_user_link(view.owner), view_time], - "Form timeline" - ); + const user_link = get_user_link(view.owner); + const timeline_content = get_user_message( + view.owner, + __("You viewed this {0}", [view_time], "Form timeline"), + __("{0} viewed this {1}", [user_link, view_time], "Form timeline") + ); view_timeline_contents.push({ creation: view.creation, - content: view_message, + content: timeline_content, hide_timestamp: true, }); }); + return view_timeline_contents; } @@ -337,7 +332,7 @@ class FormTimeline extends BaseTimeline { (this.doc_info.info_logs || []).forEach((info_log) => { info_timeline_contents.push({ creation: info_log.creation, - content: `${this.get_user_link(info_log.owner)} ${info_log.content}`, + content: `${get_user_link(info_log.owner)} ${info_log.content}`, }); }); return info_timeline_contents; @@ -345,45 +340,76 @@ class FormTimeline extends BaseTimeline { get_attachment_timeline_contents() { let attachment_timeline_contents = []; + (this.doc_info.attachment_logs || []).forEach((attachment_log) => { - let is_file_upload = attachment_log.comment_type == "Attachment"; + const is_file_upload = attachment_log.comment_type == "Attachment"; + const user_link = get_user_link(attachment_log.owner); + const filename = attachment_log.content; + const timeline_content = is_file_upload + ? get_user_message( + attachment_log.owner, + __("You attached {0}", [filename], "Form timeline"), + __("{0} attached {1}", [user_link, filename], "Form timeline") + ) + : get_user_message( + attachment_log.owner, + __("You removed attachment {0}", [filename], "Form timeline"), + __("{0} removed attachment {1}", [user_link, filename], "Form timeline") + ); + attachment_timeline_contents.push({ icon: is_file_upload ? "upload" : "delete", icon_size: "sm", creation: attachment_log.creation, - content: `${this.get_user_link(attachment_log.owner)} ${attachment_log.content}`, + content: timeline_content, }); }); + return attachment_timeline_contents; } get_milestone_timeline_contents() { let milestone_timeline_contents = []; + (this.doc_info.milestones || []).forEach((milestone_log) => { + const field = frappe.meta.get_label(this.frm.doctype, milestone_log.track_field); + const value = milestone_log.value.bold(); + const user_link = get_user_link(milestone_log.owner); + const timeline_content = get_user_message( + milestone_log.owner, + __("You changed {0} to {1}", [field, value], "Form timeline"), + __("{0} changed {1} to {2}", [user_link, field, value], "Form timeline") + ); + milestone_timeline_contents.push({ icon: "milestone", creation: milestone_log.creation, - content: __("{0} changed {1} to {2}", [ - this.get_user_link(milestone_log.owner), - frappe.meta.get_label(this.frm.doctype, milestone_log.track_field), - milestone_log.value.bold(), - ]), + content: timeline_content, }); }); + return milestone_timeline_contents; } get_like_timeline_contents() { let like_timeline_contents = []; + (this.doc_info.like_logs || []).forEach((like_log) => { + const timeline_content = get_user_message( + like_log.owner, + __("You Liked", null, "Form timeline"), + __("{0} Liked", [get_user_link(like_log.owner)], "Form timeline") + ); + like_timeline_contents.push({ icon: "heart", icon_size: "sm", creation: like_log.creation, - content: __("{0} Liked", [this.get_user_link(like_log.owner)]), + content: timeline_content, title: "Like", }); }); + return like_timeline_contents; } @@ -394,7 +420,7 @@ class FormTimeline extends BaseTimeline { icon: "branch", icon_size: "sm", creation: workflow_log.creation, - content: `${this.get_user_link(workflow_log.owner)} ${__(workflow_log.content)}`, + content: `${get_user_link(workflow_log.owner)} ${__(workflow_log.content)}`, title: "Workflow", }); }); diff --git a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js index 33c87e458a..1912b5928e 100644 --- a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js +++ b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js @@ -30,19 +30,55 @@ function get_version_timeline_content(version_doc, frm) { if (p[0] === "docstatus") { if (p[2] === 1) { let message = updater_reference_link - ? __("{0} submitted this document {1}", [ - get_user_link(version_doc), - updater_reference_link, - ]) - : __("{0} submitted this document", [get_user_link(version_doc)]); + ? get_user_message( + version_doc.owner, + __( + "You submitted this document {0}", + [updater_reference_link], + "Form timeline" + ), + __( + "{0} submitted this document {1}", + [get_user_link(version_doc.owner), updater_reference_link], + "Form timeline" + ) + ) + : get_user_message( + version_doc.owner, + __("You submitted this document", null, "Form timeline"), + __( + "{0} submitted this document", + [get_user_link(version_doc.owner)], + "Form timeline" + ) + ); + out.push(get_version_comment(version_doc, message)); } else if (p[2] === 2) { let message = updater_reference_link - ? __("{0} cancelled this document {1}", [ - get_user_link(version_doc), - updater_reference_link, - ]) - : __("{0} cancelled this document", [get_user_link(version_doc)]); + ? get_user_message( + version_doc.owner, + __( + "You cancelled this document {1}", + [updater_reference_link], + "Form timeline" + ), + __( + "{0} cancelled this document {1}", + [get_user_link(version_doc.owner), updater_reference_link], + "Form timeline" + ) + ) + : get_user_message( + version_doc.owner, + __("You cancelled this document", null, "Form timeline"), + __( + "{0} cancelled this document", + [get_user_link(version_doc.owner)], + "Form timeline" + ) + ); + out.push(get_version_comment(version_doc, message)); } } else { @@ -67,19 +103,28 @@ function get_version_timeline_content(version_doc, frm) { return parts.length < 3; }); if (parts.length) { - let message; - if (updater_reference_link) { - message = __("{0} changed value of {1} {2}", [ - get_user_link(version_doc), - parts.join(", "), - updater_reference_link, - ]); - } else { - message = __("{0} changed value of {1}", [ - get_user_link(version_doc), - parts.join(", "), - ]); - } + let message = updater_reference_link + ? get_user_message( + version_doc.owner, + __("You changed the value of {0} {1}", [ + parts.join(", "), + updater_reference_link, + ]), + __("{0} changed the value of {1} {2}", [ + get_user_link(version_doc.owner), + parts.join(", "), + updater_reference_link, + ]) + ) + : get_user_message( + version_doc.owner, + __("You changed the value of {0}", [parts.join(", ")]), + __("{0} changed the value of {1}", [ + get_user_link(version_doc.owner), + parts.join(", "), + ]) + ); + out.push(get_version_comment(version_doc, message)); } } @@ -120,19 +165,28 @@ function get_version_timeline_content(version_doc, frm) { return parts.length < 3; }); if (parts.length) { - let message; - if (updater_reference_link) { - message = __("{0} changed values for {1} {2}", [ - get_user_link(version_doc), - parts.join(", "), - updater_reference_link, - ]); - } else { - message = __("{0} changed values for {1}", [ - get_user_link(version_doc), - parts.join(", "), - ]); - } + let message = updater_reference_link + ? get_user_message( + version_doc.owner, + __("You changed the values for {0} {1}", [ + parts.join(", "), + updater_reference_link, + ]), + __("{0} changed the values for {1} {2}", [ + get_user_link(version_doc.owner), + parts.join(", "), + updater_reference_link, + ]) + ) + : get_user_message( + version_doc.owner, + __("You changed the values for {0}", [parts.join(", ")]), + __("{0} changed the values for {1}", [ + get_user_link(version_doc.owner), + parts.join(", "), + ]) + ); + out.push(get_version_comment(version_doc, message)); } } @@ -168,7 +222,7 @@ function get_version_timeline_content(version_doc, frm) { } let version_comment = get_version_comment(version_doc, message); - let user_link = get_user_link(version_doc); + let user_link = get_user_link(version_doc.owner); out.push(`${user_link} ${version_comment}`); } } @@ -230,10 +284,13 @@ function format_content_for_timeline(content) { return content.bold(); } -function get_user_link(doc) { - const user = doc.owner; +function get_user_link(user) { const user_display_text = (frappe.user_info(user).fullname || "").bold(); return frappe.utils.get_form_link("User", user, true, user_display_text); } -export { get_version_timeline_content }; +function get_user_message(user, message_self, message_other) { + return frappe.utils.is_current_user(user) ? message_self : message_other; +} + +export { get_version_timeline_content, get_user_link, get_user_message }; diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index 2df384bc43..3d481e740e 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -4727,8 +4727,12 @@ Copy,Kopieren, Hide Saved,Gespeicherte ausblenden, Show Saved,Gespeicherte anzeigen, {0} created this {1},{0} erstellte dies {1}, +{0} created this,{0} erstellte dies, +You created this,Sie erstellten dies, {0} edited this {1},{0} bearbeitete dies {1}, +You edited this {0},Sie bearbeiteten dies {0}, {0} viewed this {1},{0} sah sich dies {1} an, +You viewed this {0},Sie sahen sich dies {0} an, Toggle Full Width,Toggle Volle Breite, Documentation,Dokumentation, About,Über, diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index 875ca10c63..ae53f8d9f7 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -54,19 +54,12 @@ def upload(): comment = {} if dt and dn: + file_url = file_doc.file_url.replace("#", "%23") if file_doc.file_name else file_doc.file_url + icon = ' ' if file_doc.is_private else "" + file_name = file_doc.file_name or file_doc.file_url comment = frappe.get_doc(dt, dn).add_comment( "Attachment", - _("added {0}").format( - "{file_name}{icon}".format( - **{ - "icon": ' ' if file_doc.is_private else "", - "file_url": file_doc.file_url.replace("#", "%23") - if file_doc.file_name - else file_doc.file_url, - "file_name": file_doc.file_name or file_doc.file_url, - } - ) - ), + f"{file_name}{icon}", ) return { @@ -295,7 +288,7 @@ def remove_file( ignore_permissions = True if not file_name: file_name = frappe.db.get_value("File", fid, "file_name") - comment = doc.add_comment("Attachment Removed", _("Removed {0}").format(file_name)) + comment = doc.add_comment("Attachment Removed", file_name) frappe.delete_doc( "File", fid, ignore_permissions=ignore_permissions, delete_permanently=delete_permanently ) From 9280a41e2737f1fea1160b2143bcdc827479fe23 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 12 Oct 2022 22:29:30 +0530 Subject: [PATCH 03/39] fix(UI): Child table responsive --- frappe/public/js/frappe/form/grid.js | 28 ++++++++++++------------ frappe/public/js/frappe/form/grid_row.js | 18 ++++++++++----- frappe/public/scss/common/grid.scss | 15 +++++++++++++ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 8da073ff59..2ffba97efa 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -52,9 +52,7 @@ export default class Grid { } allow_on_grid_editing() { - if (frappe.utils.is_xs()) { - return false; - } else if ((this.meta && this.meta.editable_grid) || !this.meta) { + if ((this.meta && this.meta.editable_grid) || !this.meta) { return true; } else { return false; @@ -66,17 +64,19 @@ export default class Grid {

-
-
-
-
-
- Grid Empty State - ${__("No Data")} +
+
+
+
+
+
+ Grid Empty State + ${__("No Data")} +
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 65f7be3dd1..4927f1e300 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -254,7 +254,7 @@ export default class GridRow { ).appendTo(this.row); this.row_index = $( - ` -
+ {%- if browse_by_category -%} - - +
+ + +
{%- endif -%}
-
+
{% if not result -%}
{{ no_result_message or _("Nothing to show") }} @@ -54,10 +55,10 @@ {% block script %}