Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frappe/public/js/frappe/form/footer/form_timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,13 @@ class FormTimeline extends BaseTimeline {
}

if (this.frm.doctype === "Communication") {
args.txt = "";
args.message = "";
args.last_email = this.frm.doc;
args.recipients = this.frm.doc.sender;
args.subject = __("Re: {0}", [this.frm.doc.subject]);
} else {
const comment_value = frappe.markdown(this.frm.comment_box.get_value());
args.txt = strip_html(comment_value) ? comment_value : '';
args.message = strip_html(comment_value) ? comment_value : '';
}

new frappe.views.CommunicationComposer(args);
Expand Down
3 changes: 3 additions & 0 deletions frappe/public/js/frappe/ui/field_group.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({
}
});
},
has_field: function(fieldname) {
return !!this.fields_dict[fieldname];
},
set_input: function(key, val) {
return this.set_value(key, val);
},
Expand Down
110 changes: 75 additions & 35 deletions frappe/public/js/frappe/views/communication.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ frappe.views.CommunicationComposer = class {
minimizable: true
});

this.dialog.sections[0].wrapper.addClass('to_section');
$(this.dialog.$wrapper.find(".form-section").get(0)).addClass('to_section');

this.prepare();
this.dialog.show();
Expand Down Expand Up @@ -98,6 +98,17 @@ frappe.views.CommunicationComposer = class {
300
)
},
{
fieldtype: "Button",
label: __("Add Signature"),
fieldname: 'add_signature',
hidden: 1,
click: async function () {
let sender_email = this.dialog.get_value('sender') || "";
this.content_set = false;
await this.set_content(sender_email);
}.bind(this)
},
{ fieldtype: "Section Break" },
{
label: __("Send me a copy"),
Expand Down Expand Up @@ -144,15 +155,21 @@ frappe.views.CommunicationComposer = class {
});

if (email_accounts.length) {
this.user_email_accounts = email_accounts.map(function(e) {
return e.email_id;
});

fields.unshift({
label: __("From"),
fieldtype: "Select",
reqd: 1,
fieldname: "sender",
options: email_accounts.map(function(e) {
return e.email_id;
})
options: this.user_email_accounts
});
//Preselect email senders if there is only one
if (this.user_email_accounts.length==1) {
this['sender'] = this.user_email_accounts
}
}

return fields;
Expand All @@ -175,9 +192,15 @@ frappe.views.CommunicationComposer = class {
this.setup_email();
this.setup_email_template();
this.setup_last_edited_communication();
this.setup_add_signature_button();
this.set_values();
}

setup_add_signature_button() {
let has_sender = this.dialog.has_field('sender');
this.dialog.set_df_property('add_signature', 'hidden', !has_sender);
}

setup_multiselect_queries() {
['recipients', 'cc', 'bcc'].forEach(field => {
this.dialog.fields_dict[field].get_data = () => {
Expand Down Expand Up @@ -237,7 +260,9 @@ frappe.views.CommunicationComposer = class {
// some email clients (outlook) may not send the message id to identify
// the thread. So as a backup we use the name of the document as identifier
const identifier = `#${this.frm.doc.name}`;
if (!this.subject.includes(identifier)) {

// converting to str for int names
if (!cstr(this.subject).includes(identifier)) {
this.subject = `${this.subject} (${identifier})`;
}
}
Expand All @@ -261,7 +286,6 @@ frappe.views.CommunicationComposer = class {
const subject_field = me.dialog.fields_dict.subject;

let content = content_field.get_value() || "";
content = content.split('<!-- salutation-ends -->')[1] || content;

content_field.set_value(`${reply.message}<br>${content}`);
subject_field.set_value(reply.subject);
Expand Down Expand Up @@ -349,7 +373,7 @@ frappe.views.CommunicationComposer = class {
}

async set_values_from_last_edited_communication() {
if (this.txt || this.message) return;
if (this.message) return;

const last_edited = this.get_last_edited_communication();
if (!last_edited.content) return;
Expand Down Expand Up @@ -708,10 +732,10 @@ frappe.views.CommunicationComposer = class {
}
}

async set_content() {
async set_content(sender_email) {
if (this.content_set) return;

let message = this.txt || this.message || "";
let message = this.message || "";
if (!message && this.frm) {
const { doctype, docname } = this.frm;
message = await localforage.getItem(doctype + docname) || "";
Expand All @@ -721,35 +745,47 @@ frappe.views.CommunicationComposer = class {
this.content_set = true;
}

message += await this.get_signature();

const SALUTATION_END_COMMENT = "<!-- salutation-ends -->";
if (this.real_name && !message.includes(SALUTATION_END_COMMENT)) {
message = `
<p>${__('Dear {0},', [this.real_name], 'Salutation in new email')},</p>
${SALUTATION_END_COMMENT}<br>
${message}
`;
}
message += await this.get_signature(sender_email || null);

if (this.is_a_reply) {
if (this.is_a_reply && !this.reply_set) {
message += this.get_earlier_reply();
}

await this.dialog.set_value("content", message);
}

async get_signature() {
async get_signature(sender_email) {
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'
);
let filters = {
'add_signature': 1
};

if (sender_email) {
filters['email_id'] = sender_email;
} else {
filters['default_outgoing'] = 1;
}

const email_accounts = await frappe.db.get_list("Email Account", {
filters: filters,
fields: ['signature', 'email_id'],
limit: 1
});

signature = response.message.signature;
let filtered_email = null;
if (email_accounts.length) {
signature = email_accounts[0].signature;
filtered_email = email_accounts[0].email_id;
}

if (!sender_email && filtered_email) {
if (this.user_email_accounts &&
this.user_email_accounts.includes(filtered_email)) {
this.dialog.set_value('sender', filtered_email);
}
}
}

if (!signature) return "";
Expand All @@ -758,10 +794,12 @@ frappe.views.CommunicationComposer = class {
signature = signature.replace(/\n/g, "<br>");
}

return "<br><!-- signature-included -->" + signature;
return "<br>" + signature;
}

get_earlier_reply() {
this.reply_set = false;

const last_email = (
this.last_email
|| this.frm && this.frm.timeline.get_last_email(true)
Expand All @@ -785,6 +823,8 @@ frappe.views.CommunicationComposer = class {
last_email.communication_date || last_email.creation
);

this.reply_set = true;

return `
<div><br></div>
${separator_element || ''}
Expand All @@ -799,12 +839,12 @@ frappe.views.CommunicationComposer = class {

html2text(html) {
// convert HTML to text and try and preserve whitespace
const d = document.createElement( 'div' );
d.innerHTML = html.replace(/<\/div>/g, '<br></div>') // replace end of blocks
.replace(/<\/p>/g, '<br></p>') // replace end of paragraphs
.replace(/<br>/g, '\n');
html = html
.replace(/<\/div>/g, "<br></div>") // replace end of blocks
.replace(/<\/p>/g, "<br></p>") // replace end of paragraphs
.replace(/<br>/g, "\n");

// replace multiple empty lines with just one
return d.textContent.replace(/\n{3,}/g, '\n\n');
const text = frappe.utils.html2text(html);
return text.replace(/\n{3,}/g, "\n\n");
}
};
};
26 changes: 16 additions & 10 deletions frappe/public/scss/common/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,22 @@ body.modal-open[style^="padding-right"] {
form {
display: flex;
align-items: center;
.frappe-control:first-child {
flex: 1;
margin-bottom: 0px;
}
.frappe-control:last-child {
margin-left: 10px;
margin-bottom: -24px;
button {
// same as form-control input
height: calc(1.5em + .75rem + 2px);

.frappe-control {
&[data-fieldname="sender"] {
flex: 1;
margin-bottom: 0px;
}
&[data-fieldname="recipients"] {
margin-left: 10px;
}
&[data-fieldname="option_toggle_button"] {
margin-left: 10px;
margin-bottom: -24px;
button {
// same as form-control input
height: calc(1.5em + .75rem + 2px);
}
}
}
}
Expand Down