from django import forms from django.contrib.auth import password_validation from .models import Invitation, User, normalize_email class LoginForm(forms.Form): email = forms.EmailField(label="E-Mail", widget=forms.EmailInput(attrs={"autocomplete": "email", "autofocus": True})) password = forms.CharField(label="Passwort", strip=False, widget=forms.PasswordInput(attrs={"autocomplete": "current-password"})) def clean_email(self): return normalize_email(self.cleaned_data["email"]) class SetPasswordMixin(forms.Form): """Two password fields validated with Django's password validators.""" password1 = forms.CharField( label="Neues Passwort", strip=False, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), help_text="Mindestens 10 Zeichen, nicht nur Ziffern, kein gängiges Passwort.", ) password2 = forms.CharField( label="Passwort wiederholen", strip=False, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}) ) def password_user(self): """User instance the new password is checked against (similarity validator).""" return None def clean(self): cleaned = super().clean() p1, p2 = cleaned.get("password1"), cleaned.get("password2") if p1 and p2 and p1 != p2: self.add_error("password2", "Die Passwörter stimmen nicht überein.") elif p1: try: password_validation.validate_password(p1, self.password_user()) except forms.ValidationError as error: self.add_error("password1", error) return cleaned class RegistrationForm(SetPasswordMixin): email = forms.EmailField(label="E-Mail", widget=forms.EmailInput(attrs={"autocomplete": "email"})) display_name = forms.CharField(label="Anzeigename", max_length=80, widget=forms.TextInput(attrs={"autocomplete": "nickname"})) field_order = ["email", "display_name", "password1", "password2"] def __init__(self, *args, invitation: Invitation, **kwargs): super().__init__(*args, **kwargs) self.invitation = invitation if invitation.email: self.fields["email"].initial = invitation.email self.fields["email"].disabled = True def clean_email(self): email = normalize_email(self.invitation.email or self.cleaned_data["email"]) if User.objects.filter(email=email).exists(): raise forms.ValidationError("Für diese E-Mail-Adresse gibt es bereits ein Konto.") return email def password_user(self): return User(email=self.cleaned_data.get("email", ""), display_name=self.cleaned_data.get("display_name", "")) class UserPasswordForm(SetPasswordMixin): def __init__(self, *args, user, **kwargs): super().__init__(*args, **kwargs) self.user = user def password_user(self): return self.user class PasswordChangeForm(UserPasswordForm): old_password = forms.CharField( label="Aktuelles Passwort", strip=False, widget=forms.PasswordInput(attrs={"autocomplete": "current-password"}) ) field_order = ["old_password", "password1", "password2"] def clean_old_password(self): password = self.cleaned_data["old_password"] if not self.user.check_password(password): raise forms.ValidationError("Das aktuelle Passwort ist falsch.") return password class ProfileForm(forms.ModelForm): class Meta: model = User fields = ["display_name"] class InvitationForm(forms.ModelForm): valid_days = forms.IntegerField(label="Gültig (Tage)", min_value=1, max_value=60, initial=7) class Meta: model = Invitation fields = ["email", "role", "note"] def clean_email(self): email = normalize_email(self.cleaned_data["email"]) if email and User.objects.filter(email=email).exists(): raise forms.ValidationError("Für diese E-Mail-Adresse gibt es bereits ein Konto.") return email