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
bgneal@1078: from django.core.urlresolvers import reverse
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
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@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@1123:     hide_email = forms.BooleanField(
bgneal@1123:         label="Don't show my email address on my public profile",
bgneal@1123:         required=False)
bgneal@390:     auto_favorite = forms.BooleanField(
bgneal@1123:         label='Automatically favorite every forum topic I create or reply to',
bgneal@1123:         required=False)
bgneal@390:     auto_subscribe = forms.BooleanField(
bgneal@1123:         label='Automatically subscribe to every forum topic I create or reply to',
bgneal@1123:         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@1078:     username = forms.CharField(
bgneal@1078:                     max_length=30,
bgneal@1078:                     widget=forms.TextInput(attrs={
bgneal@1078:                        'class': 'sg101-autocomplete',
bgneal@1078:                     }))
bgneal@1078: 
bgneal@1078:     def __init__(self, *args, **kwargs):
bgneal@1078:         super(SearchUsersForm, self).__init__(*args, **kwargs)
bgneal@1078:         url = reverse('core-ajax_users')
bgneal@1078:         self.fields['username'].widget.attrs['data-autocomplete-url'] = url
bgneal@149: 
bgneal@149:     def clean_username(self):
bgneal@1078:         username = self.cleaned_data['username'].strip()
bgneal@1078:         try:
bgneal@1078:             User.objects.get(username=username, is_active=True)
bgneal@1078:         except User.DoesNotExist:
bgneal@1078:             raise forms.ValidationError("That username does not exist.")
bgneal@1078:         return username