svthalia/concrexit

View on GitHub
website/members/forms.py

Summary

Maintainability
A
1 hr
Test Coverage
"""Forms defined by the members package."""
from django import forms
from django.apps import apps
from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _

from members.models import Member
from thabloid.models.thabloid_user import ThabloidUser

from .models import Profile


class ProfileForm(forms.ModelForm):
    """Form with all the user editable fields of a Profile model.

    If the profile is minimized, no fields are required, unless the `require_address`
    keyword argument is True, which is set if a user is filling a minimized profile
    in order to be able to create a Renewal.
    """

    birthday = forms.DateField()

    class Meta:
        fields = [
            "birthday",
            "show_birthday",
            "address_street",
            "address_street2",
            "address_postal_code",
            "address_city",
            "address_country",
            "phone_number",
            "emergency_contact",
            "emergency_contact_phone_number",
            "website",
            "profile_description",
            "nickname",
            "initials",
            "display_name_preference",
            "photo",
            "receive_optin",
            "receive_newsletter",
            "receive_registration_confirmation",
            "receive_oldmembers",
            "email_gsuite_only",
        ]
        model = Profile

    def __init__(self, *args, require_address=False, **kwargs):
        super().__init__(*args, **kwargs)
        user = Member.objects.get(pk=kwargs["instance"].user_id)
        for field in [
            "birthday",
            "address_street",
            "address_city",
            "address_postal_code",
            "address_city",
            "address_country",
        ]:
            if require_address or not user.profile.is_minimized:
                self.fields[field].required = True
            else:
                self.fields[field].required = False

        if not kwargs["instance"].user.is_staff:
            self.fields["email_gsuite_only"].widget = self.fields[
                "email_gsuite_only"
            ].hidden_widget()

        if not user.has_been_member() and not kwargs["instance"].receive_oldmembers:
            self.fields["receive_oldmembers"].disabled = True
            self.fields["receive_oldmembers"].help_text = (
                "If you are a past member, receive emails about Thalia events aimed at alumni. "
                "You cannot enable this option, as we don't have any records of you having been a member "
                "(a long time ago, we didn't keep track of this yet). Contact "
                "<a href='mailto:info@thalia.nu'>info@thalia.nu</a> if you want to receive alumni emails."
            )

        self.fields["birthday"].widget.input_type = "date"
        if not user.profile.is_minimized:
            self.fields["birthday"].disabled = True

        self.render_app_specific_profile_form_fields()

    def render_app_specific_profile_form_fields(self):
        """Render app-specific profile form fields."""
        for app in apps.get_app_configs():
            if hasattr(app, "user_profile_form_fields"):
                fields, _ = app.user_profile_form_fields(self.instance)
                self.fields.update(fields)

    def save(self, commit=True):
        instance = super().save(commit)
        if commit:
            if self.cleaned_data["receive_thabloid"]:
                ThabloidUser.objects.get(pk=instance.user.pk).allow_thabloid()
            else:
                ThabloidUser.objects.get(pk=instance.user.pk).disallow_thabloid()

        # Save app-specific fields by calling the callback that was registered
        for app in apps.get_app_configs():
            if hasattr(app, "user_profile_form_fields"):
                _, callback = app.user_profile_form_fields()
                callback(self, instance, commit)

        return instance

    def clean(self):
        if self.cleaned_data.get("phone_number") is not None:
            self.cleaned_data["phone_number"] = self.cleaned_data[
                "phone_number"
            ].replace(" ", "")

        if self.cleaned_data.get("emergency_contact_phone_number") is not None:
            self.cleaned_data["emergency_contact_phone_number"] = self.cleaned_data[
                "emergency_contact_phone_number"
            ].replace(" ", "")
        super().clean()


class UserCreationForm(BaseUserCreationForm):
    """Custom Form that lowercases the username on creation."""

    def clean(self):
        if "username" in self.cleaned_data:
            self.cleaned_data["username"] = self.cleaned_data["username"].lower()

        super().clean()

    class Meta:
        fields = ("username", "first_name", "last_name")


class UserChangeForm(BaseUserChangeForm):
    """Custom user edit form that adds fields for first/last name and email.

    It also force-lowercases the username on save
    """

    username = forms.CharField(
        label=_("Username"),
        required=True,
        help_text=_("Required. 64 characters or fewer. Letters and digits only."),
        widget=forms.TextInput(attrs={"class": "vTextField", "maxlength": 64}),
        validators=[
            RegexValidator(
                regex="^[a-zA-Z0-9]{1,64}$",
                message=_(
                    "Please use 64 characters or fewer. Letters and digits only."
                ),
            )
        ],
    )

    first_name = forms.CharField(
        label=_("First name"),
        required=True,
        widget=forms.TextInput(attrs={"class": "vTextField", "maxlength": 30}),
    )
    last_name = forms.CharField(
        label=_("Last name"),
        required=True,
        widget=forms.TextInput(attrs={"class": "vTextField", "maxlength": 150}),
    )
    email = forms.CharField(
        label=_("Email address"),
        required=True,
        widget=forms.EmailInput(attrs={"class": "vTextField", "maxlength": 254}),
    )

    def clean(self):
        if "username" in self.cleaned_data:
            self.cleaned_data["username"] = self.cleaned_data["username"].lower()
        super().clean()