612 строки
23 KiB
Python
612 строки
23 KiB
Python
from datetime import datetime
|
|
import os
|
|
import re
|
|
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.forms.formsets import formset_factory
|
|
|
|
import commonware.log
|
|
import happyforms
|
|
from quieter_formset.formset import BaseFormSet
|
|
from tower import ugettext as _, ugettext_lazy as _lazy, ungettext as ngettext
|
|
|
|
from access import acl
|
|
import amo
|
|
import captcha.fields
|
|
from amo.fields import ColorField
|
|
from amo.helpers import loc
|
|
from amo.urlresolvers import reverse
|
|
from amo.utils import slug_validator, slugify, sorted_groupby, remove_icons
|
|
from addons.models import (Addon, AddonCategory, AddonDeviceType, AddonUser,
|
|
BlacklistedSlug, Category, DeviceType, Persona,
|
|
ReverseNameLookup)
|
|
from addons.widgets import IconWidgetRenderer, CategoriesSelectMultiple
|
|
from applications.models import Application
|
|
from devhub import tasks as devhub_tasks
|
|
from tags.models import Tag
|
|
from translations.fields import TransField, TransTextarea
|
|
from translations.forms import TranslationFormMixin
|
|
from translations.models import Translation
|
|
from translations.widgets import TranslationTextInput
|
|
from versions.models import Version
|
|
|
|
log = commonware.log.getLogger('z.addons')
|
|
|
|
|
|
def clean_name(name, instance=None):
|
|
if not instance:
|
|
log.debug('clean_name called without an instance: %s' % name)
|
|
if instance:
|
|
id = ReverseNameLookup(instance.is_webapp()).get(name)
|
|
else:
|
|
id = ReverseNameLookup().get(name)
|
|
|
|
# If we get an id and either there's no instance or the instance.id != id.
|
|
if id and (not instance or id != instance.id):
|
|
raise forms.ValidationError(_('This name is already in use. Please '
|
|
'choose another.'))
|
|
return name
|
|
|
|
|
|
def clean_tags(request, tags):
|
|
target = [slugify(t, spaces=True, lower=True) for t in tags.split(',')]
|
|
target = set(filter(None, target))
|
|
|
|
min_len = amo.MIN_TAG_LENGTH
|
|
max_len = Tag._meta.get_field('tag_text').max_length
|
|
max_tags = amo.MAX_TAGS
|
|
total = len(target)
|
|
|
|
blacklisted = (Tag.objects.values_list('tag_text', flat=True)
|
|
.filter(tag_text__in=target, blacklisted=True))
|
|
if blacklisted:
|
|
# L10n: {0} is a single tag or a comma-separated list of tags.
|
|
msg = ngettext('Invalid tag: {0}', 'Invalid tags: {0}',
|
|
len(blacklisted)).format(', '.join(blacklisted))
|
|
raise forms.ValidationError(msg)
|
|
|
|
restricted = (Tag.objects.values_list('tag_text', flat=True)
|
|
.filter(tag_text__in=target, restricted=True))
|
|
if not acl.action_allowed(request, 'Addons', 'Edit'):
|
|
if restricted:
|
|
# L10n: {0} is a single tag or a comma-separated list of tags.
|
|
msg = ngettext('"{0}" is a reserved tag and cannot be used.',
|
|
'"{0}" are reserved tags and cannot be used.',
|
|
len(restricted)).format('", "'.join(restricted))
|
|
raise forms.ValidationError(msg)
|
|
else:
|
|
# Admin's restricted tags don't count towards the limit.
|
|
total = len(target - set(restricted))
|
|
|
|
if total > max_tags:
|
|
num = total - max_tags
|
|
msg = ngettext('You have {0} too many tags.',
|
|
'You have {0} too many tags.', num).format(num)
|
|
raise forms.ValidationError(msg)
|
|
|
|
if any(t for t in target if len(t) > max_len):
|
|
raise forms.ValidationError(_('All tags must be %s characters '
|
|
'or less after invalid characters are removed.' % max_len))
|
|
|
|
if any(t for t in target if len(t) < min_len):
|
|
msg = ngettext("All tags must be at least {0} character.",
|
|
"All tags must be at least {0} characters.",
|
|
min_len).format(min_len)
|
|
raise forms.ValidationError(msg)
|
|
|
|
return target
|
|
|
|
|
|
class AddonFormBase(TranslationFormMixin, happyforms.ModelForm):
|
|
|
|
def __init__(self, *args, **kw):
|
|
self.request = kw.pop('request')
|
|
super(AddonFormBase, self).__init__(*args, **kw)
|
|
|
|
class Meta:
|
|
models = Addon
|
|
fields = ('name', 'slug', 'summary', 'tags')
|
|
|
|
|
|
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):
|
|
# Force the form to use app_slug if this is a webapp. We want to keep
|
|
# this under "slug" so all the js continues to work.
|
|
if kw['instance'].is_webapp():
|
|
kw.setdefault('initial', {})['slug'] = kw['instance'].app_slug
|
|
|
|
super(AddonFormBasic, self).__init__(*args, **kw)
|
|
self.fields['tags'].initial = ', '.join(self.get_tags(self.instance))
|
|
# Do not simply append validators, as validators will persist between
|
|
# instances.
|
|
validate_name = lambda x: clean_name(x, self.instance)
|
|
name_validators = list(self.fields['name'].validators)
|
|
name_validators.append(validate_name)
|
|
self.fields['name'].validators = name_validators
|
|
|
|
def get_tags(self, addon):
|
|
if acl.action_allowed(self.request, 'Addons', 'Edit'):
|
|
return [t.tag_text for t in addon.tags.all()]
|
|
else:
|
|
return [t.tag_text for t in addon.tags.filter(restricted=False)]
|
|
|
|
def save(self, addon, commit=False):
|
|
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
|
|
|
|
def _post_clean(self):
|
|
if self.instance.is_webapp():
|
|
# 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(AddonFormBasic, self)._post_clean()
|
|
finally:
|
|
self._meta.fields[slug_idx] = 'slug'
|
|
else:
|
|
super(AddonFormBasic, self)._post_clean()
|
|
|
|
def clean_tags(self):
|
|
return clean_tags(self.request, self.cleaned_data['tags'])
|
|
|
|
def clean_slug(self):
|
|
target = self.cleaned_data['slug']
|
|
slug_validator(target, lower=False)
|
|
slug_field = 'app_slug' if self.instance.is_webapp() else 'slug'
|
|
|
|
if target != getattr(self.instance, slug_field):
|
|
if Addon.objects.filter(**{slug_field: target}).exists():
|
|
raise forms.ValidationError(_('This slug is already in use.'))
|
|
|
|
if BlacklistedSlug.blocked(target):
|
|
raise forms.ValidationError(_('The slug cannot be: %s.'
|
|
% target))
|
|
return target
|
|
|
|
|
|
class AppFormBasic(AddonFormBasic):
|
|
"""Form to override name length for apps."""
|
|
name = TransField(max_length=128)
|
|
|
|
|
|
class ApplicationChoiceField(forms.ModelChoiceField):
|
|
|
|
def label_from_instance(self, obj):
|
|
return obj.id
|
|
|
|
|
|
class DeviceTypeForm(forms.Form):
|
|
device_types = forms.ModelMultipleChoiceField(
|
|
label=loc('Which device types does your app work with?'),
|
|
queryset=DeviceType.objects.all(),
|
|
initial=DeviceType.objects.all(),
|
|
widget=forms.CheckboxSelectMultiple)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.addon = kwargs.pop('addon')
|
|
super(DeviceTypeForm, self).__init__(*args, **kwargs)
|
|
device_types = AddonDeviceType.objects.filter(
|
|
addon=self.addon).values_list('device_type__id', flat=True)
|
|
if device_types:
|
|
self.initial['device_types'] = device_types
|
|
|
|
def save(self, addon):
|
|
new_types = self.cleaned_data['device_types']
|
|
old_types = addon.device_types
|
|
|
|
# Add new AddonDeviceTypes.
|
|
for d in set(new_types) - set(old_types):
|
|
AddonDeviceType(addon=addon, device_type=d).save()
|
|
|
|
# Remove old AddonDeviceTypes.
|
|
for d in set(old_types) - set(new_types):
|
|
AddonDeviceType.objects.filter(addon=addon, device_type=d).delete()
|
|
|
|
|
|
class CategoryForm(forms.Form):
|
|
application = ApplicationChoiceField(Application.objects.all(),
|
|
widget=forms.HiddenInput,
|
|
required=False)
|
|
categories = forms.ModelMultipleChoiceField(
|
|
queryset=Category.objects.all(), widget=CategoriesSelectMultiple)
|
|
|
|
def save(self, addon):
|
|
application = self.cleaned_data['application']
|
|
categories_new = self.cleaned_data['categories']
|
|
categories_old = [cats for app, cats in addon.app_categories if
|
|
(app and application and app.id == application.id) or
|
|
(not app and not application)]
|
|
if categories_old:
|
|
categories_old = categories_old[0]
|
|
|
|
# Add new categories.
|
|
for c in set(categories_new) - set(categories_old):
|
|
AddonCategory(addon=addon, category=c).save()
|
|
|
|
# Remove old categories.
|
|
for c in set(categories_old) - set(categories_new):
|
|
AddonCategory.objects.filter(addon=addon, category=c).delete()
|
|
|
|
def clean_categories(self):
|
|
categories = self.cleaned_data['categories']
|
|
total = categories.count()
|
|
max_cat = amo.MAX_CATEGORIES
|
|
|
|
if getattr(self, 'disabled', False) and total:
|
|
if categories[0].type == amo.ADDON_WEBAPP:
|
|
raise forms.ValidationError(loc('Categories cannot be changed '
|
|
'while your app is featured for this application.'))
|
|
else:
|
|
raise forms.ValidationError(_('Categories cannot be changed '
|
|
'while your add-on is featured for this application.'))
|
|
if total > 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))
|
|
|
|
has_misc = filter(lambda x: x.misc, categories)
|
|
if has_misc and total > 1:
|
|
raise forms.ValidationError(
|
|
_('The miscellaneous category cannot be combined with '
|
|
'additional categories.'))
|
|
|
|
return categories
|
|
|
|
|
|
class BaseCategoryFormSet(BaseFormSet):
|
|
|
|
def __init__(self, *args, **kw):
|
|
self.addon = kw.pop('addon')
|
|
self.request = kw.pop('request', None)
|
|
super(BaseCategoryFormSet, self).__init__(*args, **kw)
|
|
self.initial = []
|
|
if self.addon.type == amo.ADDON_WEBAPP:
|
|
apps = [None]
|
|
else:
|
|
apps = sorted(self.addon.compatible_apps.keys(),
|
|
key=lambda x: x.id)
|
|
|
|
# Drop any apps that don't have appropriate categories.
|
|
qs = Category.objects.filter(type=self.addon.type)
|
|
if self.addon.type != amo.ADDON_WEBAPP:
|
|
qs = qs.filter(application__in=[a.id for a in apps])
|
|
app_cats = dict((k, list(v)) for k, v in
|
|
sorted_groupby(qs, 'application_id'))
|
|
for app in list(apps):
|
|
if app and not app_cats.get(app.id):
|
|
apps.remove(app)
|
|
if not app_cats:
|
|
apps = []
|
|
|
|
for app in apps:
|
|
cats = dict(self.addon.app_categories).get(app, [])
|
|
self.initial.append({'categories': [c.id for c in cats]})
|
|
|
|
# Reconstruct the forms according to the initial data.
|
|
self._construct_forms()
|
|
|
|
for app, form in zip(apps, self.forms):
|
|
key = app.id if app else None
|
|
form.request = self.request
|
|
form.initial['application'] = key
|
|
form.app = app
|
|
cats = sorted(app_cats[key], key=lambda x: x.name)
|
|
form.fields['categories'].choices = [(c.id, c.name) for c in cats]
|
|
|
|
# If this add-on is featured for this application, category
|
|
# changes are forbidden.
|
|
if not acl.action_allowed(self.request, 'Addons', 'Edit'):
|
|
form.disabled = (app and self.addon.is_featured(app))
|
|
|
|
def save(self):
|
|
for f in self.forms:
|
|
f.save(self.addon)
|
|
|
|
|
|
CategoryFormSet = formset_factory(form=CategoryForm,
|
|
formset=BaseCategoryFormSet, extra=0)
|
|
|
|
|
|
def icons():
|
|
"""
|
|
Generates a list of tuples for the default icons for add-ons,
|
|
in the format (psuedo-mime-type, description).
|
|
"""
|
|
icons = [('image/jpeg', 'jpeg'), ('image/png', 'png'), ('', 'default')]
|
|
dir_list = os.listdir(settings.ADDON_ICONS_DEFAULT_PATH)
|
|
for fname in dir_list:
|
|
if '32' in fname and not 'default' in fname:
|
|
icon_name = fname.split('-')[0]
|
|
icons.append(('icon/%s' % icon_name, icon_name))
|
|
return icons
|
|
|
|
|
|
class AddonFormMedia(AddonFormBase):
|
|
icon_type = forms.CharField(widget=forms.RadioSelect(
|
|
renderer=IconWidgetRenderer, choices=[]), required=False)
|
|
icon_upload_hash = forms.CharField(required=False)
|
|
|
|
class Meta:
|
|
model = Addon
|
|
fields = ('icon_upload_hash', 'icon_type')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddonFormMedia, self).__init__(*args, **kwargs)
|
|
|
|
# Add icons here so we only read the directory when
|
|
# AddonFormMedia is actually being used.
|
|
self.fields['icon_type'].widget.choices = icons()
|
|
|
|
def save(self, addon, commit=True):
|
|
if self.cleaned_data['icon_upload_hash']:
|
|
upload_hash = self.cleaned_data['icon_upload_hash']
|
|
upload_path = os.path.join(settings.TMP_PATH, 'icon', upload_hash)
|
|
|
|
dirname = addon.get_icon_dir()
|
|
destination = os.path.join(dirname, '%s' % addon.id)
|
|
|
|
remove_icons(destination)
|
|
devhub_tasks.resize_icon.delay(upload_path, destination,
|
|
amo.ADDON_ICON_SIZES,
|
|
set_modified_on=[addon])
|
|
|
|
return super(AddonFormMedia, self).save(commit)
|
|
|
|
|
|
class AddonFormDetails(AddonFormBase):
|
|
default_locale = forms.TypedChoiceField(choices=Addon.LOCALES)
|
|
|
|
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(
|
|
_('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
|
|
|
|
|
|
def get_satisfaction(url):
|
|
"""
|
|
If there's a GetSatisfaction URL entered, we'll extract the product
|
|
and company name..
|
|
"""
|
|
gs_regex = "getsatisfaction\.com/(\w*)(?:/products/(\w*))?"
|
|
match = re.search(gs_regex, url)
|
|
if match:
|
|
return match.groups()
|
|
return None, None
|
|
|
|
|
|
class AddonFormSupport(AddonFormBase):
|
|
support_url = TransField.adapt(forms.URLField)(required=False,
|
|
verify_exists=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)
|
|
if self.instance.is_premium():
|
|
self.fields['support_email'].required = True
|
|
|
|
def save(self, addon, commit=True):
|
|
instance = self.instance
|
|
url = instance.support_url.localized_string
|
|
(instance.get_satisfaction_company,
|
|
instance.get_satisfaction_product) = get_satisfaction(url)
|
|
return super(AddonFormSupport, self).save(commit)
|
|
|
|
|
|
class AddonFormTechnical(AddonFormBase):
|
|
developer_comments = TransField(widget=TransTextarea, required=False)
|
|
|
|
class Meta:
|
|
model = Addon
|
|
fields = ('developer_comments', 'view_source', 'site_specific',
|
|
'external_software', 'auto_repackage', 'public_stats')
|
|
|
|
|
|
class AddonForm(happyforms.ModelForm):
|
|
name = forms.CharField(widget=TranslationTextInput,)
|
|
homepage = forms.CharField(widget=TranslationTextInput, required=False)
|
|
eula = forms.CharField(widget=TranslationTextInput,)
|
|
description = forms.CharField(widget=TranslationTextInput,)
|
|
developer_comments = forms.CharField(widget=TranslationTextInput,)
|
|
privacy_policy = forms.CharField(widget=TranslationTextInput,)
|
|
the_future = forms.CharField(widget=TranslationTextInput,)
|
|
the_reason = forms.CharField(widget=TranslationTextInput,)
|
|
support_email = forms.CharField(widget=TranslationTextInput,)
|
|
|
|
class Meta:
|
|
model = Addon
|
|
fields = ('name', 'homepage', 'default_locale', 'support_email',
|
|
'support_url', 'description', 'summary',
|
|
'developer_comments', 'eula', 'privacy_policy', 'the_reason',
|
|
'the_future', 'view_source', 'prerelease', 'site_specific',
|
|
'get_satisfaction_company', 'get_satisfaction_product',)
|
|
|
|
exclude = ('status', )
|
|
|
|
def clean_name(self):
|
|
name = self.cleaned_data['name']
|
|
return clean_name(name)
|
|
|
|
def save(self):
|
|
desc = self.data.get('description')
|
|
if desc and desc != unicode(self.instance.description):
|
|
amo.log(amo.LOG.EDIT_DESCRIPTIONS, self.instance)
|
|
if self.changed_data:
|
|
amo.log(amo.LOG.EDIT_PROPERTIES, self.instance)
|
|
|
|
super(AddonForm, self).save()
|
|
|
|
|
|
class AbuseForm(happyforms.Form):
|
|
recaptcha = captcha.fields.ReCaptchaField(label='')
|
|
text = forms.CharField(required=True,
|
|
label='',
|
|
widget=forms.Textarea())
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.request = kwargs.pop('request')
|
|
super(AbuseForm, self).__init__(*args, **kwargs)
|
|
|
|
if (not self.request.user.is_anonymous() or
|
|
not settings.RECAPTCHA_PRIVATE_KEY):
|
|
del self.fields['recaptcha']
|
|
|
|
|
|
class NewPersonaForm(AddonFormBase):
|
|
name = forms.CharField(max_length=50)
|
|
category = forms.ModelChoiceField(queryset=Category.objects.all(),
|
|
widget=forms.widgets.RadioSelect)
|
|
summary = forms.CharField(widget=forms.Textarea(attrs={'rows': 4}),
|
|
max_length=250, required=False)
|
|
tags = forms.CharField(required=False)
|
|
|
|
license = forms.TypedChoiceField(choices=amo.PERSONA_LICENSES_IDS,
|
|
coerce=int, empty_value=None, widget=forms.HiddenInput,
|
|
error_messages={'required': _lazy(u'A license must be selected.')})
|
|
header = forms.FileField(required=False)
|
|
header_hash = forms.CharField(widget=forms.HiddenInput)
|
|
footer = forms.FileField(required=False)
|
|
footer_hash = forms.CharField(widget=forms.HiddenInput)
|
|
accentcolor = ColorField(required=False)
|
|
textcolor = ColorField(required=False)
|
|
|
|
class Meta:
|
|
model = Addon
|
|
fields = ('name', 'summary', 'tags')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(NewPersonaForm, self).__init__(*args, **kwargs)
|
|
cats = Category.objects.filter(application=amo.FIREFOX.id,
|
|
type=amo.ADDON_PERSONA, weight__gte=0)
|
|
cats = sorted(cats, key=lambda x: x.name)
|
|
self.fields['category'].choices = [(c.id, c.name) for c in cats]
|
|
self.fields['header'].widget.attrs['data-upload-url'] = reverse(
|
|
'devhub.personas.upload_persona', args=['persona_header'])
|
|
self.fields['footer'].widget.attrs['data-upload-url'] = reverse(
|
|
'devhub.personas.upload_persona', args=['persona_footer'])
|
|
|
|
def clean_name(self):
|
|
return clean_name(self.cleaned_data['name'])
|
|
|
|
def clean_tags(self):
|
|
return clean_tags(self.request, self.cleaned_data['tags'])
|
|
|
|
def save(self, commit=False):
|
|
from .tasks import create_persona_preview_image, save_persona_image
|
|
# We ignore `commit`, since we need it to be `False` so we can save
|
|
# the ManyToMany fields on our own.
|
|
addon = super(NewPersonaForm, self).save(commit=False)
|
|
addon.status = amo.STATUS_UNREVIEWED
|
|
addon.type = amo.ADDON_PERSONA
|
|
addon.save()
|
|
addon._current_version = Version.objects.create(addon=addon,
|
|
version='0')
|
|
addon.save()
|
|
amo.log(amo.LOG.CREATE_ADDON, addon)
|
|
log.debug('New persona %r uploaded' % addon)
|
|
|
|
data = self.cleaned_data
|
|
|
|
header = data['header_hash']
|
|
footer = data['footer_hash']
|
|
|
|
header = os.path.join(settings.TMP_PATH, 'persona_header', header)
|
|
footer = os.path.join(settings.TMP_PATH, 'persona_footer', footer)
|
|
dst = os.path.join(settings.PERSONAS_PATH, str(addon.id))
|
|
|
|
# Save header, footer, and preview images.
|
|
save_persona_image(src=header, dst=dst, img_basename='header.jpg')
|
|
save_persona_image(src=footer, dst=dst, img_basename='footer.jpg')
|
|
create_persona_preview_image(src=header, dst=dst,
|
|
img_basename='preview.jpg',
|
|
set_modified_on=[addon])
|
|
|
|
# Save user info.
|
|
user = self.request.amo_user
|
|
AddonUser(addon=addon, user=user).save()
|
|
|
|
p = Persona()
|
|
p.persona_id = 0
|
|
p.addon = addon
|
|
p.header = 'header'
|
|
p.footer = 'footer'
|
|
if data['accentcolor']:
|
|
p.accentcolor = data['accentcolor'].lstrip('#')
|
|
if data['textcolor']:
|
|
p.textcolor = data['textcolor'].lstrip('#')
|
|
p.license_id = data['license']
|
|
p.submit = datetime.now()
|
|
p.author = user.name
|
|
p.display_username = user.username
|
|
p.save()
|
|
|
|
# Save tags.
|
|
for t in data['tags']:
|
|
Tag(tag_text=t).save_tag(addon)
|
|
|
|
# Save categories.
|
|
tb_c = Category.objects.get(application=amo.THUNDERBIRD.id,
|
|
name__id=data['category'].name_id)
|
|
AddonCategory(addon=addon, category=data['category']).save()
|
|
AddonCategory(addon=addon, category=tb_c).save()
|
|
|
|
return addon
|