class _Form(TranslationFormMixin, happyforms.ModelForm): the_reason = TransField(widget=TransTextarea(), required=fields_required, label=the_reason_label) the_future = TransField(widget=TransTextarea(), required=fields_required, label=the_future_label) class Meta: model = Addon fields = ('the_reason', 'the_future')
class AddonFormBasicUnlisted(AkismetSpamCheckFormMixin, AddonFormBase): name = TransField(max_length=50) slug = forms.CharField(max_length=30) summary = TransField(widget=TransTextarea(attrs={'rows': 4}), max_length=250) fields_to_akismet_comment_check = ['name', 'summary'] class Meta: model = Addon fields = ('name', 'slug', 'summary')
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
class DescribeFormUnlisted(AkismetSpamCheckFormMixin, AddonFormBase): name = TransField(max_length=50) slug = forms.CharField(max_length=30) summary = TransField(widget=TransTextarea(attrs={'rows': 4}), max_length=250) description = TransField(widget=TransTextarea(attrs={'rows': 4}), required=False) fields_to_akismet_comment_check = ['name', 'summary', 'description'] class Meta: model = Addon fields = ('name', 'slug', 'summary', 'description')
class AddonFormSupport(AddonFormBase): support_url = TransField.adapt(HttpHttpsOnlyURLField)(required=False) support_email = TransField.adapt(forms.EmailField)(required=False) class Meta: model = Addon fields = ('support_email', 'support_url') def __init__(self, *args, **kw): super(AddonFormSupport, self).__init__(*args, **kw) def save(self, addon, commit=True): return super(AddonFormSupport, self).save(commit)
class Step3Form(AddonFormBasic): description = TransField(widget=TransTextarea, required=False) tags = None class Meta: model = Addon fields = ('name', 'slug', 'summary', 'description')
class AddonFormDetails(AddonFormBase): default_locale = forms.TypedChoiceField(choices=LOCALES) homepage = TransField.adapt(HttpHttpsOnlyURLField)(required=False) class Meta: model = Addon fields = ('description', 'default_locale', 'homepage') def clean(self): # Make sure we have the required translations in the new locale. required = 'name', 'summary', '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 = 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] # They might be setting description right now. if 'description' in missing and locale in data['description']: missing.remove('description') 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 data
class Step3Form(AddonFormBasic): description = TransField(widget=TransTextarea, required=False) class Meta: model = Addon fields = ('name', 'slug', 'summary', 'tags', 'description', 'homepage', 'support_email', 'support_url')
class AddonFormTechnical(AddonFormBase): developer_comments = TransField(widget=TransTextarea, required=False) class Meta: model = Addon fields = ('developer_comments', 'view_source', 'external_software', 'auto_repackage', 'public_stats')
class PreviewForm(happyforms.ModelForm): caption = TransField(widget=TransTextarea, required=False) file_upload = forms.FileField(required=False) upload_hash = forms.CharField(required=False) def save(self, addon, commit=True): if self.cleaned_data: self.instance.addon = addon if self.cleaned_data.get('DELETE'): # Existing preview. if self.instance.id: self.instance.delete() # User has no desire to save this preview. return super(PreviewForm, self).save(commit=commit) if self.cleaned_data['upload_hash']: upload_hash = self.cleaned_data['upload_hash'] upload_path = os.path.join(settings.TMP_PATH, 'preview', upload_hash) tasks.resize_preview.delay(upload_path, self.instance, set_modified_on=[self.instance]) class Meta: model = Preview fields = ('caption', 'file_upload', 'upload_hash', 'id', 'position')
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) class Meta: model = Addon fields = ('name', 'slug', 'summary', 'tags') 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)) # Do not simply append validators, as validators will persist between # instances. def validate_name(name): return clean_addon_name(name, self.instance) name_validators = list(self.fields['name'].validators) name_validators.append(validate_name) self.fields['name'].validators = name_validators 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
class PolicyForm(TranslationFormMixin, AMOModelForm): """Form for editing the add-ons EULA and privacy policy.""" has_eula = forms.BooleanField( required=False, label=_lazy(u'This add-on has an End-User License Agreement')) eula = TransField(widget=TransTextarea(), required=False, label=_lazy(u"Please specify your add-on's " "End-User License Agreement:")) has_priv = forms.BooleanField( required=False, label=_lazy(u"This add-on has a Privacy Policy"), label_suffix='') privacy_policy = TransField( widget=TransTextarea(), required=False, label=_lazy(u"Please specify your add-on's Privacy Policy:")) def __init__(self, *args, **kw): self.addon = kw.pop('addon', None) if not self.addon: raise ValueError('addon keyword arg cannot be None') kw['instance'] = self.addon kw['initial'] = dict(has_priv=self._has_field('privacy_policy'), has_eula=self._has_field('eula')) super(PolicyForm, self).__init__(*args, **kw) def _has_field(self, name): # If there's a eula in any language, this addon has a eula. n = getattr(self.addon, u'%s_id' % name) return any(map(bool, Translation.objects.filter(id=n))) class Meta: model = Addon fields = ('eula', 'privacy_policy') def save(self, commit=True): ob = super(PolicyForm, self).save(commit) for k, field in (('has_eula', 'eula'), ('has_priv', 'privacy_policy')): if not self.cleaned_data[k]: delete_translation(self.instance, field) if 'privacy_policy' in self.changed_data: ActivityLog.create(amo.LOG.CHANGE_POLICY, self.addon, self.instance) return ob
class VersionForm(WithSourceMixin, happyforms.ModelForm): releasenotes = TransField(widget=TransTextarea(), required=False) approvalnotes = forms.CharField( widget=TranslationTextarea(attrs={'rows': 4}), required=False) source = forms.FileField(required=False, widget=SourceFileInput) class Meta: model = Version fields = ('releasenotes', 'approvalnotes', 'source')
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) is_experimental = forms.BooleanField(required=False) class Meta: model = Addon fields = ('name', 'slug', 'summary', 'tags', 'is_experimental') 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_name(self): return clean_addon_name(self.cleaned_data['name'], self.instance) def clean_slug(self): return clean_addon_slug(self.cleaned_data['slug'], self.instance) 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
class DescribeForm(AddonFormBase): name = TransField(max_length=50) slug = forms.CharField(max_length=30) summary = TransField(widget=TransTextarea(attrs={'rows': 4}), max_length=250) is_experimental = forms.BooleanField(required=False) support_url = TransField.adapt(HttpHttpsOnlyURLField)(required=False) support_email = TransField.adapt(forms.EmailField)(required=False) has_priv = forms.BooleanField( required=False, label=_lazy(u"This add-on has a Privacy Policy"), label_suffix='') privacy_policy = TransField( widget=TransTextarea(), required=False, label=_lazy(u"Please specify your add-on's Privacy Policy:")) class Meta: model = Addon fields = ('name', 'slug', 'summary', 'is_experimental', 'support_url', 'support_email', 'privacy_policy') def __init__(self, *args, **kw): kw['initial'] = { 'has_priv': self._has_field('privacy_policy', kw['instance']) } super(DescribeForm, self).__init__(*args, **kw) def clean_name(self): return clean_addon_name(self.cleaned_data['name'], self.instance) def _has_field(self, name, instance=None): # If there's a policy in any language, this addon has a policy. n = getattr(instance or self.instance, u'%s_id' % name) return any(map(bool, Translation.objects.filter(id=n))) def save(self, commit=True): obj = super(DescribeForm, self).save(commit) if not self.cleaned_data['has_priv']: delete_translation(self.instance, 'privacy_policy') return obj
class DescribeForm(AkismetSpamCheckFormMixin, AddonFormBase): name = TransField(max_length=50) slug = forms.CharField(max_length=30) summary = TransField(widget=TransTextarea(attrs={'rows': 4}), max_length=250) description = TransField(widget=TransTextarea(attrs={'rows': 6}), min_length=10) is_experimental = forms.BooleanField(required=False) requires_payment = forms.BooleanField(required=False) support_url = TransField.adapt(HttpHttpsOnlyURLField)(required=False) support_email = TransField.adapt(forms.EmailField)(required=False) has_priv = forms.BooleanField(required=False, label=_(u'This add-on has a Privacy Policy'), label_suffix='') privacy_policy = TransField( widget=TransTextarea(), required=False, label=_(u'Please specify your add-on\'s Privacy Policy:')) fields_to_akismet_comment_check = ['name', 'summary', 'description'] class Meta: model = Addon fields = ('name', 'slug', 'summary', 'description', 'is_experimental', 'support_url', 'support_email', 'privacy_policy', 'requires_payment') def __init__(self, *args, **kw): kw['initial'] = { 'has_priv': self._has_field('privacy_policy', kw['instance']) } super(DescribeForm, self).__init__(*args, **kw) content_waffle = waffle.switch_is_active('content-optimization') if not content_waffle or self.instance.type != amo.ADDON_EXTENSION: description = self.fields['description'] description.min_length = None description.widget.attrs.pop('minlength', None) description.validators = [ validator for validator in description.validators if not isinstance(validator, MinLengthValidator) ] description.required = False def _has_field(self, name, instance=None): # If there's a policy in any language, this addon has a policy. n = getattr(instance or self.instance, u'%s_id' % name) return any(map(bool, Translation.objects.filter(id=n))) def save(self, commit=True): obj = super(DescribeForm, self).save(commit) if not self.cleaned_data['has_priv']: delete_translation(self.instance, 'privacy_policy') return obj
class ContribForm(TranslationFormMixin, happyforms.ModelForm): RECIPIENTS = (('dev', _(u'The developers of this add-on')), ('moz', _(u'The Mozilla Foundation')), ('org', _(u'An organization of my choice'))) recipient = forms.ChoiceField( choices=RECIPIENTS, widget=forms.RadioSelect(attrs={'class': 'recipient'})) thankyou_note = TransField(widget=TransTextarea(), required=False) class Meta: model = Addon fields = ('paypal_id', 'suggested_amount', 'annoying', 'enable_thankyou', 'thankyou_note') widgets = { 'annoying': forms.RadioSelect(), 'suggested_amount': forms.TextInput(attrs={'class': 'short'}), 'paypal_id': forms.TextInput(attrs={'size': '50'}) } @staticmethod def initial(addon): if addon.charity: recip = 'moz' if addon.charity_id == amo.FOUNDATION_ORG else 'org' else: recip = 'dev' return { 'recipient': recip, 'annoying': addon.annoying or amo.CONTRIB_PASSIVE } def clean(self): data = self.cleaned_data try: if not self.errors and data['recipient'] == 'dev': check_paypal_id(data['paypal_id']) except forms.ValidationError, e: self.errors['paypal_id'] = self.error_class(e.messages) # thankyou_note is a dict since it's a Translation. if not (data.get('enable_thankyou') and any(data.get('thankyou_note').values())): data['thankyou_note'] = {} data['enable_thankyou'] = False return data
class VersionForm(WithSourceMixin, forms.ModelForm): releasenotes = TransField(widget=TransTextarea(), required=False) approvalnotes = forms.CharField( widget=TranslationTextarea(attrs={'rows': 4}), required=False) source = forms.FileField(required=False, widget=SourceFileInput) clear_pending_info_request = forms.BooleanField(required=False) class Meta: model = Version fields = ( 'releasenotes', 'clear_pending_info_request', 'approvalnotes', 'source', ) def __init__(self, *args, **kwargs): self.request = kwargs.pop('request') super(VersionForm, self).__init__(*args, **kwargs) # Fetch latest reviewer comment if the addon has a pending info # request, so that the template in which the form is used can display # that comment. if self.instance and self.instance.addon.pending_info_request: try: self.pending_info_request_comment = ( ActivityLog.objects.for_addons(self.instance.addon).filter( action=amo.LOG.REQUEST_INFORMATION.id).latest('pk') ).details['comments'] except (ActivityLog.DoesNotExist, KeyError): self.pending_info_request_comment = '' def save(self, *args, **kwargs): super(VersionForm, self).save(*args, **kwargs) # Clear pending info request on the addon if requested, adding an entry # in the Activity Log to indicate that. if self.cleaned_data.get('clear_pending_info_request'): AddonReviewerFlags.objects.update_or_create( addon=self.instance.addon, defaults={'pending_info_request': None}) log_and_notify(amo.LOG.DEVELOPER_CLEAR_INFO_REQUEST, None, self.request.user, self.instance)
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
class EditThemeForm(AddonFormBase): name = TransField(max_length=50, label=_('Give Your Theme a Name.')) slug = forms.CharField(max_length=30) category = forms.ModelChoiceField(queryset=Category.objects.all(), widget=forms.widgets.RadioSelect) description = TransField( widget=TransTextarea(attrs={'rows': 4}), max_length=500, required=False, label=_('Describe your Theme.')) tags = forms.CharField(required=False) accentcolor = ColorField( required=False, widget=forms.TextInput(attrs={'class': 'color-picker'}), ) textcolor = ColorField( required=False, widget=forms.TextInput(attrs={'class': 'color-picker'}), ) license = forms.TypedChoiceField( choices=amo.PERSONA_LICENSES_CHOICES, coerce=int, empty_value=None, widget=forms.HiddenInput, error_messages={'required': _(u'A license must be selected.')}) # Theme re-upload. header = forms.FileField(required=False) header_hash = forms.CharField(widget=forms.HiddenInput, required=False) class Meta: model = Addon fields = ('name', 'slug', 'description', 'tags') def __init__(self, *args, **kw): self.request = kw.pop('request') super(AddonFormBase, self).__init__(*args, **kw) addon = Addon.objects.no_cache().get(id=self.instance.id) persona = addon.persona # Allow theme artists to localize Name and Description. for trans in Translation.objects.filter(id=self.initial['name']): self.initial['name_' + trans.locale.lower()] = trans for trans in Translation.objects.filter( id=self.initial['description']): self.initial['description_' + trans.locale.lower()] = trans self.old_tags = self.get_tags(addon) self.initial['tags'] = ', '.join(self.old_tags) if persona.accentcolor: self.initial['accentcolor'] = '#' + persona.accentcolor if persona.textcolor: self.initial['textcolor'] = '#' + persona.textcolor self.initial['license'] = persona.license cats = sorted(Category.objects.filter(type=amo.ADDON_PERSONA, weight__gte=0), key=lambda x: x.name) self.fields['category'].choices = [(c.id, c.name) for c in cats] try: self.initial['category'] = addon.categories.values_list( 'id', flat=True)[0] except IndexError: pass for field in ('header', ): self.fields[field].widget.attrs = { 'data-upload-url': reverse('devhub.personas.reupload_persona', args=[addon.slug, 'persona_%s' % field]), 'data-allowed-types': amo.SUPPORTED_IMAGE_TYPES } def clean_slug(self): return clean_addon_slug(self.cleaned_data['slug'], self.instance) def save(self): addon = self.instance persona = addon.persona data = self.cleaned_data # Update Persona-specific data. persona_data = { 'license': int(data['license']), 'accentcolor': data['accentcolor'].lstrip('#'), 'textcolor': data['textcolor'].lstrip('#'), 'author': self.request.user.username, 'display_username': self.request.user.name } changed = False for k, v in persona_data.iteritems(): if v != getattr(persona, k): changed = True setattr(persona, k, v) if changed: persona.save() if self.changed_data: ActivityLog.create(amo.LOG.EDIT_PROPERTIES, addon) self.instance.modified = datetime.now() # Update Addon-specific data. changed = ( set(self.old_tags) != data['tags'] or # Check if tags changed. self.initial['slug'] != data['slug'] or # Check if slug changed. transfield_changed('description', self.initial, data) or transfield_changed('name', self.initial, data)) if changed: # Only save if addon data changed. super(EditThemeForm, self).save() # Update tags. tags_new = data['tags'] tags_old = [slugify(t, spaces=True) for t in self.old_tags] # 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) # Update category. if data['category'].id != self.initial['category']: addon_cat = addon.addoncategory_set.all()[0] addon_cat.category = data['category'] addon_cat.save() # Theme reupload. if not addon.is_pending(): if data['header_hash']: save_theme_reupload.delay(data['header_hash'], addon.pk) return data
class AppFormBasic(AddonFormBasic): """Form to override name length for apps.""" name = TransField(max_length=128)
class DescribeFormContentOptimization(CombinedNameSummaryCleanMixin, DescribeForm): name = TransField(min_length=2) summary = TransField(min_length=2)
class AddonFormDetailsUnlisted(AddonFormBase): homepage = TransField.adapt(HttpHttpsOnlyURLField)(required=False) class Meta: model = Addon fields = ('description', 'homepage')
class DescribeFormUnlistedContentOptimization(CombinedNameSummaryCleanMixin, DescribeFormUnlisted): name = TransField(max_length=68, min_length=2) summary = TransField(max_length=68, min_length=2)