Ejemplo n.º 1
0
class AppFormDetails(AddonFormBase):
    LOCALES = [(translation.to_locale(k).replace('_', '-'), v)
               for k, v in do_dictsort(settings.LANGUAGES)]

    default_locale = forms.TypedChoiceField(required=False, choices=LOCALES)
    homepage = TransField.adapt(forms.URLField)(required=False)
    privacy_policy = TransField(
        widget=TransTextarea(), required=True,
        label=_lazy(u"Please specify your app's Privacy Policy"))

    class Meta:
        model = Webapp
        fields = ('default_locale', 'homepage', 'privacy_policy')

    def clean(self):
        # Make sure we have the required translations in the new locale.
        required = ['name', 'description']
        data = self.cleaned_data
        if not self.errors and 'default_locale' in self.changed_data:
            fields = dict((k, getattr(self.instance, k + '_id'))
                          for k in required)
            locale = 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(
                    _('Before changing your default locale you must have a '
                      'name and description in that locale. '
                      'You are missing %s.') % ', '.join(map(repr, missing)))
        return data
Ejemplo n.º 2
0
class AppVersionForm(happyforms.ModelForm):
    releasenotes = TransField(widget=TransTextarea(), required=False)
    approvalnotes = forms.CharField(
        widget=TranslationTextarea(attrs={'rows': 4}), required=False)
    publish_immediately = forms.BooleanField(
        required=False,
        label=_lazy(u'Make this the Active version of my app as soon as it '
                    u'has been reviewed and approved.'))

    class Meta:
        model = Version
        fields = ('releasenotes', 'approvalnotes')

    def __init__(self, *args, **kwargs):
        super(AppVersionForm, self).__init__(*args, **kwargs)
        self.fields['publish_immediately'].initial = (
            self.instance.addon.publish_type == mkt.PUBLISH_IMMEDIATE)

    def save(self, *args, **kwargs):
        rval = super(AppVersionForm, self).save(*args, **kwargs)
        if self.instance.all_files[0].status == mkt.STATUS_PENDING:
            # If version is pending, allow changes to publish_type.
            if self.cleaned_data.get('publish_immediately'):
                publish_type = mkt.PUBLISH_IMMEDIATE
            else:
                publish_type = mkt.PUBLISH_PRIVATE
            self.instance.addon.update(publish_type=publish_type)
        return rval
Ejemplo n.º 3
0
class AppDetailsBasicForm(TranslationFormMixin, happyforms.ModelForm):
    """Form for "Details" submission step."""
    PRIVACY_MDN_URL = ('https://developer.mozilla.org/Marketplace/'
                       'Publishing/Policies_and_Guidelines/Privacy_policies')

    PUBLISH_CHOICES = (
        (amo.PUBLISH_IMMEDIATE,
         _lazy(u'Publish my app and make it visible to everyone in the '
               u'Marketplace and include it in search results.')),
        (amo.PUBLISH_PRIVATE,
         _lazy(u'Do not publish my app. Notify me and I will adjust app '
               u'visibility after it is approved.')),
    )

    app_slug = forms.CharField(max_length=30,
                               widget=forms.TextInput(attrs={'class': 'm'}))
    description = TransField(
        label=_lazy(u'Description:'),
        help_text=_lazy(u'This description will appear on the details page.'),
        widget=TransTextarea(attrs={'rows': 4}))
    privacy_policy = TransField(
        label=_lazy(u'Privacy Policy:'),
        widget=TransTextarea(attrs={'rows': 6}),
        help_text=_lazy(
            u'A privacy policy explains how you handle data received '
            u'through your app.  For example: what data do you receive? '
            u'How do you use it? Who do you share it with? Do you '
            u'receive personal information? Do you take steps to make '
            u'it anonymous? What choices do users have to control what '
            u'data you and others receive? Enter your privacy policy '
            u'link or text above.  If you don\'t have a privacy '
            u'policy, <a href="{url}" target="_blank">learn more on how to '
            u'write one.</a>'))
    homepage = TransField.adapt(forms.URLField)(
        label=_lazy(u'Homepage:'),
        required=False,
        widget=TransInput(attrs={'class': 'full'}),
        help_text=_lazy(
            u'If your app has another homepage, enter its address here.'))
    support_url = TransField.adapt(forms.URLField)(
        label=_lazy(u'Support Website:'),
        required=False,
        widget=TransInput(attrs={'class': 'full'}),
        help_text=_lazy(
            u'If your app has a support website or forum, enter its address '
            u'here.'))
    support_email = TransField.adapt(forms.EmailField)(
        label=_lazy(u'Support Email:'),
        widget=TransInput(attrs={'class': 'full'}),
        help_text=_lazy(
            u'This email address will be listed publicly on the Marketplace '
            u'and used by end users to contact you with support issues. This '
            u'email address will be listed publicly on your app details page.')
    )
    flash = forms.TypedChoiceField(
        label=_lazy(u'Does your app require Flash support?'),
        required=False,
        coerce=lambda x: bool(int(x)),
        initial=0,
        widget=forms.RadioSelect,
        choices=((1, _lazy(u'Yes')), (0, _lazy(u'No'))))
    notes = forms.CharField(
        label=_lazy(u'Your comments for reviewers'),
        required=False,
        widget=forms.Textarea(attrs={'rows': 2}),
        help_text=_lazy(
            u'Your app will be reviewed by Mozilla before it becomes publicly '
            u'listed on the Marketplace. Enter any special instructions for '
            u'the app reviewers here.'))
    publish_type = forms.TypedChoiceField(
        label=_lazy(u'Once your app is approved, choose a publishing option:'),
        choices=PUBLISH_CHOICES,
        initial=amo.PUBLISH_IMMEDIATE,
        widget=forms.RadioSelect())

    class Meta:
        model = Webapp
        fields = ('app_slug', 'description', 'privacy_policy', 'homepage',
                  'support_url', 'support_email', 'publish_type')

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

        # TODO: remove this and put it in the field definition above.
        # See https://bugzilla.mozilla.org/show_bug.cgi?id=1072513
        privacy_field = self.base_fields['privacy_policy']
        privacy_field.help_text = mark_safe(
            privacy_field.help_text.format(url=self.PRIVACY_MDN_URL))

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

    def clean_app_slug(self):
        slug = self.cleaned_data['app_slug']
        slug_validator(slug, lower=False)

        if slug != self.instance.app_slug:
            if Webapp.objects.filter(app_slug=slug).exists():
                raise forms.ValidationError(
                    _('This slug is already in use. Please choose another.'))

            if BlacklistedSlug.blocked(slug):
                raise forms.ValidationError(
                    _('The slug cannot be "%s". Please choose another.' %
                      slug))

        return slug.lower()

    def save(self, *args, **kw):
        if self.data['notes']:
            create_comm_note(self.instance,
                             self.instance.versions.latest(),
                             self.request.user,
                             self.data['notes'],
                             note_type=comm.SUBMISSION)
        self.instance = super(AppDetailsBasicForm, self).save(commit=True)
        uses_flash = self.cleaned_data.get('flash')
        af = self.instance.get_latest_file()
        if af is not None:
            af.update(uses_flash=bool(uses_flash))

        return self.instance
Ejemplo n.º 4
0
class WebsiteForm(TranslationFormMixin, happyforms.ModelForm):
    categories = forms.MultipleChoiceField(label=_lazy(u'Categories'),
                                           choices=CATEGORY_CHOICES,
                                           widget=forms.CheckboxSelectMultiple)
    description = TransField(label=_lazy(u'Description'),
                             widget=TransTextarea(attrs={'rows': 4}))
    devices = forms.MultipleChoiceField(label=_lazy(u'Compatible Devices'),
                                        choices=DEVICE_CHOICES,
                                        widget=forms.SelectMultiple)
    keywords = forms.CharField(label=_lazy(u'Keywords'),
                               required=False,
                               widget=forms.Textarea(attrs={'rows': 2}))
    name = TransField(label=_lazy(u'Name'), widget=TransInput())
    preferred_regions = forms.MultipleChoiceField(
        label=_lazy(u'Preferred Regions'),
        choices=REGIONS_CHOICES_NAME,
        required=False,
        widget=forms.SelectMultiple(attrs={'size': 10}))
    short_name = TransField(label=_lazy(u'Short Name'),
                            widget=TransInput(),
                            required=False)
    title = TransField(label=_lazy(u'Title'),
                       widget=TransInput(),
                       required=False)
    url = forms.URLField(label=_lazy(u'URL'))

    class Meta(object):
        model = Website
        fields = ('categories', 'description', 'devices', 'is_disabled',
                  'keywords', 'mobile_url', 'name', 'preferred_regions',
                  'short_name', 'status', 'title', 'tv_url', 'url')

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

        keywords = self.instance.keywords.values_list('tag_text', flat=True)
        self.initial['keywords'] = ', '.join(keywords)

    def clean_categories(self):
        categories = self.cleaned_data['categories']
        max_cat = mkt.MAX_CATEGORIES

        if len(set(categories)) > max_cat:
            # L10n: {0} is the number of categories.
            raise forms.ValidationError(
                ngettext('You can have only {0} category.',
                         'You can have only {0} categories.',
                         max_cat).format(max_cat))

        return categories

    def clean_keywords(self):
        # We set a high `max_tags` here b/c the keywords data is coming from
        # the website meta data which can contain a higher number of tags than
        # apps.
        return clean_tags(self.request,
                          self.cleaned_data['keywords'],
                          max_tags=100)

    def clean_preferred_regions(self):
        try:
            regions = map(int, self.cleaned_data.get('preferred_regions'))
        except (TypeError, ValueError):
            # Data is not a list or data contains non-integers.
            raise forms.ValidationError(_('Invalid region(s) selected.'))

        return list(regions)

    def clean_devices(self):
        try:
            devices = map(int, self.cleaned_data.get('devices'))
        except (TypeError, ValueError):
            # Data is not a list or data contains non-integers.
            raise forms.ValidationError(_('Invalid device(s) selected.'))

        return list(devices)

    def save(self, commit=False):
        form = super(WebsiteForm, self).save(commit=False)

        keywords_new = self.cleaned_data['keywords']
        keywords_old = [
            slugify(keyword, spaces=True)
            for keyword in self.instance.keywords.values_list('tag_text',
                                                              flat=True)
        ]
        for k in set(keywords_new) - set(keywords_old):
            Tag(tag_text=k).save_tag(self.instance)
        for k in set(keywords_old) - set(keywords_new):
            Tag(tag_text=k).remove_tag(self.instance)

        form.save()
        return form
Ejemplo n.º 5
0
class AppFormBasic(AddonFormBase):
    """Form to edit basic app info."""
    slug = forms.CharField(max_length=30, widget=forms.TextInput)
    manifest_url = forms.URLField()
    hosted_url = forms.CharField(
        label=_lazy(u'Hosted URL:'), required=False,
        help_text=_lazy(
            u'A URL to where your app is hosted on the web, if it exists. This'
            u' allows users to try out your app before installing it.'))
    description = TransField(
        required=True,
        label=_lazy(u'Provide a detailed description of your app'),
        help_text=_lazy(u'This description will appear on the details page.'),
        widget=TransTextarea)
    tags = forms.CharField(
        label=_lazy(u'Search Keywords:'), required=False,
        widget=forms.Textarea(attrs={'rows': 3}),
        help_text=_lazy(
            u'The search keywords are used to return search results in the '
            u'Firefox Marketplace. Be sure to include a keywords that '
            u'accurately reflect your app.'))

    class Meta:
        model = Webapp
        fields = ('slug', 'manifest_url', 'hosted_url', 'description', 'tags')

    def __init__(self, *args, **kw):
        # Force the form to use app_slug. We want to keep
        # this under "slug" so all the js continues to work.
        kw.setdefault('initial', {})['slug'] = kw['instance'].app_slug

        super(AppFormBasic, self).__init__(*args, **kw)

        self.old_manifest_url = self.instance.manifest_url

        if self.instance.is_packaged:
            # Manifest URL cannot be changed for packaged apps.
            del self.fields['manifest_url']

        self.initial['tags'] = ', '.join(self.get_tags(self.instance))

    def clean_tags(self):
        return clean_tags(self.request, self.cleaned_data['tags'])

    def get_tags(self, addon):
        if can_edit_restricted_tags(self.request):
            return list(addon.tags.values_list('tag_text', flat=True))
        else:
            return list(addon.tags.filter(restricted=False)
                        .values_list('tag_text', flat=True))

    def _post_clean(self):
        # Switch slug to app_slug in cleaned_data and self._meta.fields so
        # we can update the app_slug field for webapps.
        try:
            self._meta.fields = list(self._meta.fields)
            slug_idx = self._meta.fields.index('slug')
            data = self.cleaned_data
            if 'slug' in data:
                data['app_slug'] = data.pop('slug')
            self._meta.fields[slug_idx] = 'app_slug'
            super(AppFormBasic, self)._post_clean()
        finally:
            self._meta.fields[slug_idx] = 'slug'

    def clean_slug(self):
        slug = self.cleaned_data['slug']
        slug_validator(slug, lower=False)

        if slug != self.instance.app_slug:
            if Webapp.objects.filter(app_slug=slug).exists():
                raise forms.ValidationError(
                    _('This slug is already in use. Please choose another.'))

            if BlockedSlug.blocked(slug):
                raise forms.ValidationError(_('The slug cannot be "%s". '
                                              'Please choose another.' % slug))

        return slug.lower()

    def clean_manifest_url(self):
        manifest_url = self.cleaned_data['manifest_url']
        # Only verify if manifest changed.
        if 'manifest_url' in self.changed_data:
            verify_app_domain(manifest_url, exclude=self.instance)
        return manifest_url

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

        if 'manifest_url' in self.changed_data:
            before_url = self.old_manifest_url
            after_url = self.cleaned_data['manifest_url']

            # If a non-admin edited the manifest URL, add to Re-review Queue.
            if not acl.action_allowed(self.request, 'Admin', '%'):
                log.info(u'[Webapp:%s] (Re-review) Manifest URL changed '
                         u'from %s to %s'
                         % (self.instance, before_url, after_url))

                msg = (_(u'Manifest URL changed from {before_url} to '
                         u'{after_url}')
                       .format(before_url=before_url, after_url=after_url))

                RereviewQueue.flag(self.instance,
                                   mkt.LOG.REREVIEW_MANIFEST_URL_CHANGE, msg)

            # Refetch the new manifest.
            log.info('Manifest %s refreshed for %s'
                     % (addon.manifest_url, addon))
            update_manifests.delay([self.instance.id])

        tags_new = self.cleaned_data['tags']
        tags_old = [slugify(t, spaces=True) for t in self.get_tags(addon)]

        add_tags = set(tags_new) - set(tags_old)
        del_tags = set(tags_old) - set(tags_new)

        # Add new tags.
        for t in add_tags:
            Tag(tag_text=t).save_tag(addon)

        # Remove old tags.
        for t in del_tags:
            Tag(tag_text=t).remove_tag(addon)

        return addonform
Ejemplo n.º 6
0
class AppFormBasic(AddonFormBase):
    """Form to edit basic app info."""
    slug = forms.CharField(max_length=30, widget=forms.TextInput)
    manifest_url = forms.URLField()
    description = TransField(
        required=True,
        label=_lazy(u'Provide a detailed description of your app'),
        help_text=_lazy(u'This description will appear on the details page.'),
        widget=TransTextarea)

    class Meta:
        model = Webapp
        fields = ('slug', 'manifest_url', 'description')

    def __init__(self, *args, **kw):
        # Force the form to use app_slug. We want to keep
        # this under "slug" so all the js continues to work.
        kw.setdefault('initial', {})['slug'] = kw['instance'].app_slug

        super(AppFormBasic, self).__init__(*args, **kw)

        self.old_manifest_url = self.instance.manifest_url

        if self.instance.is_packaged:
            # Manifest URL cannot be changed for packaged apps.
            del self.fields['manifest_url']

    def _post_clean(self):
        # Switch slug to app_slug in cleaned_data and self._meta.fields so
        # we can update the app_slug field for webapps.
        try:
            self._meta.fields = list(self._meta.fields)
            slug_idx = self._meta.fields.index('slug')
            data = self.cleaned_data
            if 'slug' in data:
                data['app_slug'] = data.pop('slug')
            self._meta.fields[slug_idx] = 'app_slug'
            super(AppFormBasic, self)._post_clean()
        finally:
            self._meta.fields[slug_idx] = 'slug'

    def clean_slug(self):
        slug = self.cleaned_data['slug']
        slug_validator(slug, lower=False)

        if slug != self.instance.app_slug:
            if Webapp.objects.filter(app_slug=slug).exists():
                raise forms.ValidationError(
                    _('This slug is already in use. Please choose another.'))

            if BlacklistedSlug.blocked(slug):
                raise forms.ValidationError(
                    _('The slug cannot be "%s". '
                      'Please choose another.' % slug))

        return slug.lower()

    def clean_manifest_url(self):
        manifest_url = self.cleaned_data['manifest_url']
        # Only verify if manifest changed.
        if 'manifest_url' in self.changed_data:
            verify_app_domain(manifest_url, exclude=self.instance)
        return manifest_url

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

        if 'manifest_url' in self.changed_data:
            before_url = self.old_manifest_url
            after_url = self.cleaned_data['manifest_url']

            # If a non-admin edited the manifest URL, add to Re-review Queue.
            if not acl.action_allowed(self.request, 'Admin', '%'):
                log.info(u'[Webapp:%s] (Re-review) Manifest URL changed '
                         u'from %s to %s' %
                         (self.instance, before_url, after_url))

                msg = (_(u'Manifest URL changed from {before_url} to '
                         u'{after_url}').format(before_url=before_url,
                                                after_url=after_url))

                RereviewQueue.flag(self.instance,
                                   amo.LOG.REREVIEW_MANIFEST_URL_CHANGE, msg)

            # Refetch the new manifest.
            log.info('Manifest %s refreshed for %s' %
                     (addon.manifest_url, addon))
            update_manifests.delay([self.instance.id])

        return addonform
Ejemplo n.º 7
0
class AdminSettingsForm(PreviewForm):
    DELETE = forms.BooleanField(required=False)
    mozilla_contact = SeparatedValuesField(forms.EmailField,
                                           separator=',',
                                           required=False)
    vip_app = forms.BooleanField(required=False)
    priority_review = forms.BooleanField(required=False)
    tags = forms.CharField(required=False)
    banner_regions = JSONMultipleChoiceField(
        required=False, choices=mkt.regions.REGIONS_CHOICES_NAME)
    banner_message = TransField(required=False)

    class Meta:
        model = Preview
        fields = ('file_upload', 'upload_hash', 'position')

    def __init__(self, *args, **kw):
        # Note that this form is not inheriting from AddonFormBase, so we have
        # to get rid of 'version' ourselves instead of letting the parent class
        # do it.
        kw.pop('version', None)

        # Get the object for the app's promo `Preview` and pass it to the form.
        if kw.get('instance'):
            addon = kw.pop('instance')
            self.instance = addon
            self.promo = addon.get_promo()

        self.request = kw.pop('request', None)

        # Note: After calling `super`, `self.instance` becomes the `Preview`
        # object.
        super(AdminSettingsForm, self).__init__(*args, **kw)

        self.initial['vip_app'] = addon.vip_app
        self.initial['priority_review'] = addon.priority_review

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

        self.initial['banner_regions'] = addon.geodata.banner_regions or []
        self.initial['banner_message'] = addon.geodata.banner_message_id

    @property
    def regions_by_id(self):
        return mkt.regions.REGIONS_CHOICES_ID_DICT

    def clean_position(self):
        return -1

    def clean_banner_regions(self):
        try:
            regions = map(int, self.cleaned_data.get('banner_regions'))
        except (TypeError, ValueError):
            # input data is not a list or data contains non-integers.
            raise forms.ValidationError(_('Invalid region(s) selected.'))

        return list(regions)

    def get_tags(self, addon):
        if acl.action_allowed(self.request, 'Apps', 'Edit'):
            return list(addon.tags.values_list('tag_text', flat=True))
        else:
            return list(
                addon.tags.filter(restricted=False).values_list('tag_text',
                                                                flat=True))

    def clean_tags(self):
        return clean_tags(self.request, self.cleaned_data['tags'])

    def clean_mozilla_contact(self):
        contact = self.cleaned_data.get('mozilla_contact')
        if self.cleaned_data.get('mozilla_contact') is None:
            return u''
        return contact

    def save(self, addon, commit=True):
        if (self.cleaned_data.get('DELETE')
                and 'upload_hash' not in self.changed_data and self.promo.id):
            self.promo.delete()
        elif self.promo and 'upload_hash' in self.changed_data:
            self.promo.delete()
        elif self.cleaned_data.get('upload_hash'):
            super(AdminSettingsForm, self).save(addon, True)

        updates = {
            'vip_app': self.cleaned_data.get('vip_app'),
            'priority_review': self.cleaned_data.get('priority_review'),
        }
        contact = self.cleaned_data.get('mozilla_contact')
        if contact is not None:
            updates['mozilla_contact'] = contact
        addon.update(**updates)

        tags_new = self.cleaned_data['tags']
        tags_old = [slugify(t, spaces=True) for t in self.get_tags(addon)]

        add_tags = set(tags_new) - set(tags_old)
        del_tags = set(tags_old) - set(tags_new)

        # Add new tags.
        for t in add_tags:
            Tag(tag_text=t).save_tag(addon)

        # Remove old tags.
        for t in del_tags:
            Tag(tag_text=t).remove_tag(addon)

        geodata = addon.geodata
        geodata.banner_regions = self.cleaned_data.get('banner_regions')
        geodata.banner_message = self.cleaned_data.get('banner_message')
        geodata.save()

        uses_flash = self.cleaned_data.get('flash')
        af = addon.get_latest_file()
        if af is not None:
            af.update(uses_flash=bool(uses_flash))

        index_webapps.delay([addon.id])

        return addon