Plain is headed towards 1.0! Subscribe for development updates →

plain-support

Captcha...

Security considerations

Most support forms allow you to type in an email address. Be careful, because anybody can pretend to be anybody else at this point. Converations either need to continue over email (which confirms they have access to the email account), or include a verification step (emailing a code to the email address, for example).

 1from plain.auth import get_user_model
 2from plain.mail import TemplateEmail
 3from plain.models.forms import ModelForm
 4from plain.runtime import settings
 5
 6from .models import SupportFormEntry
 7
 8
 9class SupportForm(ModelForm):
10    """
11    The form is the customization point for users.
12    So any behavior modifications should be possible here.
13    """
14
15    class Meta:
16        model = SupportFormEntry
17        fields = ["name", "email", "message"]
18
19    def __init__(self, user, form_slug, *args, **kwargs):
20        super().__init__(*args, **kwargs)
21        self.user = user  # User provided directly by authed request
22        self.form_slug = form_slug
23        if self.user:
24            self.fields["email"].initial = user.email
25
26    def find_user(self):
27        # If the user isn't logged in (typical in an iframe, depending on session cookie settings),
28        # we can still try to look them up by email
29        # to associate the entry with them.
30        #
31        # Note that since they aren't logged in, this doesn't necessarily
32        # confirm that this wasn't an impersonation attempt.
33        # Subsequent conversations over email will confirm that they have access to the email.
34        email = self.cleaned_data.get("email")
35        if not email:
36            return None
37        UserModel = get_user_model()
38        try:
39            return UserModel.objects.get(email=email)
40        except UserModel.DoesNotExist:
41            return
42
43    def save(self, commit=True):
44        instance = super().save(commit=False)
45        instance.user = self.user or self.find_user()
46        instance.form_slug = self.form_slug
47        if commit:
48            instance.save()
49        return instance
50
51    def notify(self, instance):
52        """
53        Notify the support team of a new support form entry.
54
55        Sends an immediate email by default.
56        """
57        email = TemplateEmail(
58            template="support_form_entry",
59            subject=f"Support request from {instance.name}",
60            to=[settings.SUPPORT_EMAIL],
61            reply_to=[instance.email],
62            context={
63                "support_form_entry": instance,
64            },
65        )
66        email.send()