gremmie@1
|
1 """
|
gremmie@1
|
2 This file contains the forms used by the bio application.
|
gremmie@1
|
3 """
|
gremmie@1
|
4 try:
|
gremmie@1
|
5 from cStringIO import StringIO
|
gremmie@1
|
6 except:
|
gremmie@1
|
7 from StringIO import StringIO
|
gremmie@1
|
8
|
gremmie@1
|
9 from django import forms
|
gremmie@1
|
10 from django.conf import settings
|
gremmie@1
|
11 from django.core.files.base import ContentFile
|
bgneal@1078
|
12 from django.core.urlresolvers import reverse
|
gremmie@1
|
13 from django.contrib.auth.models import User
|
gremmie@1
|
14
|
bgneal@612
|
15 import pytz
|
bgneal@612
|
16
|
gremmie@1
|
17 from bio.models import UserProfile
|
bgneal@1011
|
18 from core.html import image_check
|
bgneal@1011
|
19 from core.html import ImageCheckError
|
bgneal@1011
|
20 from core.images.utils import parse_image, downscale_image_square
|
bgneal@1011
|
21 from core.markup import site_markup
|
gremmie@1
|
22
|
gremmie@1
|
23
|
gremmie@1
|
24 class EditUserForm(forms.ModelForm):
|
gremmie@1
|
25 """Form for editing the fields of the User model."""
|
gremmie@1
|
26 email = forms.EmailField(label='Email', required=True)
|
gremmie@1
|
27 class Meta:
|
gremmie@1
|
28 model = User
|
gremmie@1
|
29 fields = ('first_name', 'last_name', 'email')
|
gremmie@1
|
30
|
gremmie@1
|
31
|
gremmie@1
|
32 class EditUserProfileForm(forms.ModelForm):
|
gremmie@1
|
33 """Form for editing the fields of the UserProfile model."""
|
bgneal@609
|
34 location = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 }))
|
bgneal@609
|
35 occupation = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 }))
|
bgneal@609
|
36 interests = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 }))
|
bgneal@70
|
37 time_zone = forms.CharField(required=False, widget=forms.HiddenInput())
|
bgneal@120
|
38 use_24_time = forms.BooleanField(label='Show times in 24-hour mode', required=False)
|
bgneal@312
|
39 profile_text = forms.CharField(required=False,
|
bgneal@390
|
40 widget=forms.Textarea(attrs={'class': 'markItUp'}))
|
bgneal@312
|
41 signature = forms.CharField(required=False,
|
bgneal@390
|
42 widget=forms.Textarea(attrs={'class': 'markItUp'}))
|
bgneal@390
|
43 auto_favorite = forms.BooleanField(
|
bgneal@390
|
44 label='Automatically favorite every forum topic I create or reply to', required=False)
|
bgneal@390
|
45 auto_subscribe = forms.BooleanField(
|
bgneal@390
|
46 label='Automatically subscribe to every forum topic I create or reply to', required=False)
|
gremmie@1
|
47
|
gremmie@1
|
48 class Meta:
|
gremmie@1
|
49 model = UserProfile
|
bgneal@609
|
50 fields = ('location', 'country', 'birthday', 'occupation', 'interests',
|
bgneal@206
|
51 'profile_text', 'hide_email', 'signature', 'time_zone',
|
bgneal@390
|
52 'use_24_time', 'auto_favorite', 'auto_subscribe')
|
gremmie@1
|
53
|
bgneal@612
|
54 def clean_time_zone(self):
|
bgneal@612
|
55 """Ensure the timezone is valid and will work with pytz.
|
bgneal@1011
|
56
|
bgneal@612
|
57 A blank (empty) value is allowed.
|
bgneal@612
|
58 """
|
bgneal@612
|
59
|
bgneal@612
|
60 tz = self.cleaned_data['time_zone'].strip()
|
bgneal@612
|
61 if tz:
|
bgneal@612
|
62 try:
|
bgneal@612
|
63 pytz.timezone(tz)
|
bgneal@612
|
64 except pytz.UnknownTimeZoneError:
|
bgneal@612
|
65 raise forms.ValidationError('Invalid timezone')
|
bgneal@612
|
66
|
bgneal@612
|
67 return tz
|
bgneal@612
|
68
|
bgneal@1011
|
69 def _image_check(self, field_name):
|
bgneal@1011
|
70 text = self.cleaned_data[field_name]
|
bgneal@1011
|
71 if text:
|
bgneal@1011
|
72 html = site_markup(text)
|
bgneal@1011
|
73 try:
|
bgneal@1011
|
74 image_check(html)
|
bgneal@1011
|
75 except ImageCheckError as ex:
|
bgneal@1011
|
76 raise forms.ValidationError(str(ex))
|
bgneal@1011
|
77 return text
|
bgneal@1011
|
78
|
bgneal@1011
|
79 def clean_profile_text(self):
|
bgneal@1011
|
80 return self._image_check('profile_text')
|
bgneal@1011
|
81
|
bgneal@1011
|
82 def clean_signature(self):
|
bgneal@1011
|
83 return self._image_check('signature')
|
bgneal@1011
|
84
|
gremmie@1
|
85
|
gremmie@1
|
86 class UploadAvatarForm(forms.Form):
|
gremmie@1
|
87 """Form used to change a user's avatar"""
|
gremmie@1
|
88 avatar_file = forms.ImageField(required=False)
|
gremmie@1
|
89 image = None
|
gremmie@1
|
90
|
gremmie@1
|
91 def clean_avatar_file(self):
|
bgneal@265
|
92 f = self.cleaned_data['avatar_file']
|
bgneal@265
|
93 if f is not None:
|
bgneal@265
|
94 if f.size > settings.MAX_AVATAR_SIZE_BYTES:
|
bgneal@265
|
95 raise forms.ValidationError("Please upload a file smaller than "
|
bgneal@338
|
96 "%s bytes." % settings.MAX_AVATAR_SIZE_BYTES)
|
bgneal@265
|
97 try:
|
bgneal@265
|
98 self.image = parse_image(f)
|
bgneal@265
|
99 except IOError:
|
bgneal@265
|
100 raise forms.ValidationError("Please upload a valid image. "
|
bgneal@265
|
101 "The file you uploaded was either not an image or a "
|
bgneal@265
|
102 "corrupted image.")
|
bgneal@265
|
103 self.file_type = self.image.format
|
bgneal@265
|
104 return f
|
gremmie@1
|
105
|
bgneal@265
|
106 def save(self):
|
bgneal@265
|
107 """
|
bgneal@265
|
108 Perform any down-scaling needed on the new file, then return a tuple of
|
bgneal@265
|
109 (filename, file object). Note that the file object returned may not
|
bgneal@265
|
110 have a name; use the returned filename instead.
|
bgneal@265
|
111
|
bgneal@265
|
112 """
|
bgneal@265
|
113 if not self.cleaned_data['avatar_file']:
|
bgneal@265
|
114 return None, None
|
bgneal@265
|
115
|
bgneal@265
|
116 name = self.cleaned_data['avatar_file'].name
|
bgneal@265
|
117 dim = settings.MAX_AVATAR_SIZE_PIXELS
|
bgneal@265
|
118 max_size = (dim, dim)
|
bgneal@265
|
119 if self.image and self.image.size > max_size:
|
bgneal@265
|
120 self.image = downscale_image_square(self.image, dim)
|
bgneal@265
|
121
|
bgneal@265
|
122 # We need to return a Django File now. To get that from here,
|
bgneal@265
|
123 # write the image data info a StringIO and then construct a
|
bgneal@265
|
124 # Django ContentFile from that. The ContentFile has no name,
|
bgneal@265
|
125 # that is why we return one ourselves explicitly.
|
gremmie@1
|
126 s = StringIO()
|
bgneal@265
|
127 self.image.save(s, self.file_type)
|
bgneal@265
|
128 return name, ContentFile(s.getvalue())
|
bgneal@312
|
129
|
bgneal@265
|
130 return name, self.cleaned_data['avatar_file']
|
gremmie@1
|
131
|
bgneal@149
|
132
|
bgneal@149
|
133 class SearchUsersForm(forms.Form):
|
bgneal@149
|
134 """
|
bgneal@149
|
135 A form to search for users.
|
bgneal@149
|
136 """
|
bgneal@1078
|
137 username = forms.CharField(
|
bgneal@1078
|
138 max_length=30,
|
bgneal@1078
|
139 widget=forms.TextInput(attrs={
|
bgneal@1078
|
140 'class': 'sg101-autocomplete',
|
bgneal@1078
|
141 }))
|
bgneal@1078
|
142
|
bgneal@1078
|
143 def __init__(self, *args, **kwargs):
|
bgneal@1078
|
144 super(SearchUsersForm, self).__init__(*args, **kwargs)
|
bgneal@1078
|
145 url = reverse('core-ajax_users')
|
bgneal@1078
|
146 self.fields['username'].widget.attrs['data-autocomplete-url'] = url
|
bgneal@149
|
147
|
bgneal@149
|
148 def clean_username(self):
|
bgneal@1078
|
149 username = self.cleaned_data['username'].strip()
|
bgneal@1078
|
150 try:
|
bgneal@1078
|
151 User.objects.get(username=username, is_active=True)
|
bgneal@1078
|
152 except User.DoesNotExist:
|
bgneal@1078
|
153 raise forms.ValidationError("That username does not exist.")
|
bgneal@1078
|
154 return username
|