Coverage for ivatar/ivataraccount/forms.py: 100%
80 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
1# -*- coding: utf-8 -*-
2"""
3Classes for our ivatar.ivataraccount.forms
4"""
5from urllib.parse import urlsplit, urlunsplit
7from django import forms
8from django.utils.translation import gettext_lazy as _
10from ipware import get_client_ip
12from ivatar import settings
13from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL
14from ivatar.settings import MIN_LENGTH_URL, MAX_LENGTH_URL
15from .models import UnconfirmedEmail, ConfirmedEmail, Photo
16from .models import UnconfirmedOpenId, ConfirmedOpenId
17from .models import UserPreference
20MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT = 5
23class AddEmailForm(forms.Form):
24 """
25 Form to handle adding email addresses
26 """
28 email = forms.EmailField(
29 label=_("Email"),
30 min_length=MIN_LENGTH_EMAIL,
31 max_length=MAX_LENGTH_EMAIL,
32 )
34 def clean_email(self):
35 """
36 Enforce lowercase email
37 """
38 # TODO: Domain restriction as in libravatar?
39 return self.cleaned_data["email"].lower()
41 def save(self, request):
42 """
43 Save the model, ensuring some safety
44 """
45 user = request.user
46 # Enforce the maximum number of unconfirmed emails a user can have
47 num_unconfirmed = user.unconfirmedemail_set.count()
49 max_num_unconfirmed_emails = getattr(
50 settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
51 )
53 if num_unconfirmed >= max_num_unconfirmed_emails:
54 self.add_error(None, _("Too many unconfirmed mail addresses!"))
55 return False
57 # Check whether or not a confirmation email has been
58 # sent by this user already
59 if UnconfirmedEmail.objects.filter( # pylint: disable=no-member
60 user=user, email=self.cleaned_data["email"]
61 ).exists():
62 self.add_error("email", _("Address already added, currently unconfirmed"))
63 return False
65 # Check whether or not the email is already confirmed (by someone)
66 check_mail = ConfirmedEmail.objects.filter(email=self.cleaned_data["email"])
67 if check_mail.exists():
68 msg = _("Address already confirmed (by someone else)")
69 if check_mail.first().user == request.user:
70 msg = _("Address already confirmed (by you)")
71 self.add_error("email", msg)
72 return False
74 unconfirmed = UnconfirmedEmail()
75 unconfirmed.email = self.cleaned_data["email"]
76 unconfirmed.user = user
77 unconfirmed.save()
78 unconfirmed.send_confirmation_mail(url=request.build_absolute_uri("/")[:-1])
79 return True
82class UploadPhotoForm(forms.Form):
83 """
84 Form handling photo upload
85 """
87 photo = forms.FileField(
88 label=_("Photo"),
89 error_messages={"required": _("You must choose an image to upload.")},
90 )
91 not_porn = forms.BooleanField(
92 label=_("suitable for all ages (i.e. no offensive content)"),
93 required=True,
94 error_messages={
95 "required": _(
96 'We only host "G-rated" images and so this field must be checked.'
97 )
98 },
99 )
100 can_distribute = forms.BooleanField(
101 label=_("can be freely copied"),
102 required=True,
103 error_messages={
104 "required": _(
105 "This field must be checked since we need to be able to distribute photos to third parties."
106 )
107 },
108 )
110 @staticmethod
111 def save(request, data):
112 """
113 Save the model and assign it to the current user
114 """
115 # Link this file to the user's profile
116 photo = Photo()
117 photo.user = request.user
118 photo.ip_address = get_client_ip(request)[0]
119 photo.data = data.read()
120 photo.save()
121 if not photo.pk:
122 return None
123 return photo
126class AddOpenIDForm(forms.Form):
127 """
128 Form to handle adding OpenID
129 """
131 openid = forms.URLField(
132 label=_("OpenID"),
133 min_length=MIN_LENGTH_URL,
134 max_length=MAX_LENGTH_URL,
135 initial="http://",
136 )
138 def clean_openid(self):
139 """
140 Enforce restrictions
141 """
142 # Lowercase hostname port of the URL
143 url = urlsplit(self.cleaned_data["openid"])
144 data = urlunsplit(
145 (url.scheme.lower(), url.netloc.lower(), url.path, url.query, url.fragment)
146 )
148 # TODO: Domain restriction as in libravatar?
149 return data
151 def save(self, user):
152 """
153 Save the model, ensuring some safety
154 """
155 if ConfirmedOpenId.objects.filter( # pylint: disable=no-member
156 openid=self.cleaned_data["openid"]
157 ).exists():
158 self.add_error("openid", _("OpenID already added and confirmed!"))
159 return False
161 if UnconfirmedOpenId.objects.filter( # pylint: disable=no-member
162 openid=self.cleaned_data["openid"]
163 ).exists():
164 self.add_error("openid", _("OpenID already added, but not confirmed yet!"))
165 return False
167 unconfirmed = UnconfirmedOpenId()
168 unconfirmed.openid = self.cleaned_data["openid"]
169 unconfirmed.user = user
170 unconfirmed.save()
172 return unconfirmed.pk
175class UpdatePreferenceForm(forms.ModelForm):
176 """
177 Form for updating user preferences
178 """
180 class Meta: # pylint: disable=too-few-public-methods
181 """
182 Meta class for UpdatePreferenceForm
183 """
185 model = UserPreference
186 fields = ["theme"]
189class UploadLibravatarExportForm(forms.Form):
190 """
191 Form handling libravatar user export upload
192 """
194 export_file = forms.FileField(
195 label=_("Export file"),
196 error_messages={"required": _("You must choose an export file to upload.")},
197 )
198 not_porn = forms.BooleanField(
199 label=_("suitable for all ages (i.e. no offensive content)"),
200 required=True,
201 error_messages={
202 "required": _(
203 'We only host "G-rated" images and so this field must be checked.'
204 )
205 },
206 )
207 can_distribute = forms.BooleanField(
208 label=_("can be freely copied"),
209 required=True,
210 error_messages={
211 "required": _(
212 "This field must be checked since we need to be able to\
213 distribute photos to third parties."
214 )
215 },
216 )
219class DeleteAccountForm(forms.Form):
220 password = forms.CharField(
221 label=_("Password"), required=False, widget=forms.PasswordInput()
222 )