forms.py 5.32 KB
Newer Older
1
import hashlib, random
2 3
from http.client import InvalidURL
from urllib.request import urlopen
4

5
from django import forms
6
from django.conf import settings
7
from django.contrib.auth.forms import AuthenticationForm
8
from django.core.exceptions import ValidationError
9
from django.urls import reverse
10
from django.utils.encoding import force_bytes
11 12
from django.utils.translation import ugettext_lazy, ugettext as _

13
from common.utils import send_mail
14
from teams.models import Team
15
from people.models import Person
16

17

18 19
class RegistrationForm(forms.Form):
    """ Form for user registration """
20
    username = forms.RegexField(max_length=30, regex=r'^\w+$',
Claude Paroz's avatar
Claude Paroz committed
21 22 23
                               label=ugettext_lazy('Choose a username:'),
                               help_text=ugettext_lazy('May contain only letters, numbers, underscores or hyphens'))
    email = forms.EmailField(label=ugettext_lazy('Email:'))
24
    password1 = forms.CharField(widget=forms.PasswordInput(render_value=False),
Claude Paroz's avatar
Claude Paroz committed
25 26
                                label=ugettext_lazy('Password:'), required=False, min_length=7,
                                help_text=ugettext_lazy('At least 7 characters'))
27
    password2 = forms.CharField(widget=forms.PasswordInput(render_value=False),
Claude Paroz's avatar
Claude Paroz committed
28
                                label=ugettext_lazy('Confirm password:'), required=False)
29

30 31 32
    def clean_username(self):
        """  Validate the username (correctness and uniqueness)"""
        try:
33
            Person.objects.get(username__iexact=self.cleaned_data['username'])
34 35
        except Person.DoesNotExist:
            return self.cleaned_data['username']
36
        raise ValidationError(_('This username is already taken. Please choose another.'))
37 38 39 40 41

    def clean(self):
        cleaned_data = self.cleaned_data
        password1 = cleaned_data.get('password1')
        password2 = cleaned_data.get('password2')
42 43
        if not password1:
            raise ValidationError(_('You must provide a password'))
44

45
        if password1 and password1 != password2:
46
            raise ValidationError(_('The passwords do not match'))
47 48
        return cleaned_data

49
    def save(self, request):
50 51 52 53
        """ Create the user """
        username = self.cleaned_data['username']
        email = self.cleaned_data['email']
        password = self.cleaned_data['password1']
54

55 56 57
        new_user = Person.objects.create_user(username=username,
                           email=email,
                           password=password or "!")
58 59
        salt = hashlib.sha1(force_bytes(random.random())).hexdigest()[:5]
        activation_key = hashlib.sha1(force_bytes(salt + username)).hexdigest()
60 61 62 63
        new_user.activation_key = activation_key
        new_user.is_active = False
        new_user.save()
        # Send activation email
64 65 66 67 68 69
        message = _("This is a confirmation that your registration on %s succeeded. To activate your account, please click on the link below or copy and paste it in a browser.") % settings.SITE_DOMAIN
        message += "\n\nhttps://%s%s\n\n" % (
            settings.SITE_DOMAIN,
            str(reverse("register_activation", kwargs={'key': activation_key}))
        )
        message += _("Administrators of %s" % settings.SITE_DOMAIN)
70

71
        send_mail(_('Account activation'), message, to=[email])
72
        return new_user
73

74

75
class LoginForm(AuthenticationForm):
76 77 78 79 80 81 82 83
    def clean_username(self):
        username = self.cleaned_data['username']
        if '@' in username and not Person.objects.filter(username=username).exists():
            try:
                username = Person.objects.filter(email=username).first().username
            except AttributeError:
                pass
        return username
84 85


86
class DetailForm(forms.ModelForm):
87 88
    class Meta:
        model = Person
89
        fields = ('username', 'first_name', 'last_name', 'email', 'image', 'avatar_service',
90
                  'webpage_url', 'irc_nick')
91

92 93 94 95 96 97
    def clean_image(self):
        url = self.cleaned_data['image']
        if not url:
            return
        size = get_image_size(url)
        if size[0]>100 or size[1]>100:
98
            raise ValidationError(_("Image too high or too wide (%(width)%(height)d, maximum is 100×100 pixels)") % {
99
                'width': size[0], 'height': size[1]})
100
        return url
101

102

103 104
class TeamJoinForm(forms.Form):
    def __init__(self, *args, **kwargs):
Claude Paroz's avatar
Claude Paroz committed
105
        super().__init__(*args, **kwargs)
106 107
        # FIXME: exclude team to which user is already member
        self.fields['teams'] = forms.ModelChoiceField(queryset=Team.objects.all())
108 109 110 111

def get_image_size(url):
    """ Returns width and height (as tuple) of the image poited at by the url
        Code partially copied from http://effbot.org/zone/pil-image-size.htm """
112
    from PIL import ImageFile
113 114

    try:
115 116
        im_file = urlopen(url)
    except (IOError, InvalidURL, EOFError, ValueError):
117
        raise ValidationError(_("The URL you provided is not valid"))
118 119
    size = None
    p = ImageFile.Parser()
120 121
    try:
        while 1:
122
            data = im_file.read(1024)
123 124 125 126 127 128
            if not data:
                break
            p.feed(data)
            if p.image:
                size = p.image.size
                break
129
    except Exception as e:
130
        raise ValidationError("Sorry, an error occurred while trying to get image size (%s)" % str(e))
131 132
    finally:
        im_file.close()
133
    if not size:
134
        raise ValidationError(_("The URL you provided seems not to correspond to a valid image"))
135
    return size