Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

email_body.py 6.8 KiB

vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
vor 11 Jahren
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import webnotes
  5. from webnotes import msgprint, throw, _
  6. from webnotes.utils import scrub_urls
  7. import email.utils
  8. from inlinestyler.utils import inline_css
  9. def get_email(recipients, sender='', msg='', subject='[No Subject]',
  10. text_content = None, footer=None, formatted=None):
  11. """send an html email as multipart with attachments and all"""
  12. email = EMail(sender, recipients, subject)
  13. if (not '<br>' in msg) and (not '<p>' in msg) and (not '<div' in msg):
  14. msg = msg.replace('\n', '<br>')
  15. email.set_html(msg, text_content, footer=footer, formatted=formatted)
  16. return email
  17. class EMail:
  18. """
  19. Wrapper on the email module. Email object represents emails to be sent to the client.
  20. Also provides a clean way to add binary `FileData` attachments
  21. Also sets all messages as multipart/alternative for cleaner reading in text-only clients
  22. """
  23. def __init__(self, sender='', recipients=[], subject='', alternative=0, reply_to=None):
  24. from email.mime.multipart import MIMEMultipart
  25. from email import Charset
  26. Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
  27. if isinstance(recipients, basestring):
  28. recipients = recipients.replace(';', ',').replace('\n', '')
  29. recipients = recipients.split(',')
  30. # remove null
  31. recipients = filter(None, (r.strip() for r in recipients))
  32. self.sender = sender
  33. self.reply_to = reply_to or sender
  34. self.recipients = recipients
  35. self.subject = subject
  36. self.msg_root = MIMEMultipart('mixed')
  37. self.msg_multipart = MIMEMultipart('alternative')
  38. self.msg_root.attach(self.msg_multipart)
  39. self.cc = []
  40. self.html_set = False
  41. def set_html(self, message, text_content = None, footer=None, formatted=None):
  42. """Attach message in the html portion of multipart/alternative"""
  43. if not formatted:
  44. formatted = get_formatted_html(self.subject, message, footer)
  45. # this is the first html part of a multi-part message,
  46. # convert to text well
  47. if not self.html_set:
  48. if text_content:
  49. self.set_text(text_content)
  50. else:
  51. self.set_html_as_text(message)
  52. self.set_part_html(formatted)
  53. self.html_set = True
  54. def set_text(self, message):
  55. """
  56. Attach message in the text portion of multipart/alternative
  57. """
  58. from email.mime.text import MIMEText
  59. part = MIMEText(message.encode('utf-8'), 'plain', 'utf-8')
  60. self.msg_multipart.attach(part)
  61. def set_part_html(self, message):
  62. from email.mime.text import MIMEText
  63. part = MIMEText(message.encode('utf-8'), 'html', 'utf-8')
  64. self.msg_multipart.attach(part)
  65. def set_html_as_text(self, html):
  66. """return html2text"""
  67. import HTMLParser
  68. from webnotes.utils.email_lib.html2text import html2text
  69. try:
  70. self.set_text(html2text(html))
  71. except HTMLParser.HTMLParseError:
  72. pass
  73. def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'):
  74. """Append the message with MIME content to the root node (as attachment)"""
  75. from email.mime.text import MIMEText
  76. maintype, subtype = mime_type.split('/')
  77. part = MIMEText(message, _subtype = subtype)
  78. if as_attachment:
  79. part.add_header('Content-Disposition', 'attachment', filename=filename)
  80. self.msg_root.attach(part)
  81. def attach_file(self, n):
  82. """attach a file from the `FileData` table"""
  83. from webnotes.utils.file_manager import get_file
  84. res = get_file(n)
  85. if not res:
  86. return
  87. self.add_attachment(res[0], res[1])
  88. def add_attachment(self, fname, fcontent, content_type=None):
  89. """add attachment"""
  90. from email.mime.audio import MIMEAudio
  91. from email.mime.base import MIMEBase
  92. from email.mime.image import MIMEImage
  93. from email.mime.text import MIMEText
  94. import mimetypes
  95. if not content_type:
  96. content_type, encoding = mimetypes.guess_type(fname)
  97. if content_type is None:
  98. # No guess could be made, or the file is encoded (compressed), so
  99. # use a generic bag-of-bits type.
  100. content_type = 'application/octet-stream'
  101. maintype, subtype = content_type.split('/', 1)
  102. if maintype == 'text':
  103. # Note: we should handle calculating the charset
  104. if isinstance(fcontent, unicode):
  105. fcontent = fcontent.encode("utf-8")
  106. part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
  107. elif maintype == 'image':
  108. part = MIMEImage(fcontent, _subtype=subtype)
  109. elif maintype == 'audio':
  110. part = MIMEAudio(fcontent, _subtype=subtype)
  111. else:
  112. part = MIMEBase(maintype, subtype)
  113. part.set_payload(fcontent)
  114. # Encode the payload using Base64
  115. from email import encoders
  116. encoders.encode_base64(part)
  117. # Set the filename parameter
  118. if fname:
  119. part.add_header(b'Content-Disposition',
  120. ("attachment; filename=%s" % fname).encode('utf-8'))
  121. self.msg_root.attach(part)
  122. def validate(self):
  123. """validate the email ids"""
  124. from webnotes.utils import validate_email_add
  125. def _validate(email):
  126. """validate an email field"""
  127. if email and not validate_email_add(email):
  128. throw("{email} {msg}".format(**{
  129. "email": email,
  130. "msg": _("is not a valid email id")
  131. }))
  132. return email
  133. if not self.sender:
  134. self.sender = webnotes.conn.get_value('Email Settings', None,
  135. 'auto_email_id') or webnotes.conf.get('auto_email_id') or None
  136. if not self.sender:
  137. msgprint(_("Please specify 'Auto Email Id' in Setup > Email Settings"))
  138. if not "expires_on" in webnotes.conf:
  139. msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json"))
  140. raise webnotes.ValidationError
  141. self.sender = _validate(self.sender)
  142. self.reply_to = _validate(self.reply_to)
  143. for e in self.recipients + (self.cc or []):
  144. _validate(e.strip())
  145. def make(self):
  146. """build into msg_root"""
  147. self.msg_root['Subject'] = self.subject.encode("utf-8")
  148. self.msg_root['From'] = self.sender.encode("utf-8")
  149. self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients]).encode("utf-8")
  150. self.msg_root['Date'] = email.utils.formatdate()
  151. if not self.reply_to:
  152. self.reply_to = self.sender
  153. self.msg_root['Reply-To'] = self.reply_to.encode("utf-8")
  154. if self.cc:
  155. self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc]).encode("utf-8")
  156. def as_string(self):
  157. """validate, build message and convert to string"""
  158. self.validate()
  159. self.make()
  160. return self.msg_root.as_string()
  161. def get_formatted_html(subject, message, footer=None):
  162. message = scrub_urls(message)
  163. return inline_css(webnotes.get_template("templates/emails/standard.html").render({
  164. "content": message,
  165. "footer": get_footer(footer),
  166. "title": subject
  167. }))
  168. def get_footer(footer=None):
  169. """append a footer (signature)"""
  170. footer = footer or ""
  171. # control panel
  172. footer += webnotes.conn.get_value('Control Panel', None, 'mail_footer') or ''
  173. # hooks
  174. for f in webnotes.get_hooks("mail_footer"):
  175. footer += webnotes.get_attr(f)
  176. footer += "<!--unsubscribe link here-->"
  177. return footer