From 68829ab2e6d2ca8238bd9d5bd44844e032cee089 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Thu, 21 Jan 2021 19:06:33 +0100
Subject: [PATCH 001/106] feat: default_email_template
---
frappe/core/doctype/doctype/doctype.json | 13 ++++++++++++-
frappe/public/js/frappe/views/communication.js | 1 +
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index 215ef8cd62..55c74a4e63 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -55,6 +55,8 @@
"show_preview_popup",
"show_name_in_global_search",
"email_settings_sb",
+ "default_email_template",
+ "column_break_51",
"email_append_to",
"sender_field",
"subject_field",
@@ -528,6 +530,15 @@
"fieldname": "index_web_pages_for_search",
"fieldtype": "Check",
"label": "Index Web Pages for Search"
+ },
+ {
+ "fieldname": "default_email_template",
+ "fieldtype": "Data",
+ "label": "Default Email Template"
+ },
+ {
+ "fieldname": "column_break_51",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-bolt",
@@ -609,7 +620,7 @@
"link_fieldname": "reference_doctype"
}
],
- "modified": "2020-09-24 13:13:58.227153",
+ "modified": "2021-01-21 18:09:47.135696",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index c69be04347..7b9668a96e 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -159,6 +159,7 @@ frappe.views.CommunicationComposer = Class.extend({
this.setup_last_edited_communication();
this.setup_email_template();
+ this.dialog.set_value("email_template", this.frm.meta.default_email_template || '');
this.dialog.set_value("recipients", this.recipients || '');
this.dialog.set_value("cc", this.cc || '');
this.dialog.set_value("bcc", this.bcc || '');
From 2618ee74d898f48a0899691c045e2ce1e0a4b4da Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 27 Jan 2021 18:55:54 +0100
Subject: [PATCH 002/106] feat: add default_email_template to Customize Form
---
.../doctype/customize_form/customize_form.json | 13 ++++++++++++-
.../custom/doctype/customize_form/customize_form.py | 1 +
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index ff102b3c08..dee2dfce0d 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -31,6 +31,8 @@
"show_preview_popup",
"image_view",
"email_settings_section",
+ "default_email_template",
+ "column_break_26",
"email_append_to",
"sender_field",
"subject_field",
@@ -261,6 +263,15 @@
"fieldtype": "Table",
"label": "Actions",
"options": "DocType Action"
+ },
+ {
+ "fieldname": "default_email_template",
+ "fieldtype": "Data",
+ "label": "Default Email Template"
+ },
+ {
+ "fieldname": "column_break_26",
+ "fieldtype": "Column Break"
}
],
"hide_toolbar": 1,
@@ -269,7 +280,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2020-09-24 14:16:49.594012",
+ "modified": "2021-01-27 18:26:59.705786",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 82513783c7..0718f5d84c 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -479,6 +479,7 @@ doctype_properties = {
'allow_auto_repeat': 'Check',
'allow_import': 'Check',
'show_preview_popup': 'Check',
+ 'default_email_template': 'Data',
'email_append_to': 'Check',
'subject_field': 'Data',
'sender_field': 'Data'
From b14b28d7650001af5d7812bda07c15cb74e883ff Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 27 Jan 2021 18:59:00 +0100
Subject: [PATCH 003/106] fix: check if frm is available
Prevents error when creating new Communication from list view.
---
frappe/public/js/frappe/views/communication.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index 7b9668a96e..073ce44206 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -159,7 +159,10 @@ frappe.views.CommunicationComposer = Class.extend({
this.setup_last_edited_communication();
this.setup_email_template();
- this.dialog.set_value("email_template", this.frm.meta.default_email_template || '');
+ if ('frm' in this) {
+ this.dialog.set_value("email_template", this.frm.meta.default_email_template || '');
+ }
+
this.dialog.set_value("recipients", this.recipients || '');
this.dialog.set_value("cc", this.cc || '');
this.dialog.set_value("bcc", this.bcc || '');
From fa39484571d993ed5562149b6c85615e39cdc27b Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 27 Jan 2021 18:59:47 +0100
Subject: [PATCH 004/106] fix: check if email_template is set
Prevents error on empty email_template.
---
frappe/public/js/frappe/views/communication.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index 073ce44206..c5c6cdced9 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -232,6 +232,9 @@ frappe.views.CommunicationComposer = Class.extend({
this.dialog.fields_dict["email_template"].df.onchange = () => {
var email_template = me.dialog.fields_dict.email_template.get_value();
+ if (email_template === '') {
+ return;
+ }
var prepend_reply = function(reply) {
if(me.reply_added===email_template) {
From 885d198622703ef50b0f6443f3a00ee76b0c2d58 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Thu, 4 Feb 2021 12:20:12 +0100
Subject: [PATCH 005/106] fix: don't apply default email template for reply
---
frappe/public/js/frappe/views/communication.js | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index f1feb4f6a3..66e050f3d1 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -196,10 +196,6 @@ frappe.views.CommunicationComposer = Class.extend({
this.setup_last_edited_communication();
this.setup_email_template();
- if ('frm' in this) {
- this.dialog.set_value("email_template", this.frm.meta.default_email_template || '');
- }
-
this.dialog.set_value("recipients", this.recipients || '');
this.dialog.set_value("cc", this.cc || '');
this.dialog.set_value("bcc", this.bcc || '');
@@ -210,6 +206,11 @@ frappe.views.CommunicationComposer = Class.extend({
this.dialog.fields_dict.subject.set_value(this.subject || '');
this.setup_earlier_reply();
+
+ if ('frm' in this && !this.is_a_reply) {
+ // set default email template for the first email in a document
+ this.dialog.set_value("email_template", this.frm.meta.default_email_template || '');
+ }
},
setup_subject_and_recipients: function() {
From cb211c6272669662d1012aa7331538ba856c1579 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 8 Feb 2021 12:51:47 +0100
Subject: [PATCH 006/106] fix: signature should be an empty string by default
(would become undefined if the server message was empty)
---
frappe/public/js/frappe/views/communication.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index 66e050f3d1..5329967e12 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -726,7 +726,7 @@ frappe.views.CommunicationComposer = Class.extend({
if (!signature) {
const res = await this.get_default_outgoing_email_account_signature();
- signature = res.message.signature;
+ signature = res.message.signature || "";
}
if(!frappe.utils.is_html(signature)) {
From 87326625fed3f0ce0b72977f2e988783e59741c8 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 22 Mar 2021 12:31:58 +0100
Subject: [PATCH 007/106] fix: make Default Email Template a link field
---
frappe/core/doctype/doctype/doctype.json | 7 ++++---
frappe/custom/doctype/customize_form/customize_form.json | 7 ++++---
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index 7bf91892ee..fb24c095f3 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -533,8 +533,9 @@
},
{
"fieldname": "default_email_template",
- "fieldtype": "Data",
- "label": "Default Email Template"
+ "fieldtype": "Link",
+ "label": "Default Email Template",
+ "options": "Email Template"
},
{
"fieldname": "column_break_51",
@@ -620,7 +621,7 @@
"link_fieldname": "reference_doctype"
}
],
- "modified": "2021-02-23 15:10:09.227205",
+ "modified": "2021-03-22 12:26:41.031135",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index dee2dfce0d..f8db73137e 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -266,8 +266,9 @@
},
{
"fieldname": "default_email_template",
- "fieldtype": "Data",
- "label": "Default Email Template"
+ "fieldtype": "Link",
+ "label": "Default Email Template",
+ "options": "Email Template"
},
{
"fieldname": "column_break_26",
@@ -280,7 +281,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-01-27 18:26:59.705786",
+ "modified": "2021-03-22 12:27:15.462727",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",
From 6df8479525c4e7af1cfd3b0428700c40cad0c9a9 Mon Sep 17 00:00:00 2001
From: Richard Case
'+__('Dear') +' ' - + this.real_name + ",
${__('Dear')} ${this.real_name},
+- last_email_content = this.html2text(last_email_content).replace(/\n/g, '
'); - - // clip last email for a maximum of 20k characters - // to prevent the email content from getting too large - if (last_email_content.length > 20 * 1024) { - last_email_content += '' + __('Message clipped') + '' + last_email_content; - last_email_content = last_email_content.slice(0, 20 * 1024); - } - - let communication_date = last_email.communication_date || last_email.creation; - content = ` - ${reply} -- ${frappe.separator_element || ''} -${__("On {0}, {1} wrote:", [frappe.datetime.global_date_format(communication_date) , last_email.sender])}
-- ${last_email_content} -- `; - } else { - content = reply; + message += this.get_earlier_reply(); } - fields.content.set_value(content); + + await this.dialog.set_value("content", message); }, - html2text: function(html) { + get_signature: async function () { + let signature = frappe.boot.user.email_signature; + + if (!signature) { + const response = await frappe.db.get_value( + 'Email Account', + {'default_outgoing': 1, 'add_signature': 1}, + 'signature' + ); + + signature = response.message.signature; + } + + if (!signature) return ""; + + if (!frappe.utils.is_html(signature)) { + signature = signature.replace(/\n/g, "
"); + } + + return "
" + signature; + }, + + get_earlier_reply() { + const last_email = ( + this.last_email + || this.frm && this.frm.timeline.get_last_email(true) + ); + + if (!last_email) return ""; + let last_email_content = last_email.original_comment || last_email.content; + + // convert the email context to text as we are enclosing + // this inside+ last_email_content = this.html2text(last_email_content).replace(/\n/g, '`; - }, + } html2text(html) { // convert HTML to text and try and preserve whitespace @@ -798,4 +799,4 @@ frappe.views.CommunicationComposer = Class.extend({ // replace multiple empty lines with just one return d.textContent.replace(/\n{3,}/g, '\n\n'); } -}); +}; From c02fbb27b641153be54dd5025e7b183e19ac181a Mon Sep 17 00:00:00 2001 From: Sagar Vora
'); + + // clip last email for a maximum of 20k characters + // to prevent the email content from getting too large + if (last_email_content.length > 20 * 1024) { + last_email_content += '' + __('Message clipped') + '' + last_email_content; + last_email_content = last_email_content.slice(0, 20 * 1024); + } + + const communication_date = last_email.communication_date || last_email.creation; + return ` ++ ${separator_element || ''} +${__("On {0}, {1} wrote:", [ + frappe.datetime.global_date_format(communication_date), + last_email.sender + ])}
++ ${last_email_content} ++ `; + }, + + html2text(html) { // convert HTML to text and try and preserve whitespace - var d = document.createElement( 'div' ); + const d = document.createElement( 'div' ); d.innerHTML = html.replace(/<\/div>/g, '
') // replace end of blocks .replace(/<\/p>/g, '
') // replace end of paragraphs .replace(/
/g, '\n'); - let text = d.textContent; // replace multiple empty lines with just one - return text.replace(/\n{3,}/g, '\n\n'); + return d.textContent.replace(/\n{3,}/g, '\n\n'); } }); From e4527284d735e7b472bbbf0fad1ed7d299d5335a Mon Sep 17 00:00:00 2001 From: Sagar VoraDate: Sat, 17 Apr 2021 02:12:12 +0530 Subject: [PATCH 020/106] fix: sider issues --- .../public/js/frappe/views/communication.js | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 7cf7b32797..c3e6dfe00a 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -187,7 +187,7 @@ frappe.views.CommunicationComposer = Class.extend({ this.dialog.fields_dict[field].set_data(r.message); } }); - } + }; }); }, @@ -210,12 +210,12 @@ frappe.views.CommunicationComposer = Class.extend({ if (last) { this.subject = last.subject; - if(!this.recipients) { + if (!this.recipients) { this.recipients = last.sender; } // prepend "Re:" - if(strip(this.subject.toLowerCase().split(":")[0])!="re") { + if (strip(this.subject.toLowerCase().split(":")[0])!="re") { this.subject = __("Re: {0}", [this.subject]); } } @@ -304,7 +304,7 @@ frappe.views.CommunicationComposer = Class.extend({ if (this.frm) { $(document).trigger("form-stopped-typing", [this.frm]); } - } + }; }, get_last_edited_communication(clear) { @@ -314,7 +314,6 @@ frappe.views.CommunicationComposer = Class.extend({ if (clear || !frappe.last_edited_communication[this.doctype][this.key]) { frappe.last_edited_communication[this.doctype][this.key] = {}; - console.log('cleared!'); } return frappe.last_edited_communication[this.doctype][this.key]; @@ -527,7 +526,7 @@ frappe.views.CommunicationComposer = Class.extend({ // email const fields = this.dialog.fields_dict; - if(this.attach_document_print) { + if (this.attach_document_print) { $(fields.attach_document_print.input).click(); $(fields.select_print_format.wrapper).toggle(true); } @@ -545,7 +544,7 @@ frappe.views.CommunicationComposer = Class.extend({ const me = this; const btn = me.dialog.get_primary_btn(); const form_values = this.get_values(); - if(!form_values) return; + if (!form_values) return; const selected_attachments = $.map($(me.dialog.wrapper).find("[data-file-name]:checked"), function (element) { @@ -553,7 +552,7 @@ frappe.views.CommunicationComposer = Class.extend({ }); - if(form_values.attach_document_print) { + if (form_values.attach_document_print) { me.send_email(btn, form_values, selected_attachments, null, form_values.select_print_format || ""); } else { me.send_email(btn, form_values, selected_attachments); @@ -617,18 +616,18 @@ frappe.views.CommunicationComposer = Class.extend({ const me = this; me.dialog.hide(); - if(!form_values.recipients) { + if (!form_values.recipients) { frappe.msgprint(__("Enter Email Recipient(s)")); return; } - if(!form_values.attach_document_print) { + if (!form_values.attach_document_print) { print_html = null; print_format = null; } - if(this.frm && !frappe.model.can_email(me.doc.doctype, this.frm)) { + if (this.frm && !frappe.model.can_email(me.doc.doctype, this.frm)) { frappe.msgprint(__("You are not allowed to send emails related to this document")); return; } @@ -660,10 +659,10 @@ frappe.views.CommunicationComposer = Class.extend({ }, btn, callback(r) { - if(!r.exc) { + if (!r.exc) { frappe.utils.play_sound("email"); - if(r.message["emails_not_sent_to"]) { + if (r.message["emails_not_sent_to"]) { frappe.msgprint(__("Email not sent to {0} (unsubscribed / disabled)", [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); } @@ -680,7 +679,7 @@ frappe.views.CommunicationComposer = Class.extend({ try { me.success(r); } catch (e) { - console.log(e); + console.log(e); // eslint-disable-line } } @@ -692,7 +691,7 @@ frappe.views.CommunicationComposer = Class.extend({ try { me.error(r); } catch (e) { - console.log(e); + console.log(e); // eslint-disable-line } } } @@ -777,14 +776,16 @@ frappe.views.CommunicationComposer = Class.extend({ last_email_content = last_email_content.slice(0, 20 * 1024); } - const communication_date = last_email.communication_date || last_email.creation; + const communication_date = frappe.datetime.global_date_format( + last_email.communication_date || last_email.creation + ); + return ` ${separator_element || ''} -${__("On {0}, {1} wrote:", [ - frappe.datetime.global_date_format(communication_date), - last_email.sender - ])}
++ ${__("On {0}, {1} wrote:", [communication_date, last_email.sender])} +
${last_email_content}From 4a28b2f20285fe4d481a28f50b0473c20147e095 Mon Sep 17 00:00:00 2001 From: Sagar VoraDate: Sat, 17 Apr 2021 02:36:52 +0530 Subject: [PATCH 021/106] fix: set lang to frappe.boot.lang by default --- frappe/public/js/frappe/views/communication.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index c3e6dfe00a..34201a7900 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -387,10 +387,8 @@ frappe.views.CommunicationComposer = Class.extend({ //Load default print language from doctype this.lang_code = doc.language - - if (!this.lang_code && this.get_print_format().default_print_language) { - this.lang_code = this.get_print_format().default_print_language; - } + || this.get_print_format().default_print_language + || frappe.boot.lang; //On selection of language retrieve language code const me = this; From 354e89f4c60e241bbc16b64d0fd6c8dd995f1130 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 17 Apr 2021 03:24:01 +0530 Subject: [PATCH 022/106] fix: clear_cache only on success; use me instead of this --- frappe/public/js/frappe/views/communication.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 34201a7900..ef05ef5857 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -17,16 +17,15 @@ frappe.views.CommunicationComposer = Class.extend({ no_submit_on_enter: true, fields: this.get_fields(), primary_action_label: __("Send"), - size: 'large', primary_action() { me.send_action(); - me.clear_cache(); }, secondary_action_label: __("Discard"), secondary_action() { me.dialog.hide(); me.clear_cache(); }, + size: 'large', minimizable: true }); @@ -665,11 +664,10 @@ frappe.views.CommunicationComposer = Class.extend({ [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); } - if ((frappe.last_edited_communication[me.doc] || {})[me.key]) { - delete frappe.last_edited_communication[me.doc][me.key]; - } - if (this.frm) { - this.frm.reload_doc(); + me.clear_cache(); + + if (me.frm) { + me.frm.reload_doc(); } // try the success callback if it exists From 47d13a40c754949fc2110ab667dfaad6716c3f3d Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 17 Apr 2021 12:21:19 +0530 Subject: [PATCH 023/106] style: use ES6 class --- .../public/js/frappe/views/communication.js | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index ef05ef5857..77cc91b4ba 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -4,11 +4,12 @@ frappe.last_edited_communication = {}; const separator_element = ' ---'; -frappe.views.CommunicationComposer = Class.extend({ - init(opts) { +frappe.views.CommunicationComposer = class { + constructor(opts) { $.extend(this, opts); this.make(); - }, + } + make() { const me = this; @@ -37,7 +38,7 @@ frappe.views.CommunicationComposer = Class.extend({ if (this.frm) { $(document).trigger('form-typing', [this.frm]); } - }, + } get_fields() { const fields = [ @@ -151,7 +152,7 @@ frappe.views.CommunicationComposer = Class.extend({ } return fields; - }, + } toggle_more_options(show_options) { show_options = show_options || this.dialog.fields_dict.more_options.df.hidden; @@ -159,7 +160,7 @@ frappe.views.CommunicationComposer = Class.extend({ const label = frappe.utils.icon(show_options ? 'up-line': 'down'); this.dialog.get_field('option_toggle_button').set_label(label); - }, + } prepare() { this.setup_multiselect_queries(); @@ -171,7 +172,7 @@ frappe.views.CommunicationComposer = Class.extend({ this.setup_email_template(); this.setup_last_edited_communication(); this.set_values(); - }, + } setup_multiselect_queries() { ['recipients', 'cc', 'bcc'].forEach(field => { @@ -188,7 +189,7 @@ frappe.views.CommunicationComposer = Class.extend({ }); }; }); - }, + } setup_subject_and_recipients() { this.subject = this.subject || ""; @@ -240,7 +241,7 @@ frappe.views.CommunicationComposer = Class.extend({ if (this.frm && !this.recipients) { this.recipients = this.frm.doc[this.frm.email_field]; } - }, + } setup_email_template() { const me = this; @@ -276,7 +277,7 @@ frappe.views.CommunicationComposer = Class.extend({ }, }); } - }, + } setup_last_edited_communication() { if (this.frm) { @@ -304,7 +305,7 @@ frappe.views.CommunicationComposer = Class.extend({ $(document).trigger("form-stopped-typing", [this.frm]); } }; - }, + } get_last_edited_communication(clear) { if (!frappe.last_edited_communication[this.doctype]) { @@ -316,9 +317,9 @@ frappe.views.CommunicationComposer = Class.extend({ } return frappe.last_edited_communication[this.doctype][this.key]; - }, + } - set_values: async function () { + async set_values() { for (const fieldname of ["recipients", "cc", "bcc", "sender"]) { await this.dialog.set_value(fieldname, this[fieldname] || ""); } @@ -341,9 +342,9 @@ frappe.views.CommunicationComposer = Class.extend({ break; } } - }, + } - set_values_from_last_edited_communication: async function () { + async set_values_from_last_edited_communication() { if (this.txt) return; const last_edited = this.get_last_edited_communication(); @@ -358,7 +359,7 @@ frappe.views.CommunicationComposer = Class.extend({ await this.dialog.set_values(last_edited); this.content_set = true; - }, + } selected_format() { return ( @@ -366,7 +367,7 @@ frappe.views.CommunicationComposer = Class.extend({ || this.frm && this.frm.meta.default_print_format || "Standard" ); - }, + } get_print_format(format) { if (!format) { @@ -378,7 +379,7 @@ frappe.views.CommunicationComposer = Class.extend({ } else { return {}; } - }, + } setup_print_language() { const doc = this.frm && this.frm.doc; @@ -403,7 +404,7 @@ frappe.views.CommunicationComposer = Class.extend({ if (this.lang_code) { $(fields.language_sel.input).val(this.lang_code); } - }, + } setup_print() { // print formats @@ -427,7 +428,7 @@ frappe.views.CommunicationComposer = Class.extend({ $(fields.attach_document_print.wrapper).toggle(false); } - }, + } setup_attach() { const fields = this.dialog.fields_dict; @@ -474,7 +475,7 @@ frappe.views.CommunicationComposer = Class.extend({ .find(".add-more-attachments button") .on('click', () => new frappe.ui.FileUploader(args)); this.render_attachment_rows(); - }, + } render_attachment_rows(attachment) { const select_attachments = this.dialog.fields_dict.select_attachments; @@ -500,7 +501,7 @@ frappe.views.CommunicationComposer = Class.extend({ }); } } - }, + } get_attachment_row(attachment, checked) { return $(`@@ -517,7 +518,7 @@ frappe.views.CommunicationComposer = Class.extend({ ${frappe.utils.icon('link-url')}
`); - }, + } setup_email() { // email @@ -535,7 +536,7 @@ frappe.views.CommunicationComposer = Class.extend({ frappe.boot.user.send_me_a_copy = val; }); - }, + } send_action() { const me = this; @@ -554,7 +555,7 @@ frappe.views.CommunicationComposer = Class.extend({ } else { me.send_email(btn, form_values, selected_attachments); } - }, + } get_values() { const form_values = this.dialog.get_values(); @@ -575,7 +576,7 @@ frappe.views.CommunicationComposer = Class.extend({ } return form_values; - }, + } save_as_draft() { if (this.dialog && this.frm) { @@ -590,12 +591,12 @@ frappe.views.CommunicationComposer = Class.extend({ }); } - }, + } clear_cache() { this.delete_saved_draft(); this.get_last_edited_communication(true); - }, + } delete_saved_draft() { if (this.dialog && this.frm) { @@ -607,7 +608,7 @@ frappe.views.CommunicationComposer = Class.extend({ } }); } - }, + } send_email(btn, form_values, selected_attachments, print_html, print_format) { const me = this; @@ -693,7 +694,7 @@ frappe.views.CommunicationComposer = Class.extend({ } } }); - }, + } is_print_letterhead_checked() { if (this.frm && $(this.frm.wrapper).find('.form-print-wrapper').is(':visible')){ @@ -702,9 +703,9 @@ frappe.views.CommunicationComposer = Class.extend({ return (frappe.model.get_doc(":Print Settings", "Print Settings") || { with_letterhead: 1 }).with_letterhead ? 1 : 0; } - }, + } - set_content: async function() { + async set_content() { if (this.content_set) return; let message = this.txt || ""; @@ -728,9 +729,9 @@ frappe.views.CommunicationComposer = Class.extend({ } await this.dialog.set_value("content", message); - }, + } - get_signature: async function () { + async get_signature() { let signature = frappe.boot.user.email_signature; if (!signature) { @@ -750,7 +751,7 @@ frappe.views.CommunicationComposer = Class.extend({ } return "
" + signature; - }, + } get_earlier_reply() { const last_email = ( @@ -786,7 +787,7 @@ frappe.views.CommunicationComposer = Class.extend({ ${last_email_content}Date: Sat, 17 Apr 2021 12:24:00 +0530 Subject: [PATCH 024/106] fix: sider issue --- frappe/public/js/frappe/views/communication.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 77cc91b4ba..0479ec6d31 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -276,7 +276,7 @@ frappe.views.CommunicationComposer = class { prepend_reply(r.message); }, }); - } + }; } setup_last_edited_communication() { From 1fd08d39606d71b9fc2a3be368e3558d65cf8b36 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 17 Apr 2021 07:31:24 +0530 Subject: [PATCH 025/106] refactor: Jinja hooks - Rename hook from "jenv" to "jinja" - You can now pass the path to the module and all of the methods in that module will be added as methods - You can also pass module path of a method BREAKING CHANGE: Previous use of "jenv" hook won't work anymore --- frappe/hooks.py | 4 ++ frappe/utils/jinja.py | 111 +++++++++------------------------- frappe/utils/jinja_globals.py | 71 ++++++++++++++++++++++ 3 files changed, 102 insertions(+), 84 deletions(-) create mode 100644 frappe/utils/jinja_globals.py diff --git a/frappe/hooks.py b/frappe/hooks.py index 74c538c5df..c47afadf58 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -130,6 +130,10 @@ has_website_permission = { "Address": "frappe.contacts.doctype.address.address.has_website_permission" } +jinja = { + "methods": "frappe.utils.jinja_globals" +} + standard_queries = { "User": "frappe.core.doctype.user.user.user_query" } diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index cd74b2a283..1b2ef9f47f 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -18,13 +18,10 @@ def get_jenv(): set_filters(jenv) jenv.globals.update(get_safe_globals()) - jenv.globals.update(get_jenv_customization('methods')) - jenv.globals.update({ - 'resolve_class': resolve_class, - 'inspect': inspect, - 'web_blocks': web_blocks, - 'web_block': web_block - }) + + methods, filters = get_jinja_hooks('methods') + jenv.globals.update(methods or {}) + jenv.filters.update(filters or {}) frappe.local.jenv = jenv @@ -143,88 +140,34 @@ def set_filters(jenv): if frappe.flags.in_setup_help: return - jenv.filters.update(get_jenv_customization('filters')) - - -def get_jenv_customization(customization_type): - '''Returns a dict with filter/method name as key and definition as value''' +def get_jinja_hooks(): + """Returns a tuple of (methods, filters) each containing a dict of method name and method definition pair.""" import frappe - out = {} if not getattr(frappe.local, "site", None): + return (None, None) + + from types import FunctionType, ModuleType + from inspect import getmembers, isfunction + + def get_obj_dict_from_paths(object_paths): + out = {} + for obj_path in object_paths: + obj = frappe.get_attr(obj_path) + if isinstance(obj, ModuleType): + functions = getmembers(obj, isfunction) + for function_name, function in functions: + out[function_name] = function + elif isinstance(obj, FunctionType): + function_name = obj.__name__ + out[function_name] = obj return out - values = frappe.get_hooks("jenv", {}).get(customization_type) - if not values: - return out + values = frappe.get_hooks("jinja") + methods, filters = values.get("methods", []), values.get("filters", []) - for value in values: - fn_name, fn_string = value.split(":") - out[fn_name] = frappe.get_attr(fn_string) + method_dict = get_obj_dict_from_paths(methods) + filter_dict = get_obj_dict_from_paths(filters) - return out - - -def resolve_class(classes): - import frappe - - if classes is None: - return '' - - if isinstance(classes, frappe.string_types): - return classes - - if isinstance(classes, (list, tuple)): - return ' '.join([resolve_class(c) for c in classes]).strip() - - if isinstance(classes, dict): - return ' '.join([classname for classname in classes if classes[classname]]).strip() - - return classes - - -def inspect(var, render=True): - context = { "var": var } - if render: - html = " {{ var | pprint | e }}" - else: - html = "" - return get_jenv().from_string(html).render(context) - - -def web_block(template, values=None, **kwargs): - options = {"template": template, "values": values} - options.update(kwargs) - return web_blocks([options]) - - -def web_blocks(blocks): - from frappe import throw, _dict - from frappe.website.doctype.web_page.web_page import get_web_blocks_html - - web_blocks = [] - for block in blocks: - if not block.get('template'): - throw('Web Template is not specified') - - doc = _dict({ - 'doctype': 'Web Page Block', - 'web_template': block['template'], - 'web_template_values': block.get('values', {}), - 'add_top_padding': 1, - 'add_bottom_padding': 1, - 'add_container': 1, - 'hide_block': 0, - 'css_class': '' - }) - doc.update(block) - web_blocks.append(doc) - - out = get_web_blocks_html(web_blocks) - - html = out.html - for script in out.scripts: - html += ''.format(script) - - return html + return method_dict, filter_dict diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py new file mode 100644 index 0000000000..e63926a109 --- /dev/null +++ b/frappe/utils/jinja_globals.py @@ -0,0 +1,71 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +from frappe.utils.jinja import get_jenv +import frappe + + +def resolve_class(classes): + if classes is None: + return "" + + if isinstance(classes, frappe.string_types): + return classes + + if isinstance(classes, (list, tuple)): + return " ".join([resolve_class(c) for c in classes]).strip() + + if isinstance(classes, dict): + return " ".join([classname for classname in classes if classes[classname]]).strip() + + return classes + + +def inspect(var, render=True): + context = {"var": var} + if render: + html = "{{ var | pprint | e }}" + else: + return "" + return get_jenv().from_string(html).render(context) + + +def web_block(template, values=None, **kwargs): + options = {"template": template, "values": values} + options.update(kwargs) + return web_blocks([options]) + + +def web_blocks(blocks): + from frappe import throw, _dict + from frappe.website.doctype.web_page.web_page import get_web_blocks_html + + web_blocks = [] + for block in blocks: + if not block.get("template"): + throw("Web Template is not specified") + + doc = _dict( + { + "doctype": "Web Page Block", + "web_template": block["template"], + "web_template_values": block.get("values", {}), + "add_top_padding": 1, + "add_bottom_padding": 1, + "add_container": 1, + "hide_block": 0, + "css_class": "", + } + ) + doc.update(block) + web_blocks.append(doc) + + out = get_web_blocks_html(web_blocks) + + html = out.html + for script in out.scripts: + html += "".format(script) + + return html + From a62ef80cddedec855e592fbe77a4ab7cdede8bad Mon Sep 17 00:00:00 2001 From: Faris AnsariDate: Sat, 17 Apr 2021 07:41:04 +0530 Subject: [PATCH 026/106] fix: Add jinja hook boilerplate --- frappe/utils/boilerplate.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index e59f579f75..80eda0af13 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -190,6 +190,15 @@ app_license = "{app_license}" # automatically create page for each record of this doctype # website_generators = ["Web Page"] +# Jinja +# ---------- + +# add methods and filters to jinja environment +# jinja = {{ +# "methods": "{app_name}.utils.jinja_methods", +# "filters": "{app_name}.utils.jinja_filters" +# }} + # Installation # ------------ From a78fed4ffccbe3faa628eb7d6a25affc9887023e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 17 Apr 2021 07:52:04 +0530 Subject: [PATCH 027/106] fix: Move standard filters to jinja hooks --- frappe/hooks.py | 8 +++++++- frappe/utils/jinja.py | 7 +------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index c47afadf58..1c78d47755 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -131,7 +131,13 @@ has_website_permission = { } jinja = { - "methods": "frappe.utils.jinja_globals" + "methods": "frappe.utils.jinja_globals", + "filters": [ + "frappe.utils.data.global_date_format", + "frappe.utils.markdown", + "frappe.website.utils.get_shade", + "frappe.website.utils.abs_url", + ] } standard_queries = { diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 1b2ef9f47f..6d25f4a405 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -124,18 +124,13 @@ def get_jloader(): def set_filters(jenv): import frappe - from frappe.utils import global_date_format, cint, cstr, flt, markdown - from frappe.website.utils import get_shade, abs_url + from frappe.utils import cint, cstr, flt - jenv.filters["global_date_format"] = global_date_format - jenv.filters["markdown"] = markdown jenv.filters["json"] = frappe.as_json - jenv.filters["get_shade"] = get_shade jenv.filters["len"] = len jenv.filters["int"] = cint jenv.filters["str"] = cstr jenv.filters["flt"] = flt - jenv.filters["abs_url"] = abs_url if frappe.flags.in_setup_help: return From b32db6e329a006c1e0eedd93ceb53930db1753f5 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 17 Apr 2021 16:03:25 +0530 Subject: [PATCH 028/106] fix: method call --- frappe/utils/jinja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 6d25f4a405..42ab267381 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -19,7 +19,7 @@ def get_jenv(): jenv.globals.update(get_safe_globals()) - methods, filters = get_jinja_hooks('methods') + methods, filters = get_jinja_hooks() jenv.globals.update(methods or {}) jenv.filters.update(filters or {}) From 34c1235111f877d669a42ff024d43e72e62b40b6 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 17 Apr 2021 03:53:41 +0530 Subject: [PATCH 029/106] fix: Cannot read property `current` of undefined (cherry picked from commit 01cd2308bbc6a5e7029eecc67dd4ab3ac55dd4e5) --- frappe/public/js/frappe/form/form_viewers.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/public/js/frappe/form/form_viewers.js b/frappe/public/js/frappe/form/form_viewers.js index 3d488e4729..964576ef8a 100644 --- a/frappe/public/js/frappe/form/form_viewers.js +++ b/frappe/public/js/frappe/form/form_viewers.js @@ -7,6 +7,11 @@ frappe.ui.form.FormViewers = class FormViewers { refresh() { let users = this.frm.get_docinfo()['viewers']; + if (!users || !users.current || !users.current.length) { + this.parent.empty(); + return; + } + let currently_viewing = users.current.filter(user => user != frappe.session.user); let avatar_group = frappe.avatar_group(currently_viewing, 5, {'align': 'left', 'overlap': true}); this.parent.empty().append(avatar_group); From edef0a467d2ad993a68bfd78e2ad844a210851ff Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 17 Apr 2021 18:31:43 +0200 Subject: [PATCH 030/106] fix: test_token_cache PermissionError --- frappe/integrations/doctype/token_cache/test_token_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/token_cache/test_token_cache.py b/frappe/integrations/doctype/token_cache/test_token_cache.py index 73c9f38fce..7aa069647d 100644 --- a/frappe/integrations/doctype/token_cache/test_token_cache.py +++ b/frappe/integrations/doctype/token_cache/test_token_cache.py @@ -13,7 +13,7 @@ class TestTokenCache(unittest.TestCase): def setUp(self): self.token_cache = frappe.get_last_doc('Token Cache') self.token_cache.update({'connected_app': frappe.get_last_doc('Connected App').name}) - self.token_cache.save() + self.token_cache.save(ignore_permissions=True) def test_get_auth_header(self): self.token_cache.get_auth_header() From d927d393eeecd825189d6364af5ffbefcc912341 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 17 Apr 2021 23:09:16 +0530 Subject: [PATCH 031/106] fix: Add autocompletion items in Server Script - API to add autocompletion items in Code field --- frappe/cache_manager.py | 2 +- .../doctype/server_script/server_script.js | 6 +++ .../doctype/server_script/server_script.py | 23 ++++++++- frappe/public/js/frappe/form/controls/code.js | 50 +++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index bad879d2fa..4e0fe0cf44 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -18,7 +18,7 @@ global_cache_keys = ("app_hooks", "installed_apps", 'all_apps', 'scheduler_events', 'time_zone', 'webhooks', 'active_domains', 'active_modules', 'assignment_rule', 'server_script_map', 'wkhtmltopdf_version', 'domain_restricted_doctypes', 'domain_restricted_pages', 'information_schema:counts', - 'sitemap_routes', 'db_tables') + doctype_map_keys + 'sitemap_routes', 'db_tables', 'server_script_autocompletion_items') + doctype_map_keys user_cache_keys = ("bootinfo", "user_recent", "roles", "user_doc", "lang", "defaults", "user_permissions", "home_page", "linked_with", diff --git a/frappe/core/doctype/server_script/server_script.js b/frappe/core/doctype/server_script/server_script.js index 95a63780f8..e12200b6fc 100644 --- a/frappe/core/doctype/server_script/server_script.js +++ b/frappe/core/doctype/server_script/server_script.js @@ -9,6 +9,12 @@ frappe.ui.form.on('Server Script', { if (frm.doc.script_type != 'Scheduler Event') { frm.dashboard.hide(); } + + frm.call('get_autocompletion_items') + .then(r => r.message) + .then(items => { + frm.set_df_property('script', 'autocompletions', items) + }); }, setup_help(frm) { diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index 8838d9e954..6a8eb59c3a 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import ast +from types import FunctionType, ModuleType from typing import Dict, List import frappe from frappe.model.document import Document -from frappe.utils.safe_exec import safe_exec +from frappe.utils.safe_exec import get_safe_globals, safe_exec, NamespaceDict from frappe import _ @@ -122,6 +123,26 @@ class ServerScript(Document): if locals["conditions"]: return locals["conditions"] + @frappe.whitelist() + def get_autocompletion_items(self): + def get_keys(obj): + out = [] + for key in obj: + if key.startswith('_'): + continue + value = obj[key] + if isinstance(value, (FunctionType, ModuleType)): + out.append(key) + elif isinstance(value, (NamespaceDict, dict)): + out += [f'{key}.{subkey}' for subkey in get_keys(value)] + return out + + items = frappe.cache().get_value('server_script_autocompletion_items') + if not items: + items = get_keys(get_safe_globals()) + frappe.cache().set_value('server_script_autocompletion_items', items) + return items + @frappe.whitelist() def setup_scheduler_events(script_name, frequency): diff --git a/frappe/public/js/frappe/form/controls/code.js b/frappe/public/js/frappe/form/controls/code.js index eec450b390..8d2609b836 100644 --- a/frappe/public/js/frappe/form/controls/code.js +++ b/frappe/public/js/frappe/form/controls/code.js @@ -31,6 +31,56 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({ const input_value = this.get_input_value(); this.parse_validate_and_set_in_model(input_value); }, 300)); + + // setup autocompletion when it is set the first time + Object.defineProperty(this.df, 'autocompletions', { + get() { + return this._autocompletions || []; + }, + set: (value) => { + this.setup_autocompletion(); + this.df._autocompletions = value; + } + }); + }, + + setup_autocompletion() { + if (this._autocompletion_setup) return; + + const ace = window.ace; + const get_autocompletions = () => this.df.autocompletions; + + ace.config.loadModule("ace/ext/language_tools", langTools => { + this.editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true + }); + + let completer = { + getCompletions: function(editor, session, pos, prefix, callback) { + if (prefix.length === 0) { + callback(null, []); + return; + } + let autocompletions = get_autocompletions(); + if (autocompletions.length) { + callback( + null, + autocompletions.map(a => ({ + name: 'frappe', + value: a, + score: 100, + meta: 'Frappe API' + })) + ); + } + } + } + langTools.addCompleter(completer); + }); + + this._autocompletion_setup = true; }, refresh_height() { From 75822a323a2553c2f2190e12269fd0e36ed1cecc Mon Sep 17 00:00:00 2001 From: mustafaelagamey Date: Sun, 18 Apr 2021 06:46:19 +0200 Subject: [PATCH 032/106] fix: Remove `cmd` only if exist (#12886) * only remove cmd if exist When calling this function from backend this line raises key error as there's no such key called cmd * style: Simplify code Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/desk/treeview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index 12fdb0dadc..d479b71b52 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -66,7 +66,7 @@ def add_node(): doc.save() def make_tree_args(**kwarg): - del kwarg['cmd'] + kwarg.pop('cmd', None) doctype = kwarg['doctype'] parent_field = 'parent_' + doctype.lower().replace(' ', '_') From 762f74b590ff21a9629485221d860e75e967b5b6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 18 Apr 2021 10:23:41 +0530 Subject: [PATCH 033/106] fix: Kanban board sync issue (backport #12874) (#12889) --- frappe/public/js/frappe/views/kanban/kanban_board.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index f563f64cb4..bbc2051e4c 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -306,6 +306,7 @@ frappe.provide("frappe.views"); store.on('change:cur_list', setup_restore_columns); store.on('change:columns', setup_restore_columns); store.on('change:empty_state', show_empty_state); + fluxify.doAction('update_order'); } function prepare() { From 2bf968e753bbd35cf9cc37399ffc34adc67c093b Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Sat, 17 Apr 2021 22:54:12 -0600 Subject: [PATCH 034/106] fix: Make strings translatable (#12877) Make strings translatable. --- .../desk/doctype/notification_settings/notification_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/notification_settings/notification_settings.js b/frappe/desk/doctype/notification_settings/notification_settings.js index 88dc145be2..cc2fd95204 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.js +++ b/frappe/desk/doctype/notification_settings/notification_settings.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Notification Settings', { refresh: (frm) => { if (frappe.user.has_role('System Manager')) { - frm.add_custom_button('Go to Notification Settings List', () => { + frm.add_custom_button(__('Go to Notification Settings List'), () => { frappe.set_route('List', 'Notification Settings'); }); } From 0d87ad2133e730c326cc0b7b81565ff5b251b343 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Sun, 18 Apr 2021 11:58:20 +0530 Subject: [PATCH 035/106] fix(minor): Add a delete trigger in grid, and use it to refresh labels in Website Settings --- frappe/public/js/frappe/form/grid.js | 6 +++++- .../website_settings/website_settings.js | 18 +++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index b211476e63..86feefed7a 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -194,7 +194,10 @@ export default class Grid { } tasks.push(() => { - if (dirty) this.refresh(); + if (dirty) { + this.refresh(); + this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); + } }); frappe.run_serially(tasks); @@ -210,6 +213,7 @@ export default class Grid { this.frm.doc[this.df.fieldname] = []; $(this.parent).find('.rows').empty(); this.grid_rows = []; + this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').prop('checked', 0); this.refresh(); diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index 422deb244e..2f15b4c00e 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -33,20 +33,12 @@ frappe.ui.form.on('Website Settings', { frm.fields_dict.top_bar_items.grid.update_docfield_property( 'parent_label', 'options', frm.events.get_parent_options(frm, "top_bar_items") ); - - if ($(frm.fields_dict.top_bar_items.grid.wrapper).find(".grid-row-open")) { - frm.fields_dict.top_bar_items.grid.refresh(); - } }, set_parent_label_options_footer: function(frm) { frm.fields_dict.footer_items.grid.update_docfield_property( - 'parent_label', 'options', frm.events.get_parent_options(frm, "top_bar_items") + 'parent_label', 'options', frm.events.get_parent_options(frm, "footer_items") ); - - if ($(frm.fields_dict.footer_items.grid.wrapper).find(".grid-row-open")) { - frm.fields_dict.footer_items.grid.refresh(); - } }, authorize_api_indexing_access: function(frm) { @@ -122,10 +114,18 @@ frappe.ui.form.on('Website Settings', { }); frappe.ui.form.on('Top Bar Item', { + top_bar_items_delete(frm) { + frm.events.set_parent_label_options(frm); + }, + footer_items_add(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'right', 0); }, + footer_items_delete(frm) { + frm.events.set_parent_label_options_footer(frm); + }, + parent_label: function(frm, doctype, name) { frm.events.set_parent_options(frm, doctype, name); }, From 33b12d91f988ae7dfb030d1bd30eeeb5acecc48e Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 18 Apr 2021 12:38:19 +0530 Subject: [PATCH 036/106] fix: cannot read property `doc` of undefined (#12891) --- .../doctype/communication/communication_list.js | 2 +- frappe/public/js/frappe/views/communication.js | 13 ++++++++----- frappe/public/js/frappe/views/inbox/inbox_view.js | 4 +--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/communication/communication_list.js b/frappe/core/doctype/communication/communication_list.js index 454897b865..315b74a39c 100644 --- a/frappe/core/doctype/communication/communication_list.js +++ b/frappe/core/doctype/communication/communication_list.js @@ -20,6 +20,6 @@ frappe.listview_settings['Communication'] = { }, primary_action: function() { - new frappe.views.CommunicationComposer({ doc: {} }); + new frappe.views.CommunicationComposer(); } }; diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 0479ec6d31..6501018c88 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -7,6 +7,10 @@ const separator_element = ' ---'; frappe.views.CommunicationComposer = class { constructor(opts) { $.extend(this, opts); + if (!this.doc) { + this.doc = this.frm && this.frm.doc || {}; + } + this.make(); } @@ -269,7 +273,7 @@ frappe.views.CommunicationComposer = class { method: 'frappe.email.doctype.email_template.email_template.get_email_template', args: { template_name: email_template, - doc: me.frm.doc, + doc: me.doc, _lang: me.dialog.get_value("language_sel") }, callback(r) { @@ -382,11 +386,10 @@ frappe.views.CommunicationComposer = class { } setup_print_language() { - const doc = this.frm && this.frm.doc; const fields = this.dialog.fields_dict; //Load default print language from doctype - this.lang_code = doc.language + this.lang_code = this.doc.language || this.get_print_format().default_print_language || frappe.boot.lang; @@ -612,7 +615,7 @@ frappe.views.CommunicationComposer = class { send_email(btn, form_values, selected_attachments, print_html, print_format) { const me = this; - me.dialog.hide(); + this.dialog.hide(); if (!form_values.recipients) { frappe.msgprint(__("Enter Email Recipient(s)")); @@ -625,7 +628,7 @@ frappe.views.CommunicationComposer = class { } - if (this.frm && !frappe.model.can_email(me.doc.doctype, this.frm)) { + if (this.frm && !frappe.model.can_email(this.doc.doctype, this.frm)) { frappe.msgprint(__("You are not allowed to send emails related to this document")); return; } diff --git a/frappe/public/js/frappe/views/inbox/inbox_view.js b/frappe/public/js/frappe/views/inbox/inbox_view.js index 1085e93e6c..8b53bd49a9 100644 --- a/frappe/public/js/frappe/views/inbox/inbox_view.js +++ b/frappe/public/js/frappe/views/inbox/inbox_view.js @@ -204,9 +204,7 @@ frappe.views.InboxView = class InboxView extends frappe.views.ListView { }; frappe.new_doc('Email Account'); } else { - new frappe.views.CommunicationComposer({ - doc: {} - }); + new frappe.views.CommunicationComposer(); } } }; From c8763859aec2332040b2e0392cfcf5a5fdef327c Mon Sep 17 00:00:00 2001 From: Sagar VoraDate: Sun, 18 Apr 2021 13:22:43 +0530 Subject: [PATCH 037/106] test: multiple cypress fixes --- cypress/integration/relative_time_filters.js | 3 --- cypress/integration/table_multiselect.js | 2 +- frappe/commands/utils.py | 11 +++++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cypress/integration/relative_time_filters.js b/cypress/integration/relative_time_filters.js index 80e6387d99..cbb0524c24 100644 --- a/cypress/integration/relative_time_filters.js +++ b/cypress/integration/relative_time_filters.js @@ -1,7 +1,4 @@ context('Relative Timeframe', () => { - beforeEach(() => { - cy.login(); - }); before(() => { cy.login(); cy.visit('/app/website'); diff --git a/cypress/integration/table_multiselect.js b/cypress/integration/table_multiselect.js index faa72d63a5..25cab78ba2 100644 --- a/cypress/integration/table_multiselect.js +++ b/cypress/integration/table_multiselect.js @@ -1,5 +1,5 @@ context('Table MultiSelect', () => { - beforeEach(() => { + before(() => { cy.login(); }); diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 5ff66171fc..a203c8c6d9 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -11,7 +11,7 @@ import click import frappe from frappe.commands import get_site, pass_context from frappe.exceptions import SiteNotSpecifiedError -from frappe.utils import get_bench_path, update_progress_bar +from frappe.utils import get_bench_path, update_progress_bar, cint @click.command('build') @@ -567,11 +567,14 @@ def run_ui_tests(context, app, headless=False): node_bin = subprocess.getoutput("npm bin") cypress_path = "{0}/cypress".format(node_bin) - plugin_path = "{0}/cypress-file-upload".format(node_bin) + plugin_path = "{0}/../cypress-file-upload".format(node_bin) # check if cypress in path...if not, install it. - if not (os.path.exists(cypress_path) or os.path.exists(plugin_path)) \ - or not subprocess.getoutput("npm view cypress version").startswith("6."): + if not ( + os.path.exists(cypress_path) + and os.path.exists(plugin_path) + and cint(subprocess.getoutput("npm view cypress version")[:1]) >= 6 + ): # install cypress click.secho("Installing Cypress...", fg="yellow") frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile") From d309343c6fa43b8a2a28fa615a534647a6c3e069 Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Sun, 18 Apr 2021 18:56:45 +0530 Subject: [PATCH 038/106] feat(hooks): auth hooks hook for request authentication --- frappe/api.py | 33 +++++++++++++++------------------ frappe/utils/boilerplate.py | 7 +++++++ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index 6a09b795b0..3a1be2593e 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -149,24 +149,17 @@ def get_request_form_data(): return frappe.parse_json(data) def validate_auth(): - if frappe.get_request_header("Authorization") is None: - return - VALID_AUTH_PREFIX_TYPES = ['basic', 'bearer', 'token'] VALID_AUTH_PREFIX_STRING = ", ".join(VALID_AUTH_PREFIX_TYPES).title() authorization_header = frappe.get_request_header("Authorization", str()).split(" ") authorization_type = authorization_header[0].lower() - if len(authorization_header) == 1: - frappe.throw(_('Invalid Authorization headers, add a token with a prefix from one of the following: {0}.').format(VALID_AUTH_PREFIX_STRING), frappe.InvalidAuthorizationHeader) - - if authorization_type == "bearer": + if len(authorization_header) == 2: validate_oauth(authorization_header) - elif authorization_type in VALID_AUTH_PREFIX_TYPES: validate_auth_via_api_keys(authorization_header) - else: - frappe.throw(_('Invalid Authorization Type {0}, must be one of {1}.').format(authorization_type, VALID_AUTH_PREFIX_STRING), frappe.InvalidAuthorizationPrefix) + + validate_auth_via_hooks() def validate_oauth(authorization_header): @@ -192,14 +185,13 @@ def validate_oauth(authorization_header): try: required_scopes = frappe.db.get_value("OAuth Bearer Token", token, "scopes").split(get_url_delimiter()) + valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) + + if valid: + frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) + frappe.local.form_dict = form_dict except AttributeError: - frappe.throw(_("Invalid Bearer token, please provide a valid access token with prefix 'Bearer'."), frappe.InvalidAuthorizationToken) - - valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) - - if valid: - frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) - frappe.local.form_dict = form_dict + pass def validate_auth_via_api_keys(authorization_header): @@ -222,7 +214,7 @@ def validate_auth_via_api_keys(authorization_header): except binascii.Error: frappe.throw(_("Failed to decode token, please provide a valid base64-encoded token."), frappe.InvalidAuthorizationToken) except (AttributeError, TypeError, ValueError): - frappe.throw(_("Invalid token, please provide a valid token with prefix 'Basic' or 'Token'."), frappe.InvalidAuthorizationToken) + pass @@ -248,3 +240,8 @@ def validate_api_key_secret(api_key, api_secret, frappe_authorization_source=Non if frappe.local.login_manager.user in ('', 'Guest'): frappe.set_user(user) frappe.local.form_dict = form_dict + + +def validate_auth_via_hooks(): + for auth_hook in frappe.get_hooks('auth_hooks', []): + frappe.get_attr(auth_hook)() diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index e59f579f75..ffcb64cff2 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -303,6 +303,13 @@ user_data_fields = [ }} ] +# Authentication and authorization +# -------------------------------- + +# auth_hooks = [ +# "{app_name}.auth.validate" +# ] + """ desktop_template = """# -*- coding: utf-8 -*- From c16584bbf186183f970583d3793f2e7e670f60f8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 19 Apr 2021 10:45:54 +0530 Subject: [PATCH 039/106] chore: Upgrade frappe-charts to rc13 (#12896) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3c8da66242..6e82890617 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "driver.js": "^0.9.8", "express": "^4.17.1", "fast-deep-equal": "^2.0.1", - "frappe-charts": "^2.0.0-rc11", + "frappe-charts": "^2.0.0-rc13", "frappe-datatable": "^1.15.3", "frappe-gantt": "^0.5.0", "fuse.js": "^3.4.6", diff --git a/yarn.lock b/yarn.lock index 4f6f62ac0a..8ac348011d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2699,10 +2699,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -frappe-charts@^2.0.0-rc11: - version "2.0.0-rc11" - resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc11.tgz#0724fa0d43593362c075c3805ebbbe1a608fcef7" - integrity sha512-DY3tThT1lNGcJlRMOtIhnILtSm5h1iKysWhZAyj7yrGiOnOWbZpYx/NZzXZYwtRrWwMlYiLX2ylV76qo31ONsg== +frappe-charts@^2.0.0-rc13: + version "2.0.0-rc13" + resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc13.tgz#fdb251d7ae311c41e38f90a3ae108070ec6b9072" + integrity sha512-Bv7IfllIrjRbKWHn5b769dOSenqdBixAr6m5kurf8ZUOJSLOgK4HOXItJ7BA8n9PvviH9/k5DaloisjLM2Bm1w== frappe-datatable@^1.15.3: version "1.15.3" From eed1b6961edd8558729642be36410817cb470fe0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 10:51:01 +0530 Subject: [PATCH 040/106] chore: Upgrade frappe-charts to rc13 (backport #12896) (#12897) (cherry picked from commit c16584bbf186183f970583d3793f2e7e670f60f8) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3c8da66242..6e82890617 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "driver.js": "^0.9.8", "express": "^4.17.1", "fast-deep-equal": "^2.0.1", - "frappe-charts": "^2.0.0-rc11", + "frappe-charts": "^2.0.0-rc13", "frappe-datatable": "^1.15.3", "frappe-gantt": "^0.5.0", "fuse.js": "^3.4.6", diff --git a/yarn.lock b/yarn.lock index 4f6f62ac0a..8ac348011d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2699,10 +2699,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -frappe-charts@^2.0.0-rc11: - version "2.0.0-rc11" - resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc11.tgz#0724fa0d43593362c075c3805ebbbe1a608fcef7" - integrity sha512-DY3tThT1lNGcJlRMOtIhnILtSm5h1iKysWhZAyj7yrGiOnOWbZpYx/NZzXZYwtRrWwMlYiLX2ylV76qo31ONsg== +frappe-charts@^2.0.0-rc13: + version "2.0.0-rc13" + resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc13.tgz#fdb251d7ae311c41e38f90a3ae108070ec6b9072" + integrity sha512-Bv7IfllIrjRbKWHn5b769dOSenqdBixAr6m5kurf8ZUOJSLOgK4HOXItJ7BA8n9PvviH9/k5DaloisjLM2Bm1w== frappe-datatable@^1.15.3: version "1.15.3" From f617bfeba6910f10fec7a03530f0ca848ee3f715 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 19 Apr 2021 12:51:48 +0530 Subject: [PATCH 041/106] fix: update dependencies --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8cbe0e800b..91f235159f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,14 +2,14 @@ Babel~=2.9.0 beautifulsoup4~=4.9.3 bleach-whitelist~=0.0.11 bleach~=3.3.0 -boto3~=1.17.48 +boto3~=1.17.53 braintree~=4.8.0 chardet~=4.0.0 Click~=7.1.2 coverage~=5.5 croniter~=1.0.11 cryptography~=3.4.7 -dropbox~=11.6.0 +dropbox~=11.7.0 email-reply-parser~=0.5.12 Faker~=8.1.0 future==0.18.2 @@ -19,7 +19,7 @@ GitPython~=3.1.14 google-api-python-client~=2.2.0 google-auth-httplib2~=0.1.0 google-auth-oauthlib~=0.4.4 -google-auth~=1.28.1 +google-auth~=1.29.0 googlemaps~=4.4.5 gunicorn~=20.1.0 html2text==2020.1.16 @@ -38,7 +38,7 @@ passlib~=1.7.4 paytmchecksum~=1.7.0 pdfkit~=0.6.1 Pillow~=8.2.0 -premailer~=3.7.0 +premailer~=3.8.0 psutil~=5.8.0 psycopg2-binary~=2.8.6 pyasn1~=0.4.8 From 7cff552dd43fe3905185a18c460a1fde107909f7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 13:11:18 +0530 Subject: [PATCH 042/106] fix(minor): Add a delete trigger in grid, and use it to refresh labels in Website Settings (backport #12890) (#12900) (cherry picked from commit 0d87ad2133e730c326cc0b7b81565ff5b251b343) Co-authored-by: Rushabh Mehta --- frappe/public/js/frappe/form/grid.js | 6 +++++- .../website_settings/website_settings.js | 18 +++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index b211476e63..86feefed7a 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -194,7 +194,10 @@ export default class Grid { } tasks.push(() => { - if (dirty) this.refresh(); + if (dirty) { + this.refresh(); + this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); + } }); frappe.run_serially(tasks); @@ -210,6 +213,7 @@ export default class Grid { this.frm.doc[this.df.fieldname] = []; $(this.parent).find('.rows').empty(); this.grid_rows = []; + this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype); this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').prop('checked', 0); this.refresh(); diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index 422deb244e..2f15b4c00e 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -33,20 +33,12 @@ frappe.ui.form.on('Website Settings', { frm.fields_dict.top_bar_items.grid.update_docfield_property( 'parent_label', 'options', frm.events.get_parent_options(frm, "top_bar_items") ); - - if ($(frm.fields_dict.top_bar_items.grid.wrapper).find(".grid-row-open")) { - frm.fields_dict.top_bar_items.grid.refresh(); - } }, set_parent_label_options_footer: function(frm) { frm.fields_dict.footer_items.grid.update_docfield_property( - 'parent_label', 'options', frm.events.get_parent_options(frm, "top_bar_items") + 'parent_label', 'options', frm.events.get_parent_options(frm, "footer_items") ); - - if ($(frm.fields_dict.footer_items.grid.wrapper).find(".grid-row-open")) { - frm.fields_dict.footer_items.grid.refresh(); - } }, authorize_api_indexing_access: function(frm) { @@ -122,10 +114,18 @@ frappe.ui.form.on('Website Settings', { }); frappe.ui.form.on('Top Bar Item', { + top_bar_items_delete(frm) { + frm.events.set_parent_label_options(frm); + }, + footer_items_add(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'right', 0); }, + footer_items_delete(frm) { + frm.events.set_parent_label_options_footer(frm); + }, + parent_label: function(frm, doctype, name) { frm.events.set_parent_options(frm, doctype, name); }, From 557235471ed3540f12d5b9ac883e682f9dbf3daa Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 19 Apr 2021 13:39:39 +0530 Subject: [PATCH 043/106] fix: remove unused variables --- frappe/api.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index 3a1be2593e..59f14b54c8 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -1,12 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import base64 import binascii import json - -from six.moves.urllib.parse import urlencode, urlparse +from urllib.parse import urlencode, urlparse import frappe import frappe.client @@ -14,6 +12,7 @@ import frappe.handler from frappe import _ from frappe.utils.response import build_response + def handle(): """ Handler for `/api` methods @@ -38,7 +37,6 @@ def handle(): `/api/resource/{doctype}/{name}?run_method={method}` will run a whitelisted controller method """ - validate_auth() parts = frappe.request.path[1:].split("/",3) @@ -116,7 +114,7 @@ def handle(): frappe.local.form_dict['fields'] = json.loads(frappe.local.form_dict['fields']) frappe.local.form_dict.setdefault('limit_page_length', 20) frappe.local.response.update({ - "data": frappe.call( + "data": frappe.call( frappe.client.get_list, doctype, **frappe.local.form_dict @@ -140,6 +138,7 @@ def handle(): return build_response("json") + def get_request_form_data(): if frappe.local.form_dict.data is None: data = frappe.safe_decode(frappe.local.request.get_data()) @@ -148,12 +147,9 @@ def get_request_form_data(): return frappe.parse_json(data) -def validate_auth(): - VALID_AUTH_PREFIX_TYPES = ['basic', 'bearer', 'token'] - VALID_AUTH_PREFIX_STRING = ", ".join(VALID_AUTH_PREFIX_TYPES).title() +def validate_auth(): authorization_header = frappe.get_request_header("Authorization", str()).split(" ") - authorization_type = authorization_header[0].lower() if len(authorization_header) == 2: validate_oauth(authorization_header) @@ -170,8 +166,8 @@ def validate_oauth(authorization_header): authorization_header (list of str): The 'Authorization' header containing the prefix and token """ - from frappe.oauth import get_url_delimiter from frappe.integrations.oauth2 import get_oauth_server + from frappe.oauth import get_url_delimiter form_dict = frappe.local.form_dict token = authorization_header[1] @@ -185,14 +181,14 @@ def validate_oauth(authorization_header): try: required_scopes = frappe.db.get_value("OAuth Bearer Token", token, "scopes").split(get_url_delimiter()) - valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) - - if valid: - frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) - frappe.local.form_dict = form_dict except AttributeError: pass + valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) + if valid: + frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) + frappe.local.form_dict = form_dict + def validate_auth_via_api_keys(authorization_header): """ @@ -217,7 +213,6 @@ def validate_auth_via_api_keys(authorization_header): pass - def validate_api_key_secret(api_key, api_secret, frappe_authorization_source=None): """frappe_authorization_source to provide api key and secret for a doctype apart from User""" doctype = frappe_authorization_source or 'User' From 1600d852796a48b733f832cad0e67faa0e69ae66 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 19 Apr 2021 14:15:30 +0530 Subject: [PATCH 044/106] fix: Typo in get_all_language_with_name (#12902) --- frappe/translate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/translate.py b/frappe/translate.py index 62ee733f5f..a65a1c28c1 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -827,7 +827,7 @@ def get_all_languages(with_language_name=False): return frappe.db.sql_list('select name from tabLanguage') def get_all_language_with_name(): - return frappe.db.get_all('language', ['language_code', 'language_name']) + return frappe.db.get_all('Language', ['language_code', 'language_name']) if not frappe.db: frappe.connect() From 6587744b096b21c8d316167f0115abe1a52f3600 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 14:29:58 +0530 Subject: [PATCH 045/106] fix: Typo in get_all_language_with_name (backport #12902) (#12903) (cherry picked from commit 1600d852796a48b733f832cad0e67faa0e69ae66) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/translate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/translate.py b/frappe/translate.py index 62ee733f5f..a65a1c28c1 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -827,7 +827,7 @@ def get_all_languages(with_language_name=False): return frappe.db.sql_list('select name from tabLanguage') def get_all_language_with_name(): - return frappe.db.get_all('language', ['language_code', 'language_name']) + return frappe.db.get_all('Language', ['language_code', 'language_name']) if not frappe.db: frappe.connect() From fe73a0b22af3b29eb60f31ba5cc0b73c1bf7d828 Mon Sep 17 00:00:00 2001 From: leela Date: Mon, 19 Apr 2021 14:43:44 +0530 Subject: [PATCH 046/106] refactor: removed unused code --- frappe/www/login.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/frappe/www/login.py b/frappe/www/login.py index 76b232f8ee..1ce25a81d9 100644 --- a/frappe/www/login.py +++ b/frappe/www/login.py @@ -95,14 +95,6 @@ def login_via_frappe(code, state): def login_via_office365(code, state): login_via_oauth2_id_token("office_365", code, state, decoder=decoder_compat) -@frappe.whitelist(allow_guest=True) -def login_oauth_user(data=None, provider=None, state=None, email_id=None, key=None, generate_login_token=False): - if not ((data and provider and state) or (email_id and key)): - frappe.respond_as_web_page(_("Invalid Request"), _("Missing parameters for login"), http_status_code=417) - return - - _login_oauth_user(data, provider, state, email_id, key, generate_login_token) - @frappe.whitelist(allow_guest=True) def login_via_token(login_token): sid = frappe.cache().get_value("login_token:{0}".format(login_token), expires=True) From 1c2d69fbe72aae0f4c146096ef4732fed624ecff Mon Sep 17 00:00:00 2001 From: leela Date: Mon, 19 Apr 2021 14:45:38 +0530 Subject: [PATCH 047/106] fix: remove the token validation check Let token be part of state to make state dynamic. But there is no need to have validation for token. --- frappe/utils/oauth.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frappe/utils/oauth.py b/frappe/utils/oauth.py index 6596701ee3..6a92737a0d 100644 --- a/frappe/utils/oauth.py +++ b/frappe/utils/oauth.py @@ -64,8 +64,6 @@ def get_oauth2_authorize_url(provider, redirect_to): state = { "site": frappe.utils.get_url(), "token": frappe.generate_hash(), "redirect_to": redirect_to } - frappe.cache().set_value("{0}:{1}".format(provider, state["token"]), True, expires_in_sec=120) - # relative to absolute url data = { "redirect_uri": get_redirect_uri(provider), @@ -176,11 +174,6 @@ def login_oauth_user(data=None, provider=None, state=None, email_id=None, key=No frappe.respond_as_web_page(_("Invalid Request"), _("Token is missing"), http_status_code=417) return - token = frappe.cache().get_value("{0}:{1}".format(provider, state["token"]), expires=True) - if not token: - frappe.respond_as_web_page(_("Invalid Request"), _("Invalid Token"), http_status_code=417) - return - user = get_email(data) if not user: From 6ecbf55de5f5f050248432726dcd0e9e5d58d27f Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 19 Apr 2021 15:12:00 +0530 Subject: [PATCH 048/106] fix: do not empty viewers parent on form refresh --- frappe/public/js/frappe/form/form.js | 1 + frappe/public/js/frappe/form/toolbar.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index ef728e730e..de9331a726 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -360,6 +360,7 @@ frappe.ui.form.Form = class FrappeForm { grid_obj.grid.grid_pagination.go_to_page(1, true); }); frappe.ui.form.close_grid_form(); + this.viewers && this.viewers.parent.empty(); this.docname = docname; this.setup_docinfo_change_listener(); } diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 145b8d3eed..22787b70c1 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -211,7 +211,6 @@ frappe.ui.form.Toolbar = class Toolbar { make_viewers() { if (this.frm.viewers) { - this.frm.viewers.parent.empty(); return; } this.frm.viewers = new frappe.ui.form.FormViewers({ From 8c74df6cc3b5bf4b1664b6ba76f38d9fc8d6b19f Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Mon, 19 Apr 2021 15:18:15 +0530 Subject: [PATCH 049/106] fix: duplicate validate_auth calls --- frappe/api.py | 8 ++++---- frappe/handler.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index 59f14b54c8..4117c49333 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -181,13 +181,13 @@ def validate_oauth(authorization_header): try: required_scopes = frappe.db.get_value("OAuth Bearer Token", token, "scopes").split(get_url_delimiter()) + valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) + if valid: + frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) + frappe.local.form_dict = form_dict except AttributeError: pass - valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes) - if valid: - frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user")) - frappe.local.form_dict = form_dict def validate_auth_via_api_keys(authorization_header): diff --git a/frappe/handler.py b/frappe/handler.py index 82c1ea65c6..1897abe019 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -24,7 +24,7 @@ ALLOWED_MIMETYPES = ('image/png', 'image/jpeg', 'application/pdf', 'application/ def handle(): """handle request""" - validate_auth() + cmd = frappe.local.form_dict.cmd data = None From 1d0e72834d3f67ec84089190a32ddb28b53963e5 Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Mon, 19 Apr 2021 15:54:16 +0530 Subject: [PATCH 050/106] fix: remove unused imports --- frappe/api.py | 5 +++-- frappe/app.py | 1 + frappe/handler.py | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index 4117c49333..4a120f228a 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -37,8 +37,6 @@ def handle(): `/api/resource/{doctype}/{name}?run_method={method}` will run a whitelisted controller method """ - validate_auth() - parts = frappe.request.path[1:].split("/",3) call = doctype = name = None @@ -149,6 +147,9 @@ def get_request_form_data(): def validate_auth(): + """ + Authenticate and sets user for the request. + """ authorization_header = frappe.get_request_header("Authorization", str()).split(" ") if len(authorization_header) == 2: diff --git a/frappe/app.py b/frappe/app.py index 607479ad52..c9e993a853 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -56,6 +56,7 @@ def application(request): frappe.recorder.record() frappe.monitor.start() frappe.rate_limiter.apply() + frappe.api.validate_auth() if request.method == "OPTIONS": response = Response() diff --git a/frappe/handler.py b/frappe/handler.py index 1897abe019..a38feb90fa 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -9,7 +9,6 @@ import frappe import frappe.utils import frappe.sessions from frappe.utils import cint -from frappe.api import validate_auth from frappe import _, is_whitelisted from frappe.utils.response import build_response from frappe.utils.csvutils import build_csv_response From 10d9611e6a0ae7983199628a651623f1a115c00e Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 19 Apr 2021 16:05:01 +0530 Subject: [PATCH 051/106] fix: Grid Form buttons Insert Above, Insert Below not hidden when cannot_add_rows is true --- frappe/public/js/frappe/form/grid_row.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 5e3a2b8ccd..0a88beaa37 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -555,6 +555,15 @@ export default class GridRow { this.grid_form.render(); this.row.toggle(false); // this.form_panel.toggle(true); + + if (this.grid.cannot_add_rows || (this.grid.df && this.grid.df.cannot_add_rows)) { + this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') + .addClass('hidden') + } else { + this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') + .removeClass('hidden') + } + frappe.dom.freeze("", "dark"); if (cur_frm) cur_frm.cur_grid = this; this.wrapper.addClass("grid-row-open"); From ccb76ce56bceaca3cbe72e932ec01f681b685758 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 19 Apr 2021 16:58:26 +0530 Subject: [PATCH 052/106] fix: sider fix --- frappe/public/js/frappe/form/grid_row.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 0a88beaa37..e0fe1b3b54 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -558,10 +558,10 @@ export default class GridRow { if (this.grid.cannot_add_rows || (this.grid.df && this.grid.df.cannot_add_rows)) { this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') - .addClass('hidden') + .addClass('hidden'); } else { this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') - .removeClass('hidden') + .removeClass('hidden'); } frappe.dom.freeze("", "dark"); From 3b5b908d8a9bfda328517450f3922aee6d7900a5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:20:54 +0530 Subject: [PATCH 053/106] =?UTF-8?q?fix:=20Grid=20Form=20buttons=20Insert?= =?UTF-8?q?=20Above,=20Insert=20Below=20not=20hidden=20when=20can=E2=80=A6?= =?UTF-8?q?=20(backport=20#12906)=20(#12907)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: shariquerik --- frappe/public/js/frappe/form/grid_row.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 5e3a2b8ccd..e0fe1b3b54 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -555,6 +555,15 @@ export default class GridRow { this.grid_form.render(); this.row.toggle(false); // this.form_panel.toggle(true); + + if (this.grid.cannot_add_rows || (this.grid.df && this.grid.df.cannot_add_rows)) { + this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') + .addClass('hidden'); + } else { + this.wrapper.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row') + .removeClass('hidden'); + } + frappe.dom.freeze("", "dark"); if (cur_frm) cur_frm.cur_grid = this; this.wrapper.addClass("grid-row-open"); From 47228e65125084dede270e85dfbc201a8f52d479 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:23:51 +0530 Subject: [PATCH 054/106] fix: do not empty viewers parent on form refresh (backport #12905) (#12908) (cherry picked from commit 6ecbf55de5f5f050248432726dcd0e9e5d58d27f) Co-authored-by: prssanna --- frappe/public/js/frappe/form/form.js | 1 + frappe/public/js/frappe/form/toolbar.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index ef728e730e..de9331a726 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -360,6 +360,7 @@ frappe.ui.form.Form = class FrappeForm { grid_obj.grid.grid_pagination.go_to_page(1, true); }); frappe.ui.form.close_grid_form(); + this.viewers && this.viewers.parent.empty(); this.docname = docname; this.setup_docinfo_change_listener(); } diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 2f5b84fb1a..3bbfd63a46 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -211,7 +211,6 @@ frappe.ui.form.Toolbar = class Toolbar { make_viewers() { if (this.frm.viewers) { - this.frm.viewers.parent.empty(); return; } this.frm.viewers = new frappe.ui.form.FormViewers({ From 496445aac3915bfebbd439248b6193598b7ebdb9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:25:52 +0530 Subject: [PATCH 055/106] fix(website): Make language select optional and fix breakpoint issues (backport #12860) (#12909) Co-authored-by: Rushabh Mehta --- frappe/public/scss/website/index.scss | 7 +++ frappe/public/scss/website/navbar.scss | 14 ++++- frappe/templates/base.html | 36 +------------ frappe/templates/includes/navbar/navbar.html | 4 +- .../website_settings/website_settings.json | 21 ++++++-- .../website_settings/website_settings.py | 7 +-- frappe/website/js/website.js | 53 ++++++++++++++----- 7 files changed, 85 insertions(+), 57 deletions(-) diff --git a/frappe/public/scss/website/index.scss b/frappe/public/scss/website/index.scss index 1fb5badc6c..823ec9b08a 100644 --- a/frappe/public/scss/website/index.scss +++ b/frappe/public/scss/website/index.scss @@ -90,6 +90,13 @@ margin: 2rem 0; } +@media (max-width: map-get($grid-breakpoints, "lg")) { + .page-content-wrapper .container { + padding-left: 1rem; + padding-right: 1rem; + } +} + .breadcrumb-container { margin-top: 1rem; padding-top: 0.25rem; diff --git a/frappe/public/scss/website/navbar.scss b/frappe/public/scss/website/navbar.scss index 4d2ccfece9..3496a8907c 100644 --- a/frappe/public/scss/website/navbar.scss +++ b/frappe/public/scss/website/navbar.scss @@ -1,3 +1,15 @@ +.navbar { + padding-left: 0; + padding-right: 0; +} + +@media (max-width: map-get($grid-breakpoints, "lg")) { + .navbar { + padding-left: 1rem; + padding-right: 1rem; + } +} + .navbar-light { border-bottom: 1px solid $border-color; background: $navbar-bg; @@ -96,4 +108,4 @@ @extend .ellipsis; max-width: 100%; vertical-align: middle; -} \ No newline at end of file +} diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 18c9e9d99a..78aa573c99 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -56,6 +56,8 @@ } window.dev_server = {{ dev_server }}; window.socketio_port = {{ (frappe.socketio_port or 'null') }}; + window.show_language_picker = {{ show_language_picker }}; + window.is_chat_enabled = {{ chat_enable }}; @@ -110,39 +112,5 @@ {%- endblock %} {%- block body_include %}{{ body_include or "" }}{% endblock -%} -