# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import webnotes
from webnotes import msgprint, throw, _
from webnotes.utils import scrub_urls
import email.utils
from inlinestyler.utils import inline_css
def get_email(recipients, sender='', msg='', subject='[No Subject]',
text_content = None, footer=None, print_html=None, formatted=None):
"""send an html email as multipart with attachments and all"""
email = EMail(sender, recipients, subject)
if (not '
' in msg) and (not '
' in msg) and (not '
')
email.set_html(msg, text_content, footer=footer, print_html=print_html, formatted=formatted)
return email
class EMail:
"""
Wrapper on the email module. Email object represents emails to be sent to the client.
Also provides a clean way to add binary `FileData` attachments
Also sets all messages as multipart/alternative for cleaner reading in text-only clients
"""
def __init__(self, sender='', recipients=[], subject='', alternative=0, reply_to=None):
from email.mime.multipart import MIMEMultipart
from email import Charset
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
if isinstance(recipients, basestring):
recipients = recipients.replace(';', ',').replace('\n', '')
recipients = recipients.split(',')
# remove null
recipients = filter(None, (r.strip() for r in recipients))
self.sender = sender
self.reply_to = reply_to or sender
self.recipients = recipients
self.subject = subject
self.msg_root = MIMEMultipart('mixed')
self.msg_multipart = MIMEMultipart('alternative')
self.msg_root.attach(self.msg_multipart)
self.cc = []
self.html_set = False
def set_html(self, message, text_content = None, footer=None, print_html=None, formatted=None):
"""Attach message in the html portion of multipart/alternative"""
if not formatted:
formatted = get_formatted_html(self.subject, message, footer, print_html)
# this is the first html part of a multi-part message,
# convert to text well
if not self.html_set:
if text_content:
self.set_text(text_content)
else:
self.set_html_as_text(message)
self.set_part_html(formatted)
self.html_set = True
def set_text(self, message):
"""
Attach message in the text portion of multipart/alternative
"""
from email.mime.text import MIMEText
part = MIMEText(message, 'plain', 'utf-8')
self.msg_multipart.attach(part)
def set_part_html(self, message):
from email.mime.text import MIMEText
part = MIMEText(message, 'html', 'utf-8')
self.msg_multipart.attach(part)
def set_html_as_text(self, html):
"""return html2text"""
import HTMLParser
from webnotes.utils.email_lib.html2text import html2text
try:
self.set_text(html2text(html))
except HTMLParser.HTMLParseError:
pass
def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'):
"""Append the message with MIME content to the root node (as attachment)"""
from email.mime.text import MIMEText
maintype, subtype = mime_type.split('/')
part = MIMEText(message, _subtype = subtype)
if as_attachment:
part.add_header('Content-Disposition', 'attachment', filename=filename)
self.msg_root.attach(part)
def attach_file(self, n):
"""attach a file from the `FileData` table"""
from webnotes.utils.file_manager import get_file
res = get_file(n)
if not res:
return
self.add_attachment(res[0], res[1])
def add_attachment(self, fname, fcontent, content_type=None):
"""add attachment"""
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
import mimetypes
if not content_type:
content_type, encoding = mimetypes.guess_type(fname)
if content_type is None:
# No guess could be made, or the file is encoded (compressed), so
# use a generic bag-of-bits type.
content_type = 'application/octet-stream'
maintype, subtype = content_type.split('/', 1)
if maintype == 'text':
# Note: we should handle calculating the charset
if isinstance(fcontent, unicode):
fcontent = fcontent.encode("utf-8")
part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
elif maintype == 'image':
part = MIMEImage(fcontent, _subtype=subtype)
elif maintype == 'audio':
part = MIMEAudio(fcontent, _subtype=subtype)
else:
part = MIMEBase(maintype, subtype)
part.set_payload(fcontent)
# Encode the payload using Base64
from email import encoders
encoders.encode_base64(part)
# Set the filename parameter
if fname:
part.add_header(b'Content-Disposition',
("attachment; filename=%s" % fname).encode('utf-8'))
self.msg_root.attach(part)
def validate(self):
"""validate the email ids"""
from webnotes.utils import validate_email_add
def _validate(email):
"""validate an email field"""
if email and not validate_email_add(email):
throw("{email} {msg}".format(**{
"email": email,
"msg": _("is not a valid email id")
}))
return email
if not self.sender:
self.sender = webnotes.conn.get_value('Email Settings', None,
'auto_email_id') or webnotes.conf.get('auto_email_id') or None
if not self.sender:
msgprint(_("Please specify 'Auto Email Id' in Setup > Email Settings"))
if not "expires_on" in webnotes.conf:
msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json"))
raise webnotes.ValidationError
self.sender = _validate(self.sender)
self.reply_to = _validate(self.reply_to)
for e in self.recipients + (self.cc or []):
_validate(e.strip())
def make(self):
"""build into msg_root"""
self.msg_root['Subject'] = self.subject.encode("utf-8")
self.msg_root['From'] = self.sender.encode("utf-8")
self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients]).encode("utf-8")
self.msg_root['Date'] = email.utils.formatdate()
if not self.reply_to:
self.reply_to = self.sender
self.msg_root['Reply-To'] = self.reply_to.encode("utf-8")
if self.cc:
self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc]).encode("utf-8")
def as_string(self):
"""validate, build message and convert to string"""
self.validate()
self.make()
return self.msg_root.as_string()
def get_formatted_html(subject, message, footer=None, print_html=None):
message = scrub_urls(message)
return inline_css(webnotes.get_template("templates/emails/standard.html").render({
"content": message,
"footer": get_footer(footer),
"title": subject,
"print_html": print_html
}))
def get_footer(footer=None):
"""append a footer (signature)"""
footer = footer or ""
# control panel
footer += webnotes.conn.get_value('Control Panel', None, 'mail_footer') or ''
# hooks
for f in webnotes.get_hooks("mail_footer"):
footer += webnotes.get_attr(f)
footer += ""
return footer