email.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. from __future__ import unicode_literals
  2. from future.builtins import bytes, str
  3. from django.contrib.auth.tokens import default_token_generator
  4. from django.core.mail import EmailMultiAlternatives
  5. from django.core.urlresolvers import reverse
  6. from django.template import loader, Context
  7. from django.utils.http import int_to_base36
  8. from mezzanine.conf import settings
  9. from mezzanine.utils.urls import admin_url, next_url
  10. from mezzanine.conf.context_processors import settings as context_settings
  11. def split_addresses(email_string_list):
  12. """
  13. Converts a string containing comma separated email addresses
  14. into a list of email addresses.
  15. """
  16. return [f for f in [s.strip() for s in email_string_list.split(",")] if f]
  17. def subject_template(template, context):
  18. """
  19. Loads and renders an email subject template, returning the
  20. subject string.
  21. """
  22. subject = loader.get_template(template).render(Context(context))
  23. return " ".join(subject.splitlines()).strip()
  24. def send_mail_template(subject, template, addr_from, addr_to, context=None,
  25. attachments=None, fail_silently=None, addr_bcc=None,
  26. headers=None):
  27. """
  28. Send email rendering text and html versions for the specified
  29. template name using the context dictionary passed in.
  30. """
  31. if context is None:
  32. context = {}
  33. if attachments is None:
  34. attachments = []
  35. if fail_silently is None:
  36. fail_silently = settings.EMAIL_FAIL_SILENTLY
  37. # Add template accessible settings from Mezzanine to the context
  38. # (normally added by a context processor for HTTP requests).
  39. context.update(context_settings())
  40. # Allow for a single address to be passed in.
  41. # Python 3 strings have an __iter__ method, so the following hack
  42. # doesn't work: if not hasattr(addr_to, "__iter__"):
  43. if isinstance(addr_to, str) or isinstance(addr_to, bytes):
  44. addr_to = [addr_to]
  45. if addr_bcc is not None and (isinstance(addr_bcc, str) or
  46. isinstance(addr_bcc, bytes)):
  47. addr_bcc = [addr_bcc]
  48. # Loads a template passing in vars as context.
  49. render = lambda type: loader.get_template("%s.%s" %
  50. (template, type)).render(Context(context))
  51. # Create and send email.
  52. msg = EmailMultiAlternatives(subject, render("txt"),
  53. addr_from, addr_to, addr_bcc,
  54. headers=headers)
  55. msg.attach_alternative(render("html"), "text/html")
  56. for attachment in attachments:
  57. msg.attach(*attachment)
  58. msg.send(fail_silently=fail_silently)
  59. def send_verification_mail(request, user, verification_type):
  60. """
  61. Sends an email with a verification link to users when
  62. ``ACCOUNTS_VERIFICATION_REQUIRED`` is ```True`` and they're signing
  63. up, or when they reset a lost password.
  64. The ``verification_type`` arg is both the name of the urlpattern for
  65. the verification link, as well as the names of the email templates
  66. to use.
  67. """
  68. verify_url = reverse(verification_type, kwargs={
  69. "uidb36": int_to_base36(user.id),
  70. "token": default_token_generator.make_token(user),
  71. }) + "?next=" + (next_url(request) or "/")
  72. context = {
  73. "request": request,
  74. "user": user,
  75. "verify_url": verify_url,
  76. }
  77. subject_template_name = "email/%s_subject.txt" % verification_type
  78. subject = subject_template(subject_template_name, context)
  79. send_mail_template(subject, "email/%s" % verification_type,
  80. settings.DEFAULT_FROM_EMAIL, user.email,
  81. context=context)
  82. def send_approve_mail(request, user):
  83. """
  84. Sends an email to staff in listed in the setting
  85. ``ACCOUNTS_APPROVAL_EMAILS``, when a new user signs up and the
  86. ``ACCOUNTS_APPROVAL_REQUIRED`` setting is ``True``.
  87. """
  88. approval_emails = split_addresses(settings.ACCOUNTS_APPROVAL_EMAILS)
  89. if not approval_emails:
  90. return
  91. context = {
  92. "request": request,
  93. "user": user,
  94. "change_url": admin_url(user.__class__, "change", user.id),
  95. }
  96. subject = subject_template("email/account_approve_subject.txt", context)
  97. send_mail_template(subject, "email/account_approve",
  98. settings.DEFAULT_FROM_EMAIL, approval_emails,
  99. context=context)
  100. def send_approved_mail(request, user):
  101. """
  102. Sends an email to a user once their ``is_active`` status goes from
  103. ``False`` to ``True`` when the ``ACCOUNTS_APPROVAL_REQUIRED``
  104. setting is ``True``.
  105. """
  106. context = {"request": request, "user": user}
  107. subject = subject_template("email/account_approved_subject.txt", context)
  108. send_mail_template(subject, "email/account_approved",
  109. settings.DEFAULT_FROM_EMAIL, user.email,
  110. context=context)