gremmie@1: """ gremmie@1: This file contains the forms used by the bio application. gremmie@1: """ gremmie@1: try: gremmie@1: from cStringIO import StringIO gremmie@1: except: gremmie@1: from StringIO import StringIO gremmie@1: gremmie@1: from django import forms gremmie@1: from django.conf import settings gremmie@1: from django.core.files.base import ContentFile gremmie@1: from django.contrib.auth.models import User gremmie@1: bgneal@612: import pytz bgneal@612: gremmie@1: from bio.models import UserProfile bgneal@1011: from core.html import image_check bgneal@1011: from core.html import ImageCheckError bgneal@1011: from core.images.utils import parse_image, downscale_image_square bgneal@1011: from core.markup import site_markup bgneal@149: from core.widgets import AutoCompleteUserInput gremmie@1: gremmie@1: gremmie@1: class EditUserForm(forms.ModelForm): gremmie@1: """Form for editing the fields of the User model.""" gremmie@1: email = forms.EmailField(label='Email', required=True) gremmie@1: class Meta: gremmie@1: model = User gremmie@1: fields = ('first_name', 'last_name', 'email') gremmie@1: gremmie@1: gremmie@1: class EditUserProfileForm(forms.ModelForm): gremmie@1: """Form for editing the fields of the UserProfile model.""" bgneal@609: location = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 })) bgneal@609: occupation = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 })) bgneal@609: interests = forms.CharField(required=False, widget=forms.TextInput(attrs={'size': 64 })) bgneal@70: time_zone = forms.CharField(required=False, widget=forms.HiddenInput()) bgneal@120: use_24_time = forms.BooleanField(label='Show times in 24-hour mode', required=False) bgneal@312: profile_text = forms.CharField(required=False, bgneal@390: widget=forms.Textarea(attrs={'class': 'markItUp'})) bgneal@312: signature = forms.CharField(required=False, bgneal@390: widget=forms.Textarea(attrs={'class': 'markItUp'})) bgneal@390: auto_favorite = forms.BooleanField( bgneal@390: label='Automatically favorite every forum topic I create or reply to', required=False) bgneal@390: auto_subscribe = forms.BooleanField( bgneal@390: label='Automatically subscribe to every forum topic I create or reply to', required=False) gremmie@1: gremmie@1: class Meta: gremmie@1: model = UserProfile bgneal@609: fields = ('location', 'country', 'birthday', 'occupation', 'interests', bgneal@206: 'profile_text', 'hide_email', 'signature', 'time_zone', bgneal@390: 'use_24_time', 'auto_favorite', 'auto_subscribe') gremmie@1: bgneal@612: def clean_time_zone(self): bgneal@612: """Ensure the timezone is valid and will work with pytz. bgneal@1011: bgneal@612: A blank (empty) value is allowed. bgneal@612: """ bgneal@612: bgneal@612: tz = self.cleaned_data['time_zone'].strip() bgneal@612: if tz: bgneal@612: try: bgneal@612: pytz.timezone(tz) bgneal@612: except pytz.UnknownTimeZoneError: bgneal@612: raise forms.ValidationError('Invalid timezone') bgneal@612: bgneal@612: return tz bgneal@612: bgneal@1011: def _image_check(self, field_name): bgneal@1011: text = self.cleaned_data[field_name] bgneal@1011: if text: bgneal@1011: html = site_markup(text) bgneal@1011: try: bgneal@1011: image_check(html) bgneal@1011: except ImageCheckError as ex: bgneal@1011: raise forms.ValidationError(str(ex)) bgneal@1011: return text bgneal@1011: bgneal@1011: def clean_profile_text(self): bgneal@1011: return self._image_check('profile_text') bgneal@1011: bgneal@1011: def clean_signature(self): bgneal@1011: return self._image_check('signature') bgneal@1011: gremmie@1: gremmie@1: class UploadAvatarForm(forms.Form): gremmie@1: """Form used to change a user's avatar""" gremmie@1: avatar_file = forms.ImageField(required=False) gremmie@1: image = None gremmie@1: gremmie@1: def clean_avatar_file(self): bgneal@265: f = self.cleaned_data['avatar_file'] bgneal@265: if f is not None: bgneal@265: if f.size > settings.MAX_AVATAR_SIZE_BYTES: bgneal@265: raise forms.ValidationError("Please upload a file smaller than " bgneal@338: "%s bytes." % settings.MAX_AVATAR_SIZE_BYTES) bgneal@265: try: bgneal@265: self.image = parse_image(f) bgneal@265: except IOError: bgneal@265: raise forms.ValidationError("Please upload a valid image. " bgneal@265: "The file you uploaded was either not an image or a " bgneal@265: "corrupted image.") bgneal@265: self.file_type = self.image.format bgneal@265: return f gremmie@1: bgneal@265: def save(self): bgneal@265: """ bgneal@265: Perform any down-scaling needed on the new file, then return a tuple of bgneal@265: (filename, file object). Note that the file object returned may not bgneal@265: have a name; use the returned filename instead. bgneal@265: bgneal@265: """ bgneal@265: if not self.cleaned_data['avatar_file']: bgneal@265: return None, None bgneal@265: bgneal@265: name = self.cleaned_data['avatar_file'].name bgneal@265: dim = settings.MAX_AVATAR_SIZE_PIXELS bgneal@265: max_size = (dim, dim) bgneal@265: if self.image and self.image.size > max_size: bgneal@265: self.image = downscale_image_square(self.image, dim) bgneal@265: bgneal@265: # We need to return a Django File now. To get that from here, bgneal@265: # write the image data info a StringIO and then construct a bgneal@265: # Django ContentFile from that. The ContentFile has no name, bgneal@265: # that is why we return one ourselves explicitly. gremmie@1: s = StringIO() bgneal@265: self.image.save(s, self.file_type) bgneal@265: return name, ContentFile(s.getvalue()) bgneal@312: bgneal@265: return name, self.cleaned_data['avatar_file'] gremmie@1: bgneal@149: bgneal@149: class SearchUsersForm(forms.Form): bgneal@149: """ bgneal@149: A form to search for users. bgneal@149: """ bgneal@149: username = forms.CharField(max_length=30, widget=AutoCompleteUserInput()) bgneal@149: bgneal@149: def clean_username(self): bgneal@463: username = self.cleaned_data['username'].strip() bgneal@149: try: bgneal@149: User.objects.get(username=username, is_active=True) bgneal@149: except User.DoesNotExist: bgneal@149: raise forms.ValidationError("That username does not exist.") bgneal@149: return username