form email sending via CommunicationComposer

This commit is contained in:
Rushabh Mehta 2012-11-29 16:08:45 +05:30
parent f003b4abea
commit cc7301eb9f
8 changed files with 145 additions and 489 deletions

View file

@ -41,7 +41,8 @@ def get_customer_supplier(args=None):
@webnotes.whitelist()
def make(doctype=None, name=None, content=None, subject=None,
sender=None, recipients=None, contact=None, lead=None,
communication_medium="Email", send_email=False):
communication_medium="Email", send_email=False, print_html=None,
attachments='[]'):
# add to Communication
sent_via = None
@ -60,11 +61,12 @@ def make(doctype=None, name=None, content=None, subject=None,
set_lead_and_contact(d)
d.communication_medium = communication_medium
if send_email:
send_comm_email(d, sent_via)
send_comm_email(d, name, sent_via, print_html, attachments)
d.save(1)
def send_comm_email(d, sent_via=None):
def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]'):
from webnotes.utils.email_lib import sendmail
from json import loads
if sent_via:
if hasattr(sent_via, "get_sender"):
@ -73,13 +75,23 @@ def send_comm_email(d, sent_via=None):
d.subject = sent_via.get_subject(d)
if hasattr(sent_via, "get_content"):
d.content = sent_via.get_content(d)
from webnotes.utils.email_lib.smtp import get_email
mail = get_email(d.recipients.split(","), sender=d.sender, subject=d.subject,
msg=d.content)
sendmail(\
recipients = d.recipients.split(","), \
sender = d.sender, \
subject = d.subject, \
msg= d.content)
if print_html:
mail.add_attachment(name.replace(' ','').replace('/','-') + '.html', print_html)
for a in loads(attachments):
try:
mail.attach_file(a)
except IOError, e:
webnotes.msgprint("""Unable to find attachment %s. Please resend without attaching this file.""" % a,
raise_exception=True)
mail.send()
if sent_via and hasattr(sent_via, 'on_communication_sent'):
sent_via.on_communication_sent(d)

View file

@ -1,167 +0,0 @@
// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// EMAIL
// Autosuggest defaults
_e.email_as_field = 'email_id';
_e.email_as_dt = 'Contact';
_e.email_as_in = 'email_id,contact_name';
sendmail = function(emailto, emailfrom, cc, subject, message, fmt, with_attachments) {
var fn = function(html) {
$c('webnotes.utils.email_lib.send_form', {
'sendto':emailto,
'sendfrom': emailfrom?emailfrom:'',
'cc':cc?cc:'',
'subject':subject,
'message':replace_newlines(message),
'body':html,
'full_domain': wn.urllib.get_base_url(),
'with_attachments':with_attachments ? 1 : 0,
'dt':cur_frm.doctype,
'dn':cur_frm.docname,
'customer': cur_frm.doc.customer || '',
'supplier': cur_frm.doc.supplier || ''
},
function(r, rtxt) {
//
}
);
}
// build print format
_p.build(fmt, fn);
}
_e.make = function() {
var d = new Dialog(440, 440, "Send Email");
var email_go = function() {
var emailfrom = d.widgets['From'].value;
var emailto = d.widgets['To'].value;
if(!emailfrom)
emailfrom = user_email;
emailto = emailto.replace(/ /g, "");
// validate email ids
var email_list = emailto.split(/[,|;]/);
var valid = 1;
for(var i=0;i<email_list.length;i++){
if(!email_list[i]) {
email_list.splice(i, 1);
} else if(!validate_email(email_list[i])) {
msgprint('error:'+email_list[i] + ' is not a valid email id');
valid = 0;
}
}
emailto = email_list.join(",");
// validate from
if(emailfrom && !validate_email(emailfrom)) {
msgprint('error:'+ emailfrom + ' is not a valid email id. To change the default please click on Profile on the top right of the screen and change it.');
return;
}
if(!valid)return;
var cc= emailfrom;
if(!emailfrom) {
emailfrom = wn.control_panel.auto_email_id;
cc = '';
}
sendmail(emailto, emailfrom, emailfrom, d.widgets['Subject'].value, d.widgets['Message'].value, sel_val(cur_frm.print_sel), d.widgets['Send With Attachments'].checked);
_e.dialog.hide();
}
d.onhide = function() {
}
d.make_body([
['Data','To','Example: abc@hotmail.com, xyz@yahoo.com']
,['Select','Format']
,['Data','Subject']
,['Data','From']
,['Check','Send With Attachments','Will send all attached documents (if any)']
,['Text','Message','To set your signature, go to <a href="#profile-settings">Profile Settings</a>']
,['Button','Send',email_go]]
);
d.widgets['From'].value = user_email;
$(d.widgets["From"]).attr("disabled", "disabled").addClass("disp_area");
$td(d.rows['Format'].tab,0,1).cur_sel = d.widgets['Format'];
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split(term).pop();
}
$(d.widgets['To'])
.bind( "keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data( "autocomplete" ).menu.active ) {
event.preventDefault();
}
})
.autocomplete({
source: function(request, response) {
wn.call({
method:'webnotes.utils.email_lib.get_contact_list',
args: {
'select': _e.email_as_field,
'from': _e.email_as_dt,
'where': _e.email_as_in,
'txt': extractLast(request.term).value || '%'
},
callback: function(r) {
response($.ui.autocomplete.filter(
r.cl || [], extractLast(request.term)));
}
});
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
_e.dialog = d;
}

View file

@ -200,36 +200,11 @@ _f.Frm.prototype.print_doc = function() {
// email the form
_f.Frm.prototype.email_doc = function() {
// make selector
if(!_e.dialog) _e.make();
_e.dialog.widgets['To'].value = '';
if (cur_frm.doc && cur_frm.doc.contact_email) {
_e.dialog.widgets['To'].value = cur_frm.doc.contact_email;
}
// set print selector
sel = this.print_sel;
var c = $td(_e.dialog.rows['Format'].tab,0,1);
if(c.cur_sel) {
c.removeChild(c.cur_sel);
c.cur_sel = null;
}
c.appendChild(this.print_sel);
c.cur_sel = this.print_sel;
// hide / show attachments
_e.dialog.widgets['Send With Attachments'].checked = 0;
if(cur_frm.doc.file_list) {
$ds(_e.dialog.rows['Send With Attachments']);
} else {
$dh(_e.dialog.rows['Send With Attachments']);
}
_e.dialog.widgets['Subject'].value = get_doctype_label(this.meta.name) + ': ' + this.docname;
_e.dialog.show();
new wn.views.CommunicationComposer({
doc: this.doc,
subject: get_doctype_label(this.meta.name) + ': ' + this.docname,
recipients: this.doc.email || this.doc.email_id || this.doc.contact_email
});
}
// notify this form of renamed records

View file

@ -117,8 +117,9 @@ $.extend(_p, {
*/
build: function(fmtname, onload, no_letterhead, only_body) {
if(!fmtname) {
onload("");
fmtname= "Standard";
}
args = {
fmtname: fmtname,
onload: onload,
@ -134,10 +135,6 @@ $.extend(_p, {
// Get current doc (record)
var doc = locals[cur_frm.doctype][cur_frm.docname];
if(args.fmtname == 'Standard') {
/*
Render standard print layout
The function passed as args onload is then called using these parameters
*/
args.onload(_p.render({
body: _p.print_std(args.no_letterhead),
style: _p.print_style,
@ -146,53 +143,23 @@ $.extend(_p, {
no_letterhead: args.no_letterhead,
only_body: args.only_body
}));
} else {
if (!_p.formats[args.fmtname]) {
/*
If print formats are not loaded, then load them and call the args onload function on callback.
I think, this case happens when preview is invoked directly
*/
var build_args = args;
$c(
command = 'webnotes.widgets.form.print_format.get',
args = { 'name': build_args.fmtname },
fn = function(r, rt) {
_p.formats[build_args.fmtname] = r.message;
build_args.onload(_p.render({
body: _p.formats[build_args.fmtname],
style: '',
doc: doc,
title: doc.name,
no_letterhead: build_args.no_letterhead,
only_body: build_args.only_body
}));
}
);
} else {
// If print format is already loaded, go ahead with args onload function call
args.onload(_p.render({
body: _p.formats[args.fmtname],
style: '',
doc: doc,
title: doc.name,
no_letterhead: args.no_letterhead,
only_body: args.only_body
}));
}
} else {
var print_format_doc = locals["Print Format"][args.fmtname];
if(!print_format_doc) {
msgprint("Unknown Print Format: " + args.fmtname);
return;
}
args.onload(_p.render({
body: print_format_doc,
style: '',
doc: doc,
title: doc.name,
no_letterhead: args.no_letterhead,
only_body: args.only_body
}));
}
},
/*
args dict can contain:
+ body
+ style
+ doc
+ title
+ no_letterhead
+ only_body
*/
render: function(args) {
var container = document.createElement('div');
var stat = '';

View file

@ -26,7 +26,8 @@ wn.ui.FieldGroup = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.make_fields();
this.catch_enter_as_submit();
if(!this.no_submit_on_enter)
this.catch_enter_as_submit();
},
first_button: false,
make_fields: function() {

View file

@ -5,7 +5,7 @@ wn.views.CommunicationList = Class.extend({
init: function(opts) {
this.comm_list = [];
$.extend(this, opts);
if(this.doc.__islocal) {
return;
}
@ -50,11 +50,12 @@ wn.views.CommunicationList = Class.extend({
this.body = $("<table class='table table-bordered table-hover table-striped'>")
.appendTo(this.wrapper);
},
add_reply: function() {
new wn.views.CommunicationComposer({
doc: this.doc,
subject: this.doc.subject,
email: this.email
recipients: this.recipients
})
},
@ -65,14 +66,15 @@ wn.views.CommunicationList = Class.extend({
doc.content = doc.content.replace(/\n/g, "<br>");
}
if(!doc.sender) doc.sender = "[unknown sender]";
doc.sender = doc.sender.replace(/</, "&lt;").replace(/>/, "&gt;");
doc._sender = doc.sender.replace(/</, "&lt;").replace(/>/, "&gt;");
doc.content = doc.content.split("-----In response to-----")[0];
doc.content = doc.content.split("-----Original Message-----")[0];
},
make_line: function(doc) {
var me = this;
var comm = $(repl('<tr><td title="Click to Expand / Collapse">\
<p><b>%(sender)s on %(when)s</b> \
<p><b>%(_sender)s on %(when)s</b> \
<a href="#Form/Communication/%(name)s" style="font-size: 90%">\
Show Details</a></p>\
<div class="comm-content" style="border-top: 1px solid #ddd; \
@ -99,8 +101,9 @@ wn.views.CommunicationComposer = Class.extend({
this.dialog = new wn.ui.Dialog({
width: 640,
title: "Add Reply: " + (this.subject || ""),
no_submit_on_enter: true,
fields: [
{label:"To", fieldtype:"Data", reqd: 1, fieldname:"sender"},
{label:"To", fieldtype:"Data", reqd: 1, fieldname:"recipients"},
{label:"Subject", fieldtype:"Data", reqd: 1},
{label:"Message", fieldtype:"Text Editor", reqd: 1, fieldname:"content"},
{label:"Add Reply", fieldtype:"Button"},
@ -114,10 +117,12 @@ wn.views.CommunicationComposer = Class.extend({
},
prepare: function() {
this.setup_print();
this.setup_attach();
this.setup_email();
$(this.dialog.fields_dict.sender.input).val(this.email || "").change();
this.setup_autosuggest();
$(this.dialog.fields_dict.recipients.input).val(this.recipients || "").change();
$(this.dialog.fields_dict.subject.input).val(this.subject || "").change();
this.setup_earlier_reply();
this.setup_earlier_reply();
},
setup_print: function() {
// print formats
@ -126,23 +131,28 @@ wn.views.CommunicationComposer = Class.extend({
// toggle print format
$(fields.attach_document_print.input).click(function() {
$(fields.select_print_format.wrapper).toggle($(this).is(":checked"));
$(fields.select_attachments.wrapper).toggle($(this).is(":checked"));
});
// select print format
$(fields.select_print_format.wrapper).toggle(false);
$(fields.select_print_format.input)
.empty()
.add_options(cur_frm.print_formats);
.add_options(cur_frm.print_formats)
.val(cur_frm.print_formats[0]);
// show attachment list
},
setup_attach: function() {
var fields = this.dialog.fields_dict;
var attach = $(fields.select_attachments.wrapper);
attach.toggle(false);
$.each(cur_frm.get_files(), function(i, f) {
$(repl("<p><input type='checkbox' \
data-file-name='%(file)s'> %(file)s</p>", {file:f})).appendTo(attach)
});
var files = cur_frm.get_files();
if(files) {
$("<p><b>Add Attachments:</b></p>").appendTo(attach);
$.each(cur_frm.get_files(), function(i, f) {
$(repl("<p><input type='checkbox' \
data-file-name='%(file)s'> %(file)s</p>", {file:f})).appendTo(attach)
});
}
},
setup_email: function() {
// email
@ -151,28 +161,33 @@ wn.views.CommunicationComposer = Class.extend({
$(fields.send_email.input).attr("checked", "checked")
$(fields.add_reply.input).click(function() {
var args = me.dialog.get_values();
if(!args) return;
var form_values = me.dialog.get_values();
if(!form_values) return;
var selected_attachments = $.map($(me.dialog.wrapper)
.find("[data-file-name]:checked"), function(element) {
return $(element).attr("data-file-name");
})
_p.build(args.select_print_format || "", function(print_html) {
$(this).set_working();
me.dialog.hide();
wn.call({
method:"support.doctype.communication.communication.make",
method:"core.doctype.communication.communication.make",
args: {
sender: args.sender,
subject: args.subject,
content: args.content,
sender: wn.user_info(user).fullname + " <" + wn.boot.profile.email + ">",
recipients: form_values.recipients,
subject: form_values.subject,
content: form_values.content,
doctype: me.doc.doctype,
name: me.doc.name,
lead: me.doc.lead,
contact: me.doc.contact,
recipients: me.email,
print_html: args.attach_document_print
send_email: form_values.send_email,
print_html: form_values.attach_document_print
? print_html : "",
attachments: selected_attachments
},
callback: function(r) {
me.dialog.hide();
cur_frm.reload_doc();
}
});
@ -195,5 +210,56 @@ wn.views.CommunicationComposer = Class.extend({
fields.content.input.set_input("<p></p>"
+ (wn.boot.profile.email_signature || ""))
}
},
setup_autosuggest: function() {
var me = this;
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split(term).pop();
}
$(this.dialog.fields_dict.recipients.input)
.bind( "keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data( "autocomplete" ).menu.active ) {
event.preventDefault();
}
})
.autocomplete({
source: function(request, response) {
wn.call({
method:'webnotes.utils.email_lib.get_contact_list',
args: {
'select': _e.email_as_field,
'from': _e.email_as_dt,
'where': _e.email_as_in,
'txt': extractLast(request.term).value || '%'
},
callback: function(r) {
response($.ui.autocomplete.filter(
r.cl || [], extractLast(request.term)));
}
});
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
}
})
});

View file

@ -1,198 +0,0 @@
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
#
# MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
from __future__ import unicode_literals
import webnotes
from webnotes.utils import cint
from webnotes.utils.email_lib.smtp import EMail
class FormEmail:
"""
Represents an email sent from a Form
"""
def __init__(self):
"""
Get paramteres from the cgi form object
"""
self.__dict__.update(webnotes.form_dict)
self.recipients = None
if self.sendto:
self.recipients = self.sendto.replace(';', ',')
self.recipients = self.recipients.split(',')
def update_contacts(self):
"""
Add new email contact to database
"""
import webnotes
from webnotes.model.doc import Document
for r in self.recipients:
r = r.strip()
try:
if not webnotes.conn.sql("select email_id from tabContact where email_id=%s", r):
d = Document('Contact')
d.email_id = r
d.save(1)
except Exception, e:
if e.args[0]==1146: pass # no table
else: raise e
def make_full_links(self):
"""
Adds server name the relative links, so that images etc can be seen correctly
"""
# only domain
if not self.__dict__.get('full_domain'):
return
def make_full_link(match):
import os
link = match.group('name')
if not link.startswith('http'):
link = os.path.join(self.full_domain, link)
return 'src="%s"' % link
import re
p = re.compile('src[ ]*=[ ]*" (?P<name> [^"]*) "', re.VERBOSE)
self.body = p.sub(make_full_link, self.body)
p = re.compile("src[ ]*=[ ]*' (?P<name> [^']*) '", re.VERBOSE)
self.body = p.sub(make_full_link, self.body)
def get_form_link(self):
"""
Returns publicly accessible form link
"""
public_domain = webnotes.conn.get_value('Control Panel', None, 'public_domain')
from webnotes.utils.encrypt import encrypt
if not public_domain:
return ''
args = {
'dt': self.dt,
'dn':self.dn,
'acx': webnotes.conn.get_value('Control Panel', None, 'account_id'),
'server': public_domain,
'akey': encrypt(self.dn)
}
return '<div>If you are unable to view the form below <a href="http://%(server)s/index.cgi?page=Form/%(dt)s/%(dn)s&acx=%(acx)s&akey=%(akey)s">click here to see it in your browser</div>' % args
def set_attachments(self):
"""
Set attachments to the email from the form
"""
al = []
try:
al = webnotes.conn.sql('select file_list from `tab%s` where name="%s"' % (webnotes.form_dict.get('dt'), webnotes.form_dict.get('dn')))
if al:
al = (al[0][0] or '').split('\n')
except Exception, e:
if e.args[0]==1146:
pass # no attachments in single types!
else:
raise Exception, e
return al
def build_message(self):
"""
Builds the message object
"""
self.email = EMail(self.sendfrom, self.recipients, self.subject, alternative = 1)
self.make_full_links()
# message
if not self.__dict__.get('message'):
self.message = 'Please find attached %s: %s\n' % (self.dt, self.dn)
# body
self.email.set_html_as_text(self.message)
self.email.set_part_html(self.message.replace('\n','<br>') + "<br><br>" \
+ self.email.get_footer() + "<br><br>" + self.body)
def make_communication(self):
"""make email communication"""
from webnotes.model.doc import Document
from webnotes.utils import nowdate
comm = Document('Communication')
comm.communication_medium = 'Email'
comm.subject = self.subject
comm.content = self.message
comm.category = 'Sent Mail'
comm.action = 'Sent Mail'
comm.naming_series = 'COMM-'
comm.user = webnotes.session["user"]
comm.communication_date = nowdate()
comm.email_address = ", ".join(self.recipients)
try:
comm_cols = [c[0] for c in webnotes.conn.sql("""desc tabCommunication""")]
# tag to record
dt = self.dt.lower().replace(" ", "_")
if dt in comm_cols:
comm.fields[dt] = self.dn
# tag to customer, supplier (?)
if self.customer:
comm.customer = self.customer
if self.supplier:
comm.supplier = self.supplier
comm.save(1)
except Exception, e:
if e.args[0]!=1146: raise e
def send(self):
"""
Send the form with html attachment
"""
if not self.recipients:
webnotes.msgprint('No one to send to!')
return
self.build_message()
# print format (as attachment also - for text-only clients)
self.email.add_attachment(self.dn.replace(' ','').replace('/','-') + '.html', self.body)
# attachments
# self.with_attachments comes from http form variables
# i.e. with_attachments=1
if cint(self.with_attachments):
for a in self.set_attachments():
a and self.email.attach_file(a.split(',')[1])
# cc
if self.cc:
self.email.cc = [self.cc]
self.email.send()
self.make_communication()
webnotes.msgprint('Sent')

View file

@ -124,12 +124,12 @@ class EMail:
import startup
footer = ""
if self.sender == webnotes.session.user:
signature = webnotes.conn.get_value("Profile", self.sender, "email_signature") or ""
if signature and (not "<br>" in signature) and (not "<p" in signature) \
and not "<div" in signature:
signature = signature.replace("\n", "<br>\n")
footer = signature
# if self.sender == webnotes.session.user:
# signature = webnotes.conn.get_value("Profile", self.sender, "email_signature") or ""
# if signature and (not "<br>" in signature) and (not "<p" in signature) \
# and not "<div" in signature:
# signature = signature.replace("\n", "<br>\n")
# footer = signature
footer += webnotes.conn.get_value('Control Panel',None,'mail_footer') or ''
footer += getattr(startup, 'mail_footer', '')
@ -198,7 +198,7 @@ class EMail:
webnotes.msgprint("%s is not a valid email id" % self.reply_to, raise_exception = 1)
for e in self.recipients + (self.cc or []):
if not validate_email_add(e):
if e.strip() and not validate_email_add(e):
webnotes.msgprint("%s is not a valid email id" % e, raise_exception = 1)
def make(self):