diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index bede0b429b..9db656c924 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -146,6 +146,7 @@ frappe.ui.form.on("Notification", { }, }; }); + frm.preview_fields = frm.doc.__onload.preview_fields; }, refresh: function (frm) { frappe.notification.setup_fieldname_select(frm); @@ -161,6 +162,17 @@ frappe.ui.form.on("Notification", { }); frm.get_field("is_standard").toggle(frappe.boot.developer_mode); frm.trigger("event"); + if (frm.doc.document_type) { + frm.add_custom_button(__("Preview"), () => { + const args = { + doc: frm.doc, + doctype: frm.doc.document_type, + preview_fields: frm.preview_fields, + }; + let dialog = new frappe.views.RenderPreviewer(args); + return dialog; + }); + } }, document_type: function (frm) { frappe.notification.setup_fieldname_select(frm); diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index c9e9f44a53..78aa47a2b2 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -73,11 +73,65 @@ class Notification(Document): """load message""" if self.is_standard: self.message = self.get_template() + self.set_onload( + "preview_fields", + [ + {"label": _("Meets Condition?"), "fieldtype": "Data", "method": "preview_meets_condition"}, + {"label": _("Subject"), "fieldtype": "Data", "method": "preview_subject"}, + {"label": _("Message"), "fieldtype": "Code", "method": "preview_message"}, + ], + ) def autoname(self): if not self.name: self.name = self.subject + # START: PreviewRenderer API + + @frappe.whitelist() + def preview_meets_condition(self, preview_document): + if not self.condition: + return _("Yes") + try: + doc = frappe.get_cached_doc(self.document_type, preview_document) + return _("Yes") if frappe.safe_eval(self.condition, eval_locals=get_context(doc)) else _("No") + except Exception as e: + frappe.local.message_log = [] + return _("Failed to evaluate conditions: {}").format(e) + + @frappe.whitelist() + def preview_message(self, preview_document): + try: + doc = frappe.get_cached_doc(self.document_type, preview_document) + context = get_context(doc) + context.update({"alert": self, "comments": None}) + if doc.get("_comments"): + context["comments"] = json.loads(doc.get("_comments")) + msg = frappe.render_template(self.message, context) + if self.channel == "SMS": + return frappe.utils.strip_html_tags(msg) + return msg + except Exception as e: + return _("Failed to render message: {}").format(e) + + @frappe.whitelist() + def preview_subject(self, preview_document): + try: + doc = frappe.get_cached_doc(self.document_type, preview_document) + context = get_context(doc) + context.update({"alert": self, "comments": None}) + if doc.get("_comments"): + context["comments"] = json.loads(doc.get("_comments")) + if not self.subject: + return _("No subject") + if "{" in self.subject: + return frappe.render_template(self.subject, context) + return self.subject + except Exception as e: + return _("Failed to render subject: {}").format(e) + + # END: PreviewRenderer API + def validate(self): if self.channel in ("Email", "Slack", "System Notification"): validate_template(self.subject) diff --git a/frappe/public/js/form.bundle.js b/frappe/public/js/form.bundle.js index f2d19f5f3b..84af6d3f60 100644 --- a/frappe/public/js/form.bundle.js +++ b/frappe/public/js/form.bundle.js @@ -11,6 +11,7 @@ import "./frappe/form/templates/timeline_message_box.html"; import "./frappe/form/templates/users_in_sidebar.html"; import "./frappe/views/formview.js"; +import "./frappe/views/render_preview.js"; import "./frappe/form/form.js"; import "./frappe/meta_tag.js"; import "./frappe/doctype/"; diff --git a/frappe/public/js/frappe/views/render_preview.js b/frappe/public/js/frappe/views/render_preview.js new file mode 100644 index 0000000000..a1d5348b52 --- /dev/null +++ b/frappe/public/js/frappe/views/render_preview.js @@ -0,0 +1,100 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt +frappe.provide("frappe.views"); + +frappe.views.RenderPreviewer = class RenderPreviewer { + constructor(opts) { + $.extend(this, opts); + this.make(); + } + + make() { + let me = this; + + me.set_fields(); + let fields = me.header_fields; + fields.push(...me.body_fields); + + me.dialog = new frappe.ui.Dialog({ + title: __("Preview on") + " " + __(me.doctype), + no_submit_on_enter: true, + fields: fields, + minimizable: true, + size: "large", + }); + + me.prepare(); + me.dialog.show(); + } + + set_fields() { + let me = this; + me.header_fields = [ + { + label: __("Reference Doctype"), + fieldtype: "Link", + fieldname: "doctype", + reqd: 1, + read_only: 1, + hidden: 1, + }, + { + label: __("Document"), + fieldtype: "Dynamic Link", + fieldname: "preview_document", + options: "doctype", + reqd: 1, + onchange: () => me.render_previews(), + }, + ]; + me.body_fields = [{ fieldtype: "Section Break" }]; + + me.preview_fields.forEach((spec) => { + let field = { + label: spec.label, + fieldtype: spec.fieldtype, + fieldname: spec.method, + read_only: 1, + }; + if (spec.method === "preview_meets_condition") { + me.header_fields.push({ fieldtype: "Column Break" }); + me.header_fields.push(field); + } else { + me.body_fields.push(field); + } + }); + } + + prepare() { + let me = this; + me.dialog.set_values({ + doctype: me.doctype, + }); + } + async render_previews() { + let me = this; + let preview_document = me.dialog.get_value("preview_document"); + if (preview_document) { + let promises = []; + me.preview_fields.forEach((spec) => { + let fieldname = spec.method; + promises.push( + frappe + .xcall("run_doc_method", { + dt: me.doc.doctype, + dn: me.doc.name, + method: spec.method, + arg: preview_document, + }) + .then((message) => me.dialog.set_value(fieldname, message)) + ); + }); + await Promise.all(promises); + } else { + me.preview_fields.forEach((spec) => { + let fieldname = spec.method; + me.dialog.set_value(fieldname, null); + }); + } + } +};