forms.py 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

Krophil's avatar
Krophil committed
25
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
Skia's avatar
Skia committed
26
from django import forms
Skia's avatar
Skia committed
27
from django.conf import settings
28 29
from django.db import transaction
from django.core.exceptions import ValidationError
30
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
Skia's avatar
Skia committed
31
from django.utils.translation import ugettext_lazy as _
Sli's avatar
Sli committed
32
from django.utils.translation import ugettext
Skia's avatar
Skia committed
33
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
Skia's avatar
Skia committed
34
from ajax_select.fields import AutoCompleteSelectField
Skia's avatar
Skia committed
35

Skia's avatar
Skia committed
36
import re
Skia's avatar
Skia committed
37

Krophil's avatar
Krophil committed
38 39 40 41 42
from core.models import User, Page, SithFile

from core.utils import resize_image
from io import BytesIO
from PIL import Image
43

Skia's avatar
Skia committed
44

45 46 47 48 49 50 51 52 53 54
# Widgets

class SelectSingle(Select):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_single"
        else:
            attrs = {'class': "select_single"}
        return super(SelectSingle, self).render(name, value, attrs)

Krophil's avatar
Krophil committed
55

56 57 58 59 60 61 62 63
class SelectMultiple(Select):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_multiple"
        else:
            attrs = {'class': "select_multiple"}
        return super(SelectMultiple, self).render(name, value, attrs)

Krophil's avatar
Krophil committed
64

Skia's avatar
Skia committed
65 66 67 68 69 70 71 72
class SelectDateTime(DateTimeInput):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_datetime"
        else:
            attrs = {'class': "select_datetime"}
        return super(SelectDateTime, self).render(name, value, attrs)

Krophil's avatar
Krophil committed
73

74 75 76 77 78 79 80 81
class SelectDate(DateInput):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_date"
        else:
            attrs = {'class': "select_date"}
        return super(SelectDate, self).render(name, value, attrs)

Krophil's avatar
Krophil committed
82

83 84
class MarkdownInput(Textarea):
    def render(self, name, value, attrs=None):
Skia's avatar
Skia committed
85 86
        output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
                 '<div class="markdown_editor">%(content)s</div>' % {
Krophil's avatar
Krophil committed
87 88 89 90
                     'syntax_url': Page.get_page_by_full_name(settings.SITH_CORE_PAGE_SYNTAX).get_absolute_url(),
                     'help_text': _("Help on the syntax"),
                     'content': super(MarkdownInput, self).render(name, value, attrs),
                 }
91 92
        return output

Krophil's avatar
Krophil committed
93

94 95 96 97 98 99 100
class SelectFile(TextInput):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_file"
        else:
            attrs = {'class': "select_file"}
        output = '%(content)s<div name="%(name)s" class="choose_file_widget" title="%(title)s"></div>' % {
Krophil's avatar
Krophil committed
101 102 103 104
            'content': super(SelectFile, self).render(name, value, attrs),
            'title': _("Choose file"),
            'name': name,
        }
Sli's avatar
Sli committed
105
        output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
106 107
        return output

Krophil's avatar
Krophil committed
108

109 110 111 112 113 114 115
class SelectUser(TextInput):
    def render(self, name, value, attrs=None):
        if attrs:
            attrs['class'] = "select_user"
        else:
            attrs = {'class': "select_user"}
        output = '%(content)s<div name="%(name)s" class="choose_user_widget" title="%(title)s"></div>' % {
Krophil's avatar
Krophil committed
116 117 118 119
            'content': super(SelectUser, self).render(name, value, attrs),
            'title': _("Choose user"),
            'name': name,
        }
Sli's avatar
Sli committed
120
        output += '<span name="' + name + '" class="choose_user_button">' + ugettext("Choose user") + '</span>'
121 122
        return output

123
# Forms
Skia's avatar
Skia committed
124

Krophil's avatar
Krophil committed
125

Skia's avatar
Skia committed
126 127 128 129 130 131
class LoginForm(AuthenticationForm):
    def __init__(self, *arg, **kwargs):
        if 'data' in kwargs.keys():
            from counter.models import Customer
            data = kwargs['data'].copy()
            account_code = re.compile(r"^[0-9]+[A-Za-z]$")
Skia's avatar
Skia committed
132 133 134 135 136 137 138
            try:
                if account_code.match(data['username']):
                    user = Customer.objects.filter(account_id__iexact=data['username']).first().user
                elif '@' in data['username']:
                    user = User.objects.filter(email__iexact=data['username']).first()
                else:
                    user = User.objects.filter(username=data['username']).first()
Skia's avatar
Skia committed
139
                data['username'] = user.username
Krophil's avatar
Krophil committed
140 141
            except:
                pass
Skia's avatar
Skia committed
142 143 144 145
            kwargs['data'] = data
        super(LoginForm, self).__init__(*arg, **kwargs)
        self.fields['username'].label = _("Username, email, or account number")

Krophil's avatar
Krophil committed
146

Skia's avatar
Skia committed
147 148 149
class RegisteringForm(UserCreationForm):
    error_css_class = 'error'
    required_css_class = 'required'
Krophil's avatar
Krophil committed
150

Skia's avatar
Skia committed
151 152
    class Meta:
        model = User
153
        fields = ('first_name', 'last_name', 'email')
Skia's avatar
Skia committed
154 155 156 157 158 159 160 161 162

    def save(self, commit=True):
        user = super(RegisteringForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        user.generate_username()
        if commit:
            user.save()
        return user

Skia's avatar
Skia committed
163

164 165 166 167 168 169 170 171 172
class UserProfileForm(forms.ModelForm):
    """
    Form handling the user profile, managing the files
    This form is actually pretty bad and was made in the rush before the migration. It should be refactored.
    TODO: refactor this form
    """
    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', 'profile_pict', 'avatar_pict',
Krophil's avatar
Krophil committed
173 174 175
                  'scrub_pict', 'sex', 'second_email', 'address', 'parent_address', 'phone', 'parent_phone',
                  'tshirt_size', 'role', 'department', 'dpt_option', 'semester', 'quote', 'school', 'promo',
                  'forum_signature', 'is_subscriber_viewable']
176
        widgets = {
Krophil's avatar
Krophil committed
177 178 179 180 181 182 183
            'date_of_birth': SelectDate,
            'profile_pict': forms.ClearableFileInput,
            'avatar_pict': forms.ClearableFileInput,
            'scrub_pict': forms.ClearableFileInput,
            'phone': PhoneNumberInternationalFallbackWidget,
            'parent_phone': PhoneNumberInternationalFallbackWidget,
        }
184
        labels = {
Krophil's avatar
Krophil committed
185 186 187 188
            'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"),
            'avatar_pict': _("Avatar: used on the forum"),
            'scrub_pict': _("Scrub: let other know how your scrub looks like!"),
        }
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

    def __init__(self, *arg, **kwargs):
        super(UserProfileForm, self).__init__(*arg, **kwargs)

    def full_clean(self):
        super(UserProfileForm, self).full_clean()

    def generate_name(self, field_name, f):
        field_name = field_name[:-4]
        return field_name + str(self.instance.id) + "." + f.content_type.split('/')[-1]

    def process(self, files):
        avatar = self.instance.avatar_pict
        profile = self.instance.profile_pict
        scrub = self.instance.scrub_pict
        self.full_clean()
        self.cleaned_data['avatar_pict'] = avatar
        self.cleaned_data['profile_pict'] = profile
        self.cleaned_data['scrub_pict'] = scrub
        parent = SithFile.objects.filter(parent=None, name="profiles").first()
Krophil's avatar
Krophil committed
209
        for field, f in files:
210 211
            with transaction.atomic():
                try:
Skia's avatar
Skia committed
212 213
                    im = Image.open(BytesIO(f.read()))
                    new_file = SithFile(parent=parent, name=self.generate_name(field, f),
Krophil's avatar
Krophil committed
214 215 216
                                        file=resize_image(im, 400, f.content_type.split('/')[-1]),
                                        owner=self.instance, is_folder=False, mime_type=f.content_type, size=f._size,
                                        moderator=self.instance, is_moderated=True)
Skia's avatar
Skia committed
217
                    new_file.file.name = new_file.name
218 219 220 221 222 223 224 225 226 227
                    old = SithFile.objects.filter(parent=parent, name=new_file.name).first()
                    if old:
                        old.delete()
                    new_file.clean()
                    new_file.save()
                    self.cleaned_data[field] = new_file
                    self._errors.pop(field, None)
                except ValidationError as e:
                    self._errors.pop(field, None)
                    self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
Krophil's avatar
Krophil committed
228
                                   {'file_name': f, 'msg': str(e.message)})
Skia's avatar
Skia committed
229 230 231
                except IOError:
                    self._errors.pop(field, None)
                    self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
Krophil's avatar
Krophil committed
232
                                   {'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
233
        self._post_clean()
Skia's avatar
Skia committed
234

Krophil's avatar
Krophil committed
235

Skia's avatar
Skia committed
236
class UserPropForm(forms.ModelForm):
Skia's avatar
Skia committed
237 238
    error_css_class = 'error'
    required_css_class = 'required'
Krophil's avatar
Krophil committed
239

Skia's avatar
Skia committed
240 241
    class Meta:
        model = User
242
        fields = ['groups']
Skia's avatar
Skia committed
243 244 245
        help_texts = {
            'groups': "Which groups this user belongs to",
        }
Skia's avatar
Skia committed
246 247 248
        widgets = {
            'groups': CheckboxSelectMultiple,
        }
Skia's avatar
Skia committed
249

Krophil's avatar
Krophil committed
250

Skia's avatar
Skia committed
251 252 253 254
class UserGodfathersForm(forms.Form):
    type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
    user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)

Krophil's avatar
Krophil committed
255

256 257 258
class PagePropForm(forms.ModelForm):
    error_css_class = 'error'
    required_css_class = 'required'
Krophil's avatar
Krophil committed
259

260 261
    class Meta:
        model = Page
262
        fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
263
        widgets = {
264 265
            'edit_groups': CheckboxSelectMultiple,
            'view_groups': CheckboxSelectMultiple,
266 267
        }

Skia's avatar
Skia committed
268 269
    def __init__(self, *arg, **kwargs):
        super(PagePropForm, self).__init__(*arg, **kwargs)
270 271
        self.fields['edit_groups'].required = False
        self.fields['view_groups'].required = False