From cc7301eb9f1894a503c2cceed8ec912ad1bf2ab7 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 29 Nov 2012 16:08:45 +0530 Subject: [PATCH] form email sending via CommunicationComposer --- core/doctype/communication/communication.py | 30 ++- public/js/legacy/widgets/form/email.js | 167 --------------- public/js/legacy/widgets/form/form.js | 35 +--- public/js/legacy/widgets/form/print_format.js | 65 ++---- public/js/wn/ui/field_group.js | 3 +- public/js/wn/views/communication.js | 122 ++++++++--- webnotes/utils/email_lib/form_email.py | 198 ------------------ webnotes/utils/email_lib/smtp.py | 14 +- 8 files changed, 145 insertions(+), 489 deletions(-) delete mode 100644 public/js/legacy/widgets/form/email.js delete mode 100644 webnotes/utils/email_lib/form_email.py diff --git a/core/doctype/communication/communication.py b/core/doctype/communication/communication.py index f365fe38b4..c6a225b3d8 100644 --- a/core/doctype/communication/communication.py +++ b/core/doctype/communication/communication.py @@ -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) diff --git a/public/js/legacy/widgets/form/email.js b/public/js/legacy/widgets/form/email.js deleted file mode 100644 index 36afa9d919..0000000000 --- a/public/js/legacy/widgets/form/email.js +++ /dev/null @@ -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;iProfile Settings'] - ,['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; -} - diff --git a/public/js/legacy/widgets/form/form.js b/public/js/legacy/widgets/form/form.js index 2d8adee610..2daafa2c89 100644 --- a/public/js/legacy/widgets/form/form.js +++ b/public/js/legacy/widgets/form/form.js @@ -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 diff --git a/public/js/legacy/widgets/form/print_format.js b/public/js/legacy/widgets/form/print_format.js index c5bb8c2469..077b5c9daa 100644 --- a/public/js/legacy/widgets/form/print_format.js +++ b/public/js/legacy/widgets/form/print_format.js @@ -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 = ''; diff --git a/public/js/wn/ui/field_group.js b/public/js/wn/ui/field_group.js index 4b562e20c8..d6a5cc4b2b 100644 --- a/public/js/wn/ui/field_group.js +++ b/public/js/wn/ui/field_group.js @@ -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() { diff --git a/public/js/wn/views/communication.js b/public/js/wn/views/communication.js index 043c722095..6a265e9d67 100644 --- a/public/js/wn/views/communication.js +++ b/public/js/wn/views/communication.js @@ -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 = $("") .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, "
"); } if(!doc.sender) doc.sender = "[unknown sender]"; - doc.sender = doc.sender.replace(//, ">"); + doc._sender = doc.sender.replace(//, ">"); 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('
\ -

%(sender)s on %(when)s \ +

%(_sender)s on %(when)s \ \ Show Details

\
%(file)s

", {file:f})).appendTo(attach) - }); + var files = cur_frm.get_files(); + if(files) { + $("

Add Attachments:

").appendTo(attach); + $.each(cur_frm.get_files(), function(i, f) { + $(repl("

%(file)s

", {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("

" + (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; + } + }); } -}) +}); + diff --git a/webnotes/utils/email_lib/form_email.py b/webnotes/utils/email_lib/form_email.py deleted file mode 100644 index 9f2ebc9dd9..0000000000 --- a/webnotes/utils/email_lib/form_email.py +++ /dev/null @@ -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 [^"]*) "', re.VERBOSE) - self.body = p.sub(make_full_link, self.body) - - p = re.compile("src[ ]*=[ ]*' (?P [^']*) '", 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 '
If you are unable to view the form below click here to see it in your browser
' % 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','
') + "

" \ - + self.email.get_footer() + "

" + 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') diff --git a/webnotes/utils/email_lib/smtp.py b/webnotes/utils/email_lib/smtp.py index 65b45b13c8..05b676b52e 100644 --- a/webnotes/utils/email_lib/smtp.py +++ b/webnotes/utils/email_lib/smtp.py @@ -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 "
" in signature) and (not "\n") - footer = signature + # if self.sender == webnotes.session.user: + # signature = webnotes.conn.get_value("Profile", self.sender, "email_signature") or "" + # if signature and (not "
" in signature) and (not "\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):