Пример #1
0
class HttpHttpsOnlyURLFieldTestCase(TestCase):

    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
        self.field = HttpHttpsOnlyURLField()

    def test_invalid_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'javascript://foo.com/')

    def test_invalid_ftp_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftp://foo.com/')

    def test_invalid_ftps_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftps://foo.com/')

    def test_no_scheme_assumes_http(self):
        assert self.field.clean(u'foo.com') == 'http://foo.com'

    def test_http_scheme(self):
        assert self.field.clean(u'http://foo.com/') == u'http://foo.com/'

    def test_https_scheme(self):
        assert self.field.clean(u'https://foo.com/') == u'https://foo.com/'

    def test_catches_invalid_url(self):
        # https://github.com/mozilla/addons-server/issues/1452
        with self.assertRaises(exceptions.ValidationError):
            assert self.field.clean(u'https://test.[com')
Пример #2
0
class HttpHttpsOnlyURLFieldTestCase(TestCase):
    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
        self.field = HttpHttpsOnlyURLField()

    def test_invalid_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'javascript://foo.com/')

    def test_invalid_ftp_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftp://foo.com/')

    def test_invalid_ftps_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftps://foo.com/')

    def test_no_scheme_assumes_http(self):
        assert self.field.clean(u'foo.com') == 'http://foo.com'

    def test_http_scheme(self):
        assert self.field.clean(u'http://foo.com/') == u'http://foo.com/'

    def test_https_scheme(self):
        assert self.field.clean(u'https://foo.com/') == u'https://foo.com/'

    def test_catches_invalid_url(self):
        # https://github.com/mozilla/addons-server/issues/1452
        with self.assertRaises(exceptions.ValidationError):
            assert self.field.clean(u'https://test.[com')
Пример #3
0
class HttpHttpsOnlyURLFieldTestCase(TestCase):
    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
        self.field = HttpHttpsOnlyURLField()

    def test_invalid_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'javascript://foo.com/')

    def test_invalid_ftp_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftp://foo.com/')

    def test_invalid_ftps_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftps://foo.com/')

    def test_no_scheme_assumes_http(self):
        assert self.field.clean(u'foo.com') == 'http://foo.com/'

    def test_http_scheme(self):
        assert self.field.clean(u'http://foo.com/') == u'http://foo.com/'

    def test_https_scheme(self):
        assert self.field.clean(u'https://foo.com/') == u'https://foo.com/'
Пример #4
0
class HttpHttpsOnlyURLFieldTestCase(TestCase):

    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
        self.field = HttpHttpsOnlyURLField()

    def test_invalid_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'javascript://foo.com/')

    def test_invalid_ftp_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftp://foo.com/')

    def test_invalid_ftps_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftps://foo.com/')

    def test_no_scheme_assumes_http(self):
        assert self.field.clean(u'foo.com') == 'http://foo.com/'

    def test_http_scheme(self):
        assert self.field.clean(u'http://foo.com/') == u'http://foo.com/'

    def test_https_scheme(self):
        assert self.field.clean(u'https://foo.com/') == u'https://foo.com/'
Пример #5
0
class AddonFormBasic(AddonFormBase):
    name = TransField(max_length=50)
    slug = forms.CharField(max_length=30)
    summary = TransField(widget=TransTextarea(attrs={'rows': 4}),
                         max_length=250)
    tags = forms.CharField(required=False)
    contributions = HttpHttpsOnlyURLField(required=False, max_length=255)
    is_experimental = forms.BooleanField(required=False)
    requires_payment = forms.BooleanField(required=False)

    class Meta:
        model = Addon
        fields = ('name', 'slug', 'summary', 'tags', 'is_experimental',
                  'requires_payment', 'contributions')

    def __init__(self, *args, **kw):
        super(AddonFormBasic, self).__init__(*args, **kw)

        if self.fields.get('tags'):
            self.fields['tags'].initial = ', '.join(
                self.get_tags(self.instance))

    def clean_slug(self):
        return clean_addon_slug(self.cleaned_data['slug'], self.instance)

    def clean_contributions(self):
        if self.cleaned_data['contributions']:
            hostname = urlsplit(self.cleaned_data['contributions']).hostname
            if not hostname.endswith(amo.VALID_CONTRIBUTION_DOMAINS):
                raise forms.ValidationError(
                    ugettext('URL domain must be one of [%s], or a subdomain.')
                    % ', '.join(amo.VALID_CONTRIBUTION_DOMAINS))
        return self.cleaned_data['contributions']

    def save(self, addon, commit=False):
        if self.fields.get('tags'):
            tags_new = self.cleaned_data['tags']
            tags_old = [slugify(t, spaces=True) for t in self.get_tags(addon)]

            # Add new tags.
            for t in set(tags_new) - set(tags_old):
                Tag(tag_text=t).save_tag(addon)

            # Remove old tags.
            for t in set(tags_old) - set(tags_new):
                Tag(tag_text=t).remove_tag(addon)

        # We ignore `commit`, since we need it to be `False` so we can save
        # the ManyToMany fields on our own.
        addonform = super(AddonFormBasic, self).save(commit=False)
        addonform.save()

        return addonform
Пример #6
0
class UserRegisterForm(happyforms.ModelForm, UsernameMixin):
    """
    For registering users.  We're not building off
    d.contrib.auth.forms.UserCreationForm because it doesn't do a lot of the
    details here, so we'd have to rewrite most of it anyway.
    """
    username = forms.CharField(max_length=50, required=False)
    email = forms.EmailField(widget=RequiredEmailInput)
    display_name = forms.CharField(label=_lazy(u'Display Name'),
                                   max_length=50,
                                   required=False)
    location = forms.CharField(label=_lazy(u'Location'),
                               max_length=100,
                               required=False)
    occupation = forms.CharField(label=_lazy(u'Occupation'),
                                 max_length=100,
                                 required=False)
    recaptcha = ReCaptchaField()
    homepage = HttpHttpsOnlyURLField(label=_lazy(u'Homepage'), required=False)

    class Meta:
        model = UserProfile
        fields = ('username', 'display_name', 'location', 'occupation',
                  'recaptcha', 'homepage', 'email')

    def __init__(self, *args, **kwargs):
        instance = kwargs.get('instance')
        if instance and instance.has_anonymous_username():
            kwargs.setdefault('initial', {})
            kwargs['initial']['username'] = ''

        super(UserRegisterForm, self).__init__(*args, **kwargs)

        if not settings.NOBOT_RECAPTCHA_PRIVATE_KEY:
            del self.fields['recaptcha']

        errors = {
            'invalid':
            _('This URL has an invalid format. '
              'Valid URLs look like '
              'http://example.com/my_page.')
        }
        self.fields['homepage'].error_messages = errors

    def clean_display_name(self):
        name = self.cleaned_data['display_name']
        if BlacklistedName.blocked(name):
            raise forms.ValidationError(_('This display name cannot be used.'))
        return name
Пример #7
0
class UserRegisterForm(happyforms.ModelForm, UsernameMixin, PasswordMixin):
    """
    For registering users.  We're not building off
    d.contrib.auth.forms.UserCreationForm because it doesn't do a lot of the
    details here, so we'd have to rewrite most of it anyway.
    """
    username = forms.CharField(max_length=50, required=False)
    email = forms.EmailField(widget=RequiredEmailInput)
    display_name = forms.CharField(label=_lazy(u'Display Name'),
                                   max_length=50,
                                   required=False)
    location = forms.CharField(label=_lazy(u'Location'),
                               max_length=100,
                               required=False)
    occupation = forms.CharField(label=_lazy(u'Occupation'),
                                 max_length=100,
                                 required=False)
    password = forms.CharField(max_length=255,
                               min_length=PasswordMixin.min_length,
                               error_messages=PasswordMixin.error_msg,
                               widget=PasswordMixin.widget(render_value=False,
                                                           required=True))
    password2 = forms.CharField(max_length=255,
                                widget=PasswordMixin.widget(render_value=False,
                                                            required=True))
    recaptcha = ReCaptchaField()
    homepage = HttpHttpsOnlyURLField(label=_lazy(u'Homepage'), required=False)

    class Meta:
        model = UserProfile
        fields = ('username', 'display_name', 'location', 'occupation',
                  'password', 'password2', 'recaptcha', 'homepage', 'email')

    def __init__(self, *args, **kwargs):
        instance = kwargs.get('instance')
        if instance and instance.has_anonymous_username():
            kwargs.setdefault('initial', {})
            kwargs['initial']['username'] = ''

        super(UserRegisterForm, self).__init__(*args, **kwargs)

        if not settings.NOBOT_RECAPTCHA_PRIVATE_KEY:
            del self.fields['recaptcha']

        errors = {
            'invalid':
            _('This URL has an invalid format. '
              'Valid URLs look like '
              'http://example.com/my_page.')
        }
        self.fields['homepage'].error_messages = errors

    def clean_email(self):
        d = self.cleaned_data['email'].split('@')[-1]
        if BlacklistedEmailDomain.blocked(d):
            raise forms.ValidationError(
                _('Please use an email address from a '
                  'different provider to complete '
                  'your registration.'))
        return self.cleaned_data['email']

    def clean_display_name(self):
        name = self.cleaned_data['display_name']
        if BlacklistedName.blocked(name):
            raise forms.ValidationError(_('This display name cannot be used.'))
        return name

    def clean(self):
        super(UserRegisterForm, self).clean()

        data = self.cleaned_data

        # Passwords
        p1 = data.get('password')
        p2 = data.get('password2')

        # If p1 is invalid because its blocked, this message is non sensical.
        if p1 and p1 != p2:
            msg = _('The passwords did not match.')
            self._errors['password2'] = ErrorList([msg])
            if p2:
                del data['password2']

        return data
Пример #8
0
class AdditionalDetailsForm(AddonFormBase):
    default_locale = forms.TypedChoiceField(choices=LOCALES)
    homepage = TransField.adapt(HttpHttpsOnlyURLField)(required=False)
    tags = forms.CharField(required=False)
    contributions = HttpHttpsOnlyURLField(required=False, max_length=255)

    class Meta:
        model = Addon
        fields = ('default_locale', 'homepage', 'tags', 'contributions')

    def __init__(self, *args, **kw):
        super(AdditionalDetailsForm, self).__init__(*args, **kw)

        if self.fields.get('tags'):
            self.fields['tags'].initial = ', '.join(
                self.get_tags(self.instance))

    def clean_contributions(self):
        if self.cleaned_data['contributions']:
            hostname = urlsplit(self.cleaned_data['contributions']).hostname
            if not hostname.endswith(amo.VALID_CONTRIBUTION_DOMAINS):
                raise forms.ValidationError(ugettext(
                    'URL domain must be one of [%s], or a subdomain.'
                ) % ', '.join(amo.VALID_CONTRIBUTION_DOMAINS))
        return self.cleaned_data['contributions']

    def clean(self):
        # Make sure we have the required translations in the new locale.
        required = 'name', 'summary', 'description'
        if not self.errors and 'default_locale' in self.changed_data:
            fields = dict((k, getattr(self.instance, k + '_id'))
                          for k in required)
            locale = self.cleaned_data['default_locale']
            ids = filter(None, fields.values())
            qs = (Translation.objects.filter(locale=locale, id__in=ids,
                                             localized_string__isnull=False)
                  .values_list('id', flat=True))
            missing = [k for k, v in fields.items() if v not in qs]
            if missing:
                raise forms.ValidationError(ugettext(
                    'Before changing your default locale you must have a '
                    'name, summary, and description in that locale. '
                    'You are missing %s.') % ', '.join(map(repr, missing)))
        return super(AdditionalDetailsForm, self).clean()

    def save(self, addon, commit=False):
        if self.fields.get('tags'):
            tags_new = self.cleaned_data['tags']
            tags_old = [slugify(t, spaces=True) for t in self.get_tags(addon)]

            # Add new tags.
            for t in set(tags_new) - set(tags_old):
                Tag(tag_text=t).save_tag(addon)

            # Remove old tags.
            for t in set(tags_old) - set(tags_new):
                Tag(tag_text=t).remove_tag(addon)

        # We ignore `commit`, since we need it to be `False` so we can save
        # the ManyToMany fields on our own.
        addonform = super(AdditionalDetailsForm, self).save(commit=False)
        addonform.save()

        return addonform
Пример #9
0
 def setUp(self):
     super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
     self.field = HttpHttpsOnlyURLField()
Пример #10
0
class UserEditForm(happyforms.ModelForm):
    username = forms.CharField(max_length=50, required=False)
    display_name = forms.CharField(label=_(u'Display Name'),
                                   max_length=50,
                                   required=False)
    location = forms.CharField(label=_(u'Location'),
                               max_length=100,
                               required=False)
    occupation = forms.CharField(label=_(u'Occupation'),
                                 max_length=100,
                                 required=False)
    homepage = HttpHttpsOnlyURLField(label=_(u'Homepage'), required=False)
    email = forms.EmailField(
        required=False,
        help_text=fxa_error_message(
            _(u'You can change your email address on Firefox Accounts.'),
            LOGIN_HELP_URL),
        widget=forms.EmailInput(attrs={'readonly': 'readonly'}))
    photo = forms.FileField(label=_(u'Profile Photo'), required=False)
    biography = forms.CharField(widget=forms.Textarea, required=False)

    notifications = forms.MultipleChoiceField(
        choices=[],
        widget=NotificationsSelectMultiple,
        initial=notifications.NOTIFICATIONS_DEFAULT,
        required=False)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)

        instance = kwargs.get('instance')
        if instance and instance.has_anonymous_username:
            kwargs.setdefault('initial', {})
            kwargs['initial']['username'] = ''

        super(UserEditForm, self).__init__(*args, **kwargs)

        errors = {
            'invalid':
            ugettext('This URL has an invalid format. Valid URLs look like '
                     'http://example.com/my_page.')
        }
        self.fields['homepage'].error_messages = errors

        if self.instance:
            # We are fetching all `UserNotification` instances and then,
            # if the waffle-switch is active overwrite their value with the
            # data from basket. This simplifies the process of implementing
            # the waffle-switch. Once we switched the integration "on" on prod
            # all `UserNotification` instances that are now handled by basket
            # can be deleted.
            default = {
                idx: notification.default_checked
                for idx, notification in
                notifications.NOTIFICATIONS_BY_ID.items()
            }
            user = {
                notification.notification_id: notification.enabled
                for notification in self.instance.notifications.all()
            }
            default.update(user)

            if waffle.switch_is_active('activate-basket-sync'):
                newsletters = fetch_subscribed_newsletters(self.instance)

                by_basket_id = notifications.REMOTE_NOTIFICATIONS_BY_BASKET_ID
                for basket_id, notification in by_basket_id.items():
                    default[notification.id] = basket_id in newsletters

            # Add choices to Notification.
            if self.instance.is_developer:
                choices = [(l.id, l.label)
                           for l in notifications.NOTIFICATIONS_COMBINED]
            else:
                choices = [(l.id, l.label)
                           for l in notifications.NOTIFICATIONS_COMBINED
                           if l.group != 'dev']

            # Append a "NEW" message to new notification options.
            saved = self.instance.notifications.values_list('notification_id',
                                                            flat=True)
            self.choices_status = {}
            for idx, label in choices:
                self.choices_status[idx] = idx not in saved

            self.fields['notifications'].choices = choices
            self.fields['notifications'].initial = [
                i for i, v in default.items() if v
            ]
            self.fields['notifications'].widget.form_instance = self

    class Meta:
        model = UserProfile
        fields = (
            'username',
            'email',
            'display_name',
            'location',
            'occupation',
            'homepage',
            'photo',
            'biography',
            'display_collections',
            'display_collections_fav',
            'notifications',
        )

    def clean_username(self):
        name = self.cleaned_data['username']

        if not name:
            if self.instance.has_anonymous_username:
                name = self.instance.username
            else:
                name = self.instance.anonymize_username()

        # All-digits usernames are disallowed since they can be
        # confused for user IDs in URLs. (See bug 862121.)
        if name.isdigit():
            raise forms.ValidationError(
                ugettext('Usernames cannot contain only digits.'))

        slug_validator(
            name,
            lower=False,
            message=ugettext(
                'Enter a valid username consisting of letters, numbers, '
                'underscores or hyphens.'))
        if DeniedName.blocked(name):
            raise forms.ValidationError(
                ugettext('This username cannot be used.'))

        # FIXME: Bug 858452. Remove this check when collation of the username
        # column is changed to case insensitive.
        if (UserProfile.objects.exclude(id=self.instance.id).filter(
                username__iexact=name).exists()):
            raise forms.ValidationError(
                ugettext('This username is already in use.'))

        return name

    def clean_display_name(self):
        name = self.cleaned_data['display_name']
        if DeniedName.blocked(name):
            raise forms.ValidationError(
                ugettext('This display name cannot be used.'))
        return name

    def clean_email(self):
        # TODO(django 1.9): Change the field to disabled=True and remove this.
        return self.instance.email

    def clean_photo(self):
        photo = self.cleaned_data['photo']

        if not photo:
            return

        image_check = ImageCheck(photo)
        if (photo.content_type not in amo.IMG_TYPES
                or not image_check.is_image()):
            raise forms.ValidationError(
                ugettext('Images must be either PNG or JPG.'))

        if image_check.is_animated():
            raise forms.ValidationError(ugettext('Images cannot be animated.'))

        if photo.size > settings.MAX_PHOTO_UPLOAD_SIZE:
            msg = ugettext('Please use images smaller than %dMB.')
            size_in_mb = settings.MAX_PHOTO_UPLOAD_SIZE / 1024 / 1024
            raise forms.ValidationError(msg % size_in_mb)

        return photo

    def clean_biography(self):
        biography = self.cleaned_data['biography']
        normalized = clean_nl(unicode(biography))
        if has_links(normalized):
            # There's some links, we don't want them.
            raise forms.ValidationError(ugettext('No links are allowed.'))
        return biography

    def save(self, log_for_developer=True):
        user = super(UserEditForm, self).save(commit=False)
        data = self.cleaned_data
        photo = data['photo']
        if photo:
            user.picture_type = 'image/png'
            tmp_destination = user.picture_path_original

            with storage.open(tmp_destination, 'wb') as fh:
                for chunk in photo.chunks():
                    fh.write(chunk)

            tasks.resize_photo.delay(
                tmp_destination,
                user.picture_path,
                set_modified_on=user.serializable_reference())

        visible_notifications = (notifications.NOTIFICATIONS_BY_ID
                                 if self.instance.is_developer else
                                 notifications.NOTIFICATIONS_BY_ID_NOT_DEV)

        for (notification_id, notification) in visible_notifications.items():
            enabled = (notification.mandatory
                       or (str(notification_id) in data['notifications']))
            UserNotification.objects.update_or_create(
                user=self.instance,
                notification_id=notification_id,
                defaults={'enabled': enabled})

        if waffle.switch_is_active('activate-basket-sync'):
            by_basket_id = notifications.REMOTE_NOTIFICATIONS_BY_BASKET_ID
            for basket_id, notification in by_basket_id.items():
                needs_subscribe = str(notification.id) in data['notifications']
                needs_unsubscribe = (str(notification.id)
                                     not in data['notifications'])

                if needs_subscribe:
                    subscribe_newsletter(self.instance,
                                         basket_id,
                                         request=self.request)
                elif needs_unsubscribe:
                    unsubscribe_newsletter(self.instance, basket_id)

        log.debug(u'User (%s) updated their profile' % user)

        user.save()
        return user
Пример #11
0
class HttpHttpsOnlyURLFieldTestCase(TestCase):

    domain = 'example.com'

    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()

        with override_settings(DOMAIN=self.domain):
            self.field = HttpHttpsOnlyURLField()

    def test_invalid_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'javascript://foo.com/')

    def test_invalid_ftp_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftp://foo.com/')

    def test_invalid_ftps_scheme_validation_error(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'ftps://foo.com/')

    def test_no_scheme_assumes_http(self):
        assert self.field.clean(u'foo.com') == 'http://foo.com'

    def test_http_scheme(self):
        assert self.field.clean(u'http://foo.com/') == u'http://foo.com/'

    def test_https_scheme(self):
        assert self.field.clean(u'https://foo.com/') == u'https://foo.com/'

    def test_catches_invalid_url(self):
        # https://github.com/mozilla/addons-server/issues/1452
        with self.assertRaises(exceptions.ValidationError):
            assert self.field.clean(u'https://test.[com')

    def test_with_domain_and_no_scheme(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'%s' % self.domain)

    def test_with_domain_and_http(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'http://%s' % self.domain)

    def test_with_domain_and_https(self):
        with self.assertRaises(exceptions.ValidationError):
            self.field.clean(u'https://%s' % self.domain)

    def test_domain_is_escaped_in_regex_validator(self):
        assert self.field.clean(u'example-com.fr') == u'http://example-com.fr'
Пример #12
0
    def setUp(self):
        super(HttpHttpsOnlyURLFieldTestCase, self).setUp()

        with override_settings(DOMAIN=self.domain):
            self.field = HttpHttpsOnlyURLField()
Пример #13
0
class UserEditForm(happyforms.ModelForm):
    username = forms.CharField(max_length=50, required=False)
    display_name = forms.CharField(label=_(u'Display Name'),
                                   max_length=50,
                                   required=False)
    location = forms.CharField(label=_(u'Location'),
                               max_length=100,
                               required=False)
    occupation = forms.CharField(label=_(u'Occupation'),
                                 max_length=100,
                                 required=False)
    homepage = HttpHttpsOnlyURLField(label=_(u'Homepage'), required=False)
    email = forms.EmailField(
        required=False,
        help_text=fxa_error_message(
            _(u'Firefox Accounts users cannot currently change their '
              u'email address.')),
        widget=forms.EmailInput(attrs={'readonly': 'readonly'}))
    photo = forms.FileField(label=_(u'Profile Photo'), required=False)
    biography = forms.CharField(widget=forms.Textarea, required=False)

    notifications = forms.MultipleChoiceField(
        choices=[],
        widget=NotificationsSelectMultiple,
        initial=notifications.NOTIFICATIONS_DEFAULT,
        required=False)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)

        instance = kwargs.get('instance')
        if instance and instance.has_anonymous_username():
            kwargs.setdefault('initial', {})
            kwargs['initial']['username'] = ''

        super(UserEditForm, self).__init__(*args, **kwargs)

        errors = {
            'invalid':
            ugettext('This URL has an invalid format. Valid URLs look like '
                     'http://example.com/my_page.')
        }
        self.fields['homepage'].error_messages = errors

        if self.instance:
            default = dict(
                (i, n.default_checked)
                for i, n in notifications.NOTIFICATIONS_BY_ID.items())
            user = dict((n.notification_id, n.enabled)
                        for n in self.instance.notifications.all())
            default.update(user)

            # Add choices to Notification.
            choices = notifications.NOTIFICATIONS_CHOICES
            if not self.instance.is_developer:
                choices = notifications.NOTIFICATIONS_CHOICES_NOT_DEV

            # Append a "NEW" message to new notification options.
            saved = self.instance.notifications.values_list('notification_id',
                                                            flat=True)
            self.choices_status = {}
            for idx, label in choices:
                self.choices_status[idx] = idx not in saved

            self.fields['notifications'].choices = choices
            self.fields['notifications'].initial = [
                i for i, v in default.items() if v
            ]
            self.fields['notifications'].widget.form_instance = self

    class Meta:
        model = UserProfile
        fields = (
            'username',
            'email',
            'display_name',
            'location',
            'occupation',
            'homepage',
            'photo',
            'biography',
            'display_collections',
            'display_collections_fav',
            'notifications',
        )

    def clean_username(self):
        name = self.cleaned_data['username']

        if not name:
            if self.instance.has_anonymous_username():
                name = self.instance.username
            else:
                name = self.instance.anonymize_username()

        # All-digits usernames are disallowed since they can be
        # confused for user IDs in URLs. (See bug 862121.)
        if name.isdigit():
            raise forms.ValidationError(
                ugettext('Usernames cannot contain only digits.'))

        slug_validator(
            name,
            lower=False,
            message=ugettext(
                'Enter a valid username consisting of letters, numbers, '
                'underscores or hyphens.'))
        if DeniedName.blocked(name):
            raise forms.ValidationError(
                ugettext('This username cannot be used.'))

        # FIXME: Bug 858452. Remove this check when collation of the username
        # column is changed to case insensitive.
        if (UserProfile.objects.exclude(id=self.instance.id).filter(
                username__iexact=name).exists()):
            raise forms.ValidationError(
                ugettext('This username is already in use.'))

        return name

    def clean_display_name(self):
        name = self.cleaned_data['display_name']
        if DeniedName.blocked(name):
            raise forms.ValidationError(
                ugettext('This display name cannot be used.'))
        return name

    def clean_email(self):
        # TODO(django 1.9): Change the field to disabled=True and remove this.
        return self.instance.email

    def clean_photo(self):
        photo = self.cleaned_data['photo']

        if not photo:
            return

        if photo.content_type not in ('image/png', 'image/jpeg'):
            raise forms.ValidationError(
                ugettext('Images must be either PNG or JPG.'))

        if photo.size > settings.MAX_PHOTO_UPLOAD_SIZE:
            msg = ugettext('Please use images smaller than %dMB.')
            size_in_mb = settings.MAX_PHOTO_UPLOAD_SIZE / 1024 / 1024 - 1
            raise forms.ValidationError(msg % size_in_mb)

        return photo

    def clean_biography(self):
        biography = self.cleaned_data['biography']
        normalized = clean_nl(unicode(biography))
        if has_links(normalized):
            # There's some links, we don't want them.
            raise forms.ValidationError(ugettext('No links are allowed.'))
        return biography

    def save(self, log_for_developer=True):
        u = super(UserEditForm, self).save(commit=False)
        data = self.cleaned_data
        photo = data['photo']
        if photo:
            u.picture_type = 'image/png'
            tmp_destination = u.picture_path + '__unconverted'

            with storage.open(tmp_destination, 'wb') as fh:
                for chunk in photo.chunks():
                    fh.write(chunk)

            tasks.resize_photo.delay(tmp_destination,
                                     u.picture_path,
                                     set_modified_on=[u])

        for (i, n) in notifications.NOTIFICATIONS_BY_ID.items():
            enabled = n.mandatory or (str(i) in data['notifications'])
            UserNotification.objects.update_or_create(
                user=self.instance,
                notification_id=i,
                defaults={'enabled': enabled})

        log.debug(u'User (%s) updated their profile' % u)

        u.save()
        return u
Пример #14
0
 def setUp(self):
     super(HttpHttpsOnlyURLFieldTestCase, self).setUp()
     self.field = HttpHttpsOnlyURLField()