class NotificationTemplate(models.Model): """Notification template.""" template = models.ForeignKey(EmailTemplate, blank=False) from_email = models.CharField(_("Email From"), max_length=254, validators=[validate_email_with_name]) to = CommaSeparatedEmailField(_("Email To"), null=False, blank=False, default=[]) cc = CommaSeparatedEmailField(_("Cc"), blank=True, default=[]) bcc = CommaSeparatedEmailField(("Bcc"), blank=True, default=[]) def __str__(self): """String representation.""" return '{self.template}'.format(self=self) @staticmethod def autocomplete_search_fields(): """Auto complete search fields.""" return ( "id__iexact", "template__name__icontains", ) def notify(self, context): """Notify with given context.""" mail.send(template=self.template.name, recipients=self.to, sender=self.from_email, context=context)
class Email(models.Model): """ A model to hold email information. """ PRIORITY_CHOICES = [(PRIORITY.low, _("low")), (PRIORITY.medium, _("medium")), (PRIORITY.high, _("high")), (PRIORITY.now, _("now"))] STATUS_CHOICES = [(STATUS.sent, _("sent")), (STATUS.failed, _("failed")), (STATUS.queued, _("queued"))] from_email = models.CharField(_("Email From"), max_length=254, validators=[validate_email_with_name]) to = CommaSeparatedEmailField(_("Email To")) cc = CommaSeparatedEmailField(_("Cc")) bcc = CommaSeparatedEmailField(("Bcc")) subject = models.CharField(_("Subject"), max_length=989, blank=True) message = models.TextField(_("Message"), blank=True) html_message = models.TextField(_("HTML Message"), blank=True) """ Emails with 'queued' status will get processed by ``send_queued`` command. Status field will then be set to ``failed`` or ``sent`` depending on whether it's successfully delivered. """ status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, db_index=True, blank=True, null=True) priority = models.PositiveSmallIntegerField(choices=PRIORITY_CHOICES, blank=True, null=True) created = models.DateTimeField(auto_now_add=True, db_index=True) last_updated = models.DateTimeField(db_index=True, auto_now=True) scheduled_time = models.DateTimeField(blank=True, null=True, db_index=True) headers = JSONField(blank=True, null=True) template = models.ForeignKey('post_office.EmailTemplate', blank=True, null=True) context = context_field_class(blank=True, null=True) backend_alias = models.CharField(blank=True, default='', max_length=64) class Meta: app_label = 'post_office' def __str__(self): return u'%s' % self.to def email_message(self, connection=None): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object, depending on whether html_message is empty. """ subject = smart_text(self.subject) if self.template is not None and not self.html_message: _context = Context(self.context) subject = Template(self.template.subject).render(_context) message = Template(self.template.content).render(_context) html_message = Template( self.template.html_content).render(_context) else: subject = self.subject message = self.message html_message = self.html_message if html_message: msg = EmailMultiAlternatives(subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, connection=connection, headers=self.headers) msg.attach_alternative(html_message, "text/html") else: msg = EmailMessage(subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, connection=connection, headers=self.headers) for attachment in self.attachments.all(): msg.attach(attachment.name, attachment.file.read()) return msg def dispatch(self, log_level=None, disconnect_after_delivery=True): """ Actually send out the email and log the result """ if log_level is None: log_level = get_log_level() connection = None try: connection = connections[self.backend_alias or 'default'] self.email_message(connection=connection).send() status = STATUS.sent message = '' exception_type = '' except Exception as e: status = STATUS.failed message = str(e) exception_type = type(e).__name__ if connection and disconnect_after_delivery: connection.close() self.status = status self.save() # If log level is 0, log nothing, 1 logs only sending failures # and 2 means log both successes and failures if log_level == 1: if status == STATUS.failed: self.logs.create(status=status, message=message, exception_type=exception_type) elif log_level == 2: self.logs.create(status=status, message=message, exception_type=exception_type) return status def save(self, *args, **kwargs): self.full_clean() return super(Email, self).save(*args, **kwargs)
class Email(models.Model): """ A model to hold email information. """ PRIORITY_CHOICES = [(PRIORITY.low, _("low")), (PRIORITY.medium, _("medium")), (PRIORITY.high, _("high")), (PRIORITY.now, _("now"))] STATUS_CHOICES = [(STATUS.sent, _("sent")), (STATUS.failed, _("failed")), (STATUS.queued, _("queued")), (STATUS.requeued, _("requeued"))] from_email = models.CharField(_("Email From"), max_length=254, validators=[validate_email_with_name]) to = CommaSeparatedEmailField(_("Email To")) cc = CommaSeparatedEmailField(_("Cc")) bcc = CommaSeparatedEmailField(_("Bcc")) subject = models.CharField(_("Subject"), max_length=989, blank=True) message = models.TextField(_("Message"), blank=True) html_message = models.TextField(_("HTML Message"), blank=True) """ Emails with 'queued' status will get processed by ``send_queued`` command. Status field will then be set to ``failed`` or ``sent`` depending on whether it's successfully delivered. """ status = models.PositiveSmallIntegerField(_("Status"), choices=STATUS_CHOICES, db_index=True, blank=True, null=True) priority = models.PositiveSmallIntegerField(_("Priority"), choices=PRIORITY_CHOICES, blank=True, null=True) created = models.DateTimeField(auto_now_add=True, db_index=True) last_updated = models.DateTimeField(db_index=True, auto_now=True) scheduled_time = models.DateTimeField( _("Scheduled Time"), blank=True, null=True, db_index=True, help_text=_("The scheduled sending time")) expires_at = models.DateTimeField( _("Expires"), blank=True, null=True, help_text=_("Email won't be sent after this timestamp")) number_of_retries = models.PositiveIntegerField(null=True, blank=True) headers = JSONField(_('Headers'), blank=True, null=True) template = models.ForeignKey('post_office.EmailTemplate', blank=True, null=True, verbose_name=_("Email template"), on_delete=models.CASCADE) context = context_field_class(_('Context'), blank=True, null=True) backend_alias = models.CharField(_("Backend alias"), blank=True, default='', max_length=64) class Meta: app_label = 'post_office' verbose_name = pgettext_lazy("Email address", "Email") verbose_name_plural = pgettext_lazy("Email addresses", "Emails") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._cached_email_message = None def __str__(self): return '%s' % self.to def email_message(self): """ Returns Django EmailMessage object for sending. """ if self._cached_email_message: return self._cached_email_message return self.prepare_email_message() def prepare_email_message(self): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object, depending on whether html_message is empty. """ if get_override_recipients(): self.to = get_override_recipients() if self.template is not None: engine = get_template_engine() subject = engine.from_string(self.template.subject).render( self.context) plaintext_message = engine.from_string( self.template.content).render(self.context) multipart_template = engine.from_string(self.template.html_content) html_message = multipart_template.render(self.context) else: subject = smart_str(self.subject) plaintext_message = self.message multipart_template = None html_message = self.html_message connection = connections[self.backend_alias or 'default'] if isinstance(self.headers, dict) or self.expires_at: headers = dict(self.headers or {}) if self.expires_at: headers.update({ 'Expires': self.expires_at.strftime("%a, %-d %b %H:%M:%S %z") }) else: headers = None if html_message: if plaintext_message: msg = EmailMultiAlternatives(subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) msg.attach_alternative(html_message, "text/html") else: msg = EmailMultiAlternatives(subject=subject, body=html_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) msg.content_subtype = 'html' if hasattr(multipart_template, 'attach_related'): multipart_template.attach_related(msg) else: msg = EmailMessage(subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) for attachment in self.attachments.all(): if attachment.headers: mime_part = MIMENonMultipart(*attachment.mimetype.split('/')) mime_part.set_payload(attachment.file.read()) for key, val in attachment.headers.items(): try: mime_part.replace_header(key, val) except KeyError: mime_part.add_header(key, val) msg.attach(mime_part) else: msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype or None) attachment.file.close() self._cached_email_message = msg return msg def dispatch(self, log_level=None, disconnect_after_delivery=True, commit=True): """ Sends email and log the result. """ try: self.email_message().send() status = STATUS.sent message = '' exception_type = '' except Exception as e: status = STATUS.failed message = str(e) exception_type = type(e).__name__ # If run in a bulk sending mode, reraise and let the outer # layer handle the exception if not commit: raise if commit: self.status = status self.save(update_fields=['status']) if log_level is None: log_level = get_log_level() # If log level is 0, log nothing, 1 logs only sending failures # and 2 means log both successes and failures if log_level == 1: if status == STATUS.failed: self.logs.create(status=status, message=message, exception_type=exception_type) elif log_level == 2: self.logs.create(status=status, message=message, exception_type=exception_type) return status def clean(self): if self.scheduled_time and self.expires_at and self.scheduled_time > self.expires_at: raise ValidationError( _("The scheduled time may not be later than the expires time.") ) def save(self, *args, **kwargs): self.full_clean() return super().save(*args, **kwargs)
class Email(models.Model): """ A model to hold email information. """ PRIORITY_CHOICES = [(PRIORITY.low, _("low")), (PRIORITY.medium, _("medium")), (PRIORITY.high, _("high")), (PRIORITY.now, _("now"))] STATUS_CHOICES = [(STATUS.sent, _("sent")), (STATUS.failed, _("failed")), (STATUS.queued, _("queued"))] from_email = models.CharField(_("Email From"), max_length=254, validators=[validate_email_with_name]) to = CommaSeparatedEmailField(_("Email To")) cc = CommaSeparatedEmailField(_("Cc")) bcc = CommaSeparatedEmailField(_("Bcc")) subject = models.CharField(_("Subject"), max_length=989, blank=True) message = models.TextField(_("Message"), blank=True) html_message = models.TextField(_("HTML Message"), blank=True) """ Emails with 'queued' status will get processed by ``send_queued`` command. Status field will then be set to ``failed`` or ``sent`` depending on whether it's successfully delivered. """ status = models.PositiveSmallIntegerField( _("Status"), choices=STATUS_CHOICES, db_index=True, blank=True, null=True) priority = models.PositiveSmallIntegerField(_("Priority"), choices=PRIORITY_CHOICES, blank=True, null=True) created = models.DateTimeField(auto_now_add=True, db_index=True) last_updated = models.DateTimeField(db_index=True, auto_now=True) scheduled_time = models.DateTimeField(_('The scheduled sending time'), blank=True, null=True, db_index=True) headers = JSONField(_('Headers'), blank=True, null=True) template = models.ForeignKey('post_office.EmailTemplate', blank=True, null=True, verbose_name=_('Email template'), on_delete=models.CASCADE) context = context_field_class(_('Context'), blank=True, null=True) backend_alias = models.CharField(_('Backend alias'), blank=True, default='', max_length=64) site = models.ForeignKey( sites_models.Site, null=True, blank=True, related_name='emails', verbose_name=_('Site'), help_text=_('Related site object.') ) objects = EmailManager() class Meta: app_label = 'post_office' verbose_name = pgettext_lazy("Email address", "Email") verbose_name_plural = pgettext_lazy("Email addresses", "Emails") def __init__(self, *args, **kwargs): super(Email, self).__init__(*args, **kwargs) self._cached_email_message = None def __str__(self): return u'%s' % self.to def email_message(self): """ Returns Django EmailMessage object for sending. """ if self._cached_email_message: return self._cached_email_message return self.prepare_email_message() def prepare_email_message(self): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object, depending on whether html_message is empty. """ subject = smart_text(self.subject) if self.template is not None: _context = Context(self.context) subject = Template(self.template.subject).render(_context) message = Template(self.template.content).render(_context) html_message = Template(self.template.html_content).render(_context) else: subject = self.subject message = self.message html_message = self.html_message connection = connections[self.backend_alias or 'default'] if html_message: msg = EmailMultiAlternatives( subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=self.headers, connection=connection) msg.attach_alternative(html_message, "text/html") else: msg = EmailMessage( subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=self.headers, connection=connection) for attachment in self.attachments.all(): try: msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype or None) attachment.file.close() except Exception as e: logger.exception('failed to attach file: %s' % e) self._cached_email_message = msg return msg def dispatch(self, log_level=None, disconnect_after_delivery=True, commit=True): """ Sends email and log the result. """ try: self.email_message().send() status = STATUS.sent message = '' exception_type = '' except Exception as e: status = STATUS.failed message = str(e) exception_type = type(e).__name__ # If run in a bulk sending mode, reraise and let the outer # layer handle the exception if not commit: raise if commit: self.status = status self.save(update_fields=['status']) if log_level is None: log_level = get_log_level() # If log level is 0, log nothing, 1 logs only sending failures # and 2 means log both successes and failures if log_level == 1: if status == STATUS.failed: self.logs.create(status=status, message=message, exception_type=exception_type) elif log_level == 2: self.logs.create(status=status, message=message, exception_type=exception_type) return status def save(self, *args, **kwargs): site = sites_models.Site.objects.get_current() if site.pk: self.site = site self.full_clean() return super(Email, self).save(*args, **kwargs)
class Email(models.Model): """ A model to hold email information. """ PRIORITY_CHOICES = [(PRIORITY.low, 'low'), (PRIORITY.medium, 'medium'), (PRIORITY.high, 'high'), (PRIORITY.now, 'now')] STATUS_CHOICES = [(STATUS.sent, 'sent'), (STATUS.failed, 'failed'), (STATUS.queued, 'queued')] from_email = models.CharField(max_length=254, validators=[validate_email_with_name]) to = CommaSeparatedEmailField() cc = CommaSeparatedEmailField() bcc = CommaSeparatedEmailField() subject = models.CharField(max_length=255, blank=True) message = models.TextField(blank=True) html_message = models.TextField(blank=True) """ Emails with 'queued' status will get processed by ``send_queued`` command. Status field will then be set to ``failed`` or ``sent`` depending on whether it's successfully delivered. """ status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, db_index=True, blank=True, null=True) priority = models.PositiveSmallIntegerField(choices=PRIORITY_CHOICES, blank=True, null=True) created = models.DateTimeField(auto_now_add=True, db_index=True) last_updated = models.DateTimeField(db_index=True, auto_now=True) scheduled_time = models.DateTimeField(blank=True, null=True, db_index=True) headers = JSONField(blank=True, null=True) template = models.ForeignKey('post_office.EmailTemplate', blank=True, null=True) context = context_field_class(blank=True, null=True) def __unicode__(self): return u'%s' % self.to def email_message(self, connection=None): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object from a ``Message`` instance, depending on whether html_message is empty. """ subject = smart_text(self.subject) if self.template is not None: _context = Context(self.context) subject = Template(self.template.subject).render(_context) message = Template(self.template.content).render(_context) html_message = Template( self.template.html_content).render(_context) else: subject = self.subject message = self.message html_message = self.html_message if html_message: msg = EmailMultiAlternatives(subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, connection=connection, headers=self.headers) msg.attach_alternative(html_message, "text/html") else: msg = EmailMessage(subject=subject, body=message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, connection=connection, headers=self.headers) for attachment in self.attachments.all(): msg.attach(attachment.name, attachment.file.read()) return msg def dispatch(self, connection=None, log_level=None): """ Actually send out the email and log the result """ connection_opened = False if log_level is None: log_level = get_log_level() try: if connection is None: connection = get_connection(get_email_backend()) connection.open() connection_opened = True self.email_message(connection=connection).send() status = STATUS.sent message = '' exception_type = '' if connection_opened: connection.close() except Exception: status = STATUS.failed exception, message, _ = sys.exc_info() exception_type = exception.__name__ self.status = status self.save() # If log level is 0, log nothing, 1 logs only sending failures # and 2 means log both successes and failures if log_level == 1: if status == STATUS.failed: self.logs.create(status=status, message=message, exception_type=exception_type) elif log_level == 2: self.logs.create(status=status, message=message, exception_type=exception_type) return status def save(self, *args, **kwargs): self.full_clean() return super(Email, self).save(*args, **kwargs)
class Email(models.Model): """ A model to hold email information. """ from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.db.models import Q from django.conf import settings User = get_user_model() PRIORITY_CHOICES = [(PRIORITY.low, _("low")), (PRIORITY.medium, _("medium")), (PRIORITY.high, _("high")), (PRIORITY.now, _("now"))] STATUS_CHOICES = [(STATUS.sent, _("sent")), (STATUS.failed, _("failed")), (STATUS.queued, _("queued"))] from_email = models.CharField(_("Email From"), max_length=254, validators=[validate_email_with_name], help_text=_("Must have permission to send through the default server!")) group = models.ForeignKey('auth.group', blank=True, null=True, help_text=_("Send email to all members of a group."), verbose_name=_('Send to group'), on_delete=models.SET_NULL) to = CommaSeparatedEmailField(_("Email To"), help_text=_("Optional (if no group of recipients selected), separate addresses with a comma.")) cc = CommaSeparatedEmailField(_("Email Cc"), help_text=_("Optional, separate multiple addresses with a comma.")) bcc = CommaSeparatedEmailField(_("Email Bcc"), help_text=_("Optional, separate multiple addresses with a comma.")) subject = models.CharField(_("Subject"), max_length=989, blank=True, help_text=_('Leave this blank if sending content by a template!')) message = models.TextField(_("Text Content"), blank=True, help_text=_('Leave this blank if sending content by a template!')) html_message = models.TextField(_("HTML Content"), blank=True, help_text=_('Leave this blank if sending content by a template!')) """ Emails with 'queued' status will get processed by ``send_queued`` command. Status field will then be set to ``failed`` or ``sent`` depending on whether it's successfully delivered. """ status = models.PositiveSmallIntegerField( _("Status"), choices=STATUS_CHOICES, db_index=True, blank=True, null=True) priority = models.PositiveSmallIntegerField(_("Priority"), choices=PRIORITY_CHOICES, blank=True, null=True) created = models.DateTimeField(auto_now_add=True, db_index=True) last_updated = models.DateTimeField(db_index=True, auto_now=True) scheduled_time = models.DateTimeField(_('Scheduled sending time'), blank=True, null=True, db_index=True) headers = JSONField(_('Headers'), blank=True, null=True) template = models.ForeignKey('post_office.EmailTemplate', blank=True, help_text=_('If selected, will replace subject, plain-text content, and html content with content from the chosen template.'), null=True, verbose_name=_('Email template'), on_delete=models.CASCADE) context = context_field_class(_('Context'), blank=True, null=True) backend_alias = models.CharField(_('Backend alias'), blank=True, default='', max_length=64) class Meta: app_label = 'post_office' verbose_name = pgettext_lazy("Email address", "Email") verbose_name_plural = pgettext_lazy("Email addresses", "Emails") def __init__(self, *args, **kwargs): super(Email, self).__init__(*args, **kwargs) self._cached_email_message = None def __str__(self): return u'%s' % self.to def email_message(self): """ Returns Django EmailMessage object for sending. """ if self._cached_email_message: return self._cached_email_message return self.prepare_email_message() def prepare_email_message(self): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object, depending on whether html_message is empty. """ if get_override_recipients(): self.to = get_override_recipients() if self.template is not None: engine = get_template_engine() subject = engine.from_string(self.template.subject).render(self.context) plaintext_message = engine.from_string(self.template.content).render(self.context) multipart_template = engine.from_string(self.template.html_content) html_message = multipart_template.render(self.context) else: subject = smart_text(self.subject) plaintext_message = self.message multipart_template = None html_message = self.html_message connection = connections[self.backend_alias or 'default'] if html_message: if plaintext_message: msg = EmailMultiAlternatives( subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=self.headers, connection=connection) msg.attach_alternative(html_message, "text/html") else: msg = EmailMultiAlternatives( subject=subject, body=html_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=self.headers, connection=connection) msg.content_subtype = 'html' if hasattr(multipart_template, 'attach_related'): multipart_template.attach_related(msg) else: msg = EmailMessage( subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=self.headers, connection=connection) for attachment in self.attachments.all(): if attachment.headers: mime_part = MIMENonMultipart(*attachment.mimetype.split('/')) mime_part.set_payload(attachment.file.read()) for key, val in attachment.headers.items(): try: mime_part.replace_header(key, val) except KeyError: mime_part.add_header(key, val) msg.attach(mime_part) else: msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype or None) attachment.file.close() self._cached_email_message = msg return msg def dispatch(self, log_level=None, disconnect_after_delivery=True, commit=True): """ Sends email and log the result. """ try: self.email_message().send() status = STATUS.sent message = '' exception_type = '' except Exception as e: status = STATUS.failed message = str(e) exception_type = type(e).__name__ # If run in a bulk sending mode, reraise and let the outer # layer handle the exception if not commit: raise if commit: self.status = status self.save(update_fields=['status']) if log_level is None: log_level = get_log_level() # If log level is 0, log nothing, 1 logs only sending failures # and 2 means log both successes and failures if log_level == 1: if status == STATUS.failed: self.logs.create(status=status, message=message, exception_type=exception_type) elif log_level == 2: self.logs.create(status=status, message=message, exception_type=exception_type) return status def save(self, *args, **kwargs): self.full_clean() return super(Email, self).save(*args, **kwargs)