Source code for mails.views

__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"

from django import forms
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.encoding import force_text
from django.views.generic.edit import UpdateView

from .forms import EmailForm, HiddenDataForm


[docs]class MailViewBase: """Send a templated email after being edited by user.""" form_class = None mail_code = None mail_config = {} mail_variables = {} fail_silently = True def __init__(self, *args, **kwargs): if not self.mail_code: raise AttributeError( self.__class__.__name__ + " object has no attribute `mail_code`" ) super().__init__(*args, **kwargs) self.mail_form = None
[docs] def can_send_mail(self): """Overwrite method to control permissions for sending mails.""" return True
[docs] def get_mail_config(self): return self.mail_config
[docs]class MailFormView(MailViewBase, UpdateView): """ MailUpdateView acts as a base class-based form view, but will intercept the POST request of the original form. It'll render the email edit form and save/send both after validation. """
[docs] def get_template_names(self): """The mail editor form has its own template.""" if self.mail_form and not self.mail_form.is_valid(): return ["mails/mail_form.html"] return super().get_template_names()
[docs] def post(self, request, *args, **kwargs): """Save forms or intercept the request.""" self.object = None if hasattr(self, "get_object"): self.object = self.get_object() form = self.get_form() if form.is_valid(): self.mail_form = EmailForm( request.POST or None, csp_nonce=self.request.csp_nonce, mail_code=self.mail_code, instance=self.object, **self.get_mail_config(), **self.mail_variables ) if self.mail_form.is_valid(): return self.form_valid(form) return self.render_to_response( self.get_context_data( form=self.mail_form, transfer_data_form=HiddenDataForm(form) ) ) else: return self.form_invalid(form)
[docs] def form_valid(self, form): """If both the regular form and mailing form are valid, save both.""" response = super().form_valid(form) try: if not self.can_send_mail(): if self.fail_silently: return response else: raise PermissionDenied( "You are not allowed to send mail: %s." % self.mail_code ) self.mail_form.save() except AttributeError: # self.mail_form is None raise AttributeError( "Did you check the order in which %(cls)s inherits MailView?" % { "cls": self.__class__.__name__, } ) messages.success(self.request, "Mail sent") return response
[docs] def get_success_url(self): """ Returns the supplied URL. """ if self.success_url: if hasattr(self, "object") and self.object: url = self.success_url.format(**self.object.__dict__) else: url = force_text(self.success_url) elif hasattr(self, "object") and self.object: try: url = self.object.get_absolute_url() except AttributeError: raise ImproperlyConfigured( "No URL to redirect to. Either provide a url or define" " a get_absolute_url method on the Model." ) else: raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") return url
[docs]class MailView(MailViewBase, UpdateView): form_class = EmailForm template_name = "mails/mail_form.html"
[docs] def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["mail_code"] = self.mail_code kwargs["instance"] = self.get_object() kwargs["csp_nonce"] = self.request.csp_nonce kwargs.update(**self.get_mail_config()) kwargs.update(**self.mail_variables) return kwargs
[docs] def form_valid(self, form): """If both the regular form and mailing form are valid, save both.""" if not self.can_send_mail(): if self.fail_silently: return HttpResponseRedirect(self.get_success_url()) else: raise PermissionDenied( "You are not allowed to send mail: %s." % self.mail_code ) messages.success(self.request, "Mail sent") return super().form_valid(form)
[docs]class MailEditorSubview: """ This subview works as an interrupter for function based views. If a FBV is completed, the MailEditingSubview will interrupt the request and provide a form that give the user the possibility to edit a template based email before sending it. """ template_name = "mails/mail_form.html" def __init__( self, request, mail_code, context=None, header_template=None, **kwargs ): self.mail_code = mail_code self.context = context or {} self.request = request self.header_template = header_template self.mail_form = EmailForm( request.POST or None, csp_nonce=self.request.csp_nonce, mail_code=mail_code, **kwargs ) self._is_valid = False
[docs] def interrupt(self): """ Interrupt request by rendering the templated email form. The `request` should be an HttpRequest instance that should be captured and be included into the response of the interrupted response. Currently only POST requests are supported. """ self.context["form"] = self.mail_form self.context["header_template"] = self.header_template if "object" in self.mail_form.engine.template_variables: self.context["object"] = self.mail_form.engine.template_variables["object"] else: self.context["object"] = None return render(self.request, self.template_name, self.context)
[docs] def is_valid(self): """See if data is returned and valid.""" self._is_valid = self.mail_form.is_valid() return self._is_valid
[docs] def send_mail(self): """Send email as returned by user.""" if not self._is_valid: raise ValueError( "The mail: %s could not be sent because the data didn't validate." % self.mail_code ) return self.mail_form.save()
[docs]class MailEditorSubviewHTMX(MailEditorSubview): template_name = "mails/_hx_mail_form.html"
[docs]class MailEditorMixin: """Deprecated.""" pass