feat: syntax highlighting in field description (#33791)
This commit is contained in:
parent
08de97b1ce
commit
cc82ab19ab
7 changed files with 102 additions and 50 deletions
|
|
@ -44,16 +44,15 @@ frappe.ui.form.on("Server Script", {
|
|||
},
|
||||
|
||||
setup_help(frm) {
|
||||
frm.get_field("help_html").html(`
|
||||
const help_field = frm.get_field("help_html");
|
||||
help_field.html(`
|
||||
<h4>DocType Event</h4>
|
||||
<p>Add logic for standard doctype events like Before Insert, After Submit, etc.</p>
|
||||
<pre>
|
||||
<code>
|
||||
<pre><code class="language-python">
|
||||
# set property
|
||||
if "test" in doc.description:
|
||||
doc.status = 'Closed'
|
||||
|
||||
|
||||
# validate
|
||||
if "validate" in doc.description:
|
||||
raise frappe.ValidationError
|
||||
|
|
@ -65,13 +64,11 @@ if doc.allocated_to:
|
|||
owner = doc.allocated_to,
|
||||
description = doc.subject
|
||||
)).insert()
|
||||
</code>
|
||||
</pre>
|
||||
</code></pre>
|
||||
|
||||
<h5>Payment processing</h5>
|
||||
<p>Payment processing events have a special state. See the <a href="https://github.com/frappe/payments/blob/develop/payments/controllers/payment_controller.py">PaymentController in Frappe Payments</a> for details.</p>
|
||||
<pre>
|
||||
<code>
|
||||
<pre><code class="language-python">
|
||||
# retreive payment session state
|
||||
ps = doc.flags.payment_session
|
||||
|
||||
|
|
@ -81,7 +78,10 @@ if ps.is_success:
|
|||
# custom process return values
|
||||
doc.flags.payment_session.result = {
|
||||
"message": "Thank you for your payment",
|
||||
"action": {"href": "https://shop.example.com", "label": "Return to shop"},
|
||||
"action": {
|
||||
"href": "https://shop.example.com",
|
||||
"label": "Return to shop"
|
||||
}
|
||||
}
|
||||
if ps.is_pre_authorized:
|
||||
if ps.changed: # could be an idempotent run
|
||||
|
|
@ -92,21 +92,20 @@ if ps.is_processing:
|
|||
if ps.is_declined:
|
||||
if ps.changed: # could be an idempotent run
|
||||
...
|
||||
</code>
|
||||
</pre>
|
||||
</code></pre>
|
||||
|
||||
<p>The <i>On Payment Failed</i> (<code>on_payment_failed</code>) event only transports the error message which the controller implementation had extracted from the transaction.</p>
|
||||
<pre>
|
||||
<code>
|
||||
|
||||
<pre><code class="language-python">
|
||||
msg = doc.flags.payment_failure_message
|
||||
doc.my_failure_message_field = msg
|
||||
</code>
|
||||
</pre>
|
||||
</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>API Call</h4>
|
||||
<p>Respond to <code>/api/method/<method-name></code> calls, just like whitelisted methods</p>
|
||||
<pre><code>
|
||||
<pre><code class="language-python">
|
||||
# respond to API
|
||||
|
||||
if frappe.form_dict.message == "ping":
|
||||
|
|
@ -119,12 +118,16 @@ else:
|
|||
|
||||
<h4>Permission Query</h4>
|
||||
<p>Add conditions to the where clause of list queries.</p>
|
||||
<pre><code>
|
||||
# generate dynamic conditions and set it in the conditions variable
|
||||
tenant_id = frappe.db.get_value(...)
|
||||
conditions = f'tenant_id = {tenant_id}'
|
||||
<p>Generate dynamic conditions and set it in the conditions variable:</p>
|
||||
|
||||
# resulting select query
|
||||
<pre><code class="language-python">
|
||||
tenant_id = frappe.db.get_value(...) # -> 2
|
||||
conditions = f'tenant_id = {tenant_id}'
|
||||
</code></pre>
|
||||
|
||||
<p>The resulting select query is:</p>
|
||||
|
||||
<pre><code class="language-sql">
|
||||
select name from \`tabPerson\`
|
||||
where tenant_id = 2
|
||||
order by creation desc
|
||||
|
|
@ -135,15 +138,13 @@ order by creation desc
|
|||
<h4>Workflow Task</h4>
|
||||
<p>Execute when a particular <a href="/app/workflow-action-master">Workflow Action Master</a> is executed.</p>
|
||||
<p>Gets the document which the action is being applied on in the <code>doc</code> variable.</p>
|
||||
<code><pre>
|
||||
<pre><code class="language-python">
|
||||
# create a customer with the same name as the given document
|
||||
|
||||
customer = frappe.new_doc("Customer")
|
||||
customer.customer_name = doc.first_name + " " + doc.last_name # we get this from the workflow action
|
||||
customer.customer_name = doc.first_name + " " + doc.last_name # we get this doc from the workflow action
|
||||
customer.customer_type = "Company"
|
||||
|
||||
c.save()
|
||||
</code></pre>
|
||||
`);
|
||||
customer.save()
|
||||
</code></pre>`);
|
||||
frappe.utils.highlight_pre(help_field.$wrapper);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
frappe.ui.form.on("Client Script", {
|
||||
setup(frm) {
|
||||
frm.get_field("sample").html(SAMPLE_HTML);
|
||||
const sample_field = frm.get_field("sample");
|
||||
sample_field.html(SAMPLE_HTML);
|
||||
frappe.utils.highlight_pre(sample_field.$wrapper);
|
||||
},
|
||||
refresh(frm) {
|
||||
if (frm.doc.dt && frm.doc.script) {
|
||||
|
|
@ -106,53 +108,50 @@ frappe.ui.form.on('${doctype}', {
|
|||
|
||||
const SAMPLE_HTML = `<h3>Client Script Help</h3>
|
||||
<p>Client Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started</p>
|
||||
<pre><code>
|
||||
|
||||
<pre><code class="language-javascript">
|
||||
// fetch local_tax_no on selection of customer
|
||||
// cur_frm.add_fetch(link_field, source_fieldname, target_fieldname);
|
||||
cur_frm.add_fetch("customer", "local_tax_no', 'local_tax_no');
|
||||
// cur_frm.add_fetch(link_field, source_fieldname, target_fieldname);
|
||||
cur_frm.add_fetch("customer", "local_tax_no", "local_tax_no");
|
||||
|
||||
// additional validation on dates
|
||||
frappe.ui.form.on('Task', 'validate', function(frm) {
|
||||
frappe.ui.form.on("Task", "validate", function(frm) {
|
||||
if (frm.doc.from_date < get_today()) {
|
||||
msgprint('You can not select past date in From Date');
|
||||
msgprint("You can not select past date in From Date");
|
||||
validated = false;
|
||||
}
|
||||
});
|
||||
|
||||
// make a field read-only after saving
|
||||
frappe.ui.form.on('Task', {
|
||||
frappe.ui.form.on("Task", {
|
||||
refresh: function(frm) {
|
||||
// use the __islocal value of doc, to check if the doc is saved or not
|
||||
frm.set_df_property('myfield', 'read_only', frm.doc.__islocal ? 0 : 1);
|
||||
frm.set_df_property("myfield", "read_only", frm.is_new() ? 0 : 1);
|
||||
}
|
||||
});
|
||||
|
||||
// additional permission check
|
||||
frappe.ui.form.on('Task', {
|
||||
frappe.ui.form.on("Task", {
|
||||
validate: function(frm) {
|
||||
if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {
|
||||
msgprint('You are only allowed Material Receipt');
|
||||
if(user === "user1@example.com" && frm.doc.purpose !== "Material Receipt") {
|
||||
msgprint("You are only allowed Material Receipt");
|
||||
validated = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// calculate sales incentive
|
||||
frappe.ui.form.on('Sales Invoice', {
|
||||
frappe.ui.form.on("Sales Invoice", {
|
||||
validate: function(frm) {
|
||||
// calculate incentives for each person on the deal
|
||||
total_incentive = 0
|
||||
$.each(frm.doc.sales_team, function(i, d) {
|
||||
for (const row of frm.doc.sales_team) {
|
||||
// calculate incentive
|
||||
var incentive_percent = 2;
|
||||
if(frm.doc.base_grand_total > 400) incentive_percent = 4;
|
||||
// actual incentive
|
||||
d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;
|
||||
total_incentive += flt(d.incentives)
|
||||
row.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;
|
||||
total_incentive += flt(row.incentives)
|
||||
});
|
||||
frm.doc.total_incentive = total_incentive;
|
||||
}
|
||||
})
|
||||
|
||||
</code></pre>`;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ frappe.notification = {
|
|||
if (frm.doc.channel === "Email") {
|
||||
template = `<h5>Message Example</h5>
|
||||
|
||||
<pre><h3>Order Overdue</h3>
|
||||
<pre><code class="language-xml"><h3>Order Overdue</h3>
|
||||
|
||||
<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Last comment: {{ comments[-1].comment }} by {{ comments[-1].by }}
|
|||
<li>Customer: {{ doc.customer }}</li>
|
||||
<li>Amount: {{ doc.grand_total }}</li>
|
||||
</ul>
|
||||
</pre>
|
||||
</code></pre>
|
||||
`;
|
||||
} else if (["Slack", "System Notification", "SMS"].includes(frm.doc.channel)) {
|
||||
template = `<h5>Message Example</h5>
|
||||
|
|
@ -148,7 +148,11 @@ Last comment: {{ comments[-1].comment }} by {{ comments[-1].by }}
|
|||
</pre>`;
|
||||
}
|
||||
if (template) {
|
||||
frm.set_df_property("message_examples", "options", template);
|
||||
const message_examples_field = frm.get_field("message_examples");
|
||||
message_examples_field.html(template);
|
||||
if (frm.doc.channel === "Email") {
|
||||
frappe.utils.highlight_pre(message_examples_field.$wrapper);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -206,7 +206,17 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control
|
|||
return;
|
||||
}
|
||||
if (this.df.description) {
|
||||
this.$wrapper.find(".help-box").html(__(this.df.description, null, this.df.parent));
|
||||
const description = __(this.df.description, null, this.df.parent);
|
||||
const help_box = this.$wrapper.find(".help-box");
|
||||
help_box.html(description);
|
||||
if (description.includes("<code")) {
|
||||
frappe.require("syntax_highlighting.bundle.js").then(() => {
|
||||
help_box.find("code").each(function () {
|
||||
hljs.highlightElement(this);
|
||||
this.style.display = "inline"; // override hljs's "block" display
|
||||
});
|
||||
});
|
||||
}
|
||||
this.toggle_description(true);
|
||||
} else {
|
||||
this.set_empty_description();
|
||||
|
|
|
|||
|
|
@ -1837,4 +1837,29 @@ Object.assign(frappe.utils, {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds syntax highlighting to all <pre> tags in the given jQuery wrapper.
|
||||
* Example wrapper:
|
||||
*
|
||||
* ```html
|
||||
* <pre><code class="language-python">
|
||||
* def add(a, b):
|
||||
* return a + b
|
||||
*
|
||||
* print(add(1, 2))
|
||||
*
|
||||
* # Output: 3
|
||||
* </code></pre>
|
||||
* ```
|
||||
*
|
||||
* @param {jQuery} $wrapper - The jQuery wrapper to add syntax highlighting to.
|
||||
*/
|
||||
highlight_pre($wrapper) {
|
||||
frappe.require("syntax_highlighting.bundle.js").then(() => {
|
||||
$wrapper.find("pre").each(function () {
|
||||
hljs.highlightElement(this);
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
12
frappe/public/js/syntax_highlighting.bundle.js
Normal file
12
frappe/public/js/syntax_highlighting.bundle.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import hljs from "highlight.js/lib/core";
|
||||
import javascript from "highlight.js/lib/languages/javascript";
|
||||
import python from "highlight.js/lib/languages/python";
|
||||
import xml from "highlight.js/lib/languages/xml";
|
||||
import sql from "highlight.js/lib/languages/sql";
|
||||
|
||||
hljs.registerLanguage("javascript", javascript);
|
||||
hljs.registerLanguage("python", python);
|
||||
hljs.registerLanguage("xml", xml);
|
||||
hljs.registerLanguage("sql", sql);
|
||||
|
||||
window.hljs = hljs;
|
||||
|
|
@ -9,3 +9,4 @@
|
|||
@import "frappe/public/js/lib/leaflet_easy_button/easy-button.css";
|
||||
@import "frappe/public/js/lib/leaflet_control_locate/L.Control.Locate.css";
|
||||
@import "frappe/public/js/lib/leaflet_draw/leaflet.draw.css";
|
||||
@import "frappe/public/node_modules/highlight.js/styles/tomorrow.css";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue