Start implementing static theme submission (#6628)

This commit is contained in:
Andrew Williamson 2017-10-10 16:17:31 +01:00 коммит произвёл GitHub
Родитель eabaee1287
Коммит 8d7a120467
16 изменённых файлов: 267 добавлений и 34 удалений

Просмотреть файл

@ -3234,7 +3234,7 @@ class TestStaticCategoryView(TestCase):
assert response.status_code == 200
data = json.loads(response.content)
assert len(data) == 98
assert len(data) == 113
# some basic checks to verify integrity
entry = data[0]
@ -3259,7 +3259,7 @@ class TestStaticCategoryView(TestCase):
assert response.status_code == 200
data = json.loads(response.content)
assert len(data) == 98
assert len(data) == 113
# some basic checks to verify integrity
entry = data[0]

Просмотреть файл

@ -51,6 +51,10 @@ urlpatterns = [
url('^complete-themes/(?:(?P<category_name>[^/]+)/)?format:rss$',
ThemeCategoriesRss(), name='browse.themes.rss'),
# This won't let you browse any themes but detail page needs the url.
url('^static-themes/(?P<category>[^/]+)?$', views.extensions,
name='browse.static-themes'),
url('^extensions/(?:(?P<category>[^/]+)/)?$', views.extensions,
name='browse.extensions'),

Просмотреть файл

@ -19,7 +19,7 @@ class FIREFOX(App):
pretty = _(u'Firefox')
browser = True
types = [ADDON_EXTENSION, ADDON_THEME, ADDON_DICT, ADDON_SEARCH,
ADDON_LPAPP, ADDON_PLUGIN, ADDON_PERSONA]
ADDON_LPAPP, ADDON_PLUGIN, ADDON_PERSONA, ADDON_STATICTHEME]
guid = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'
min_display_version = 3.0
# These versions were relabeled and should not be displayed.

Просмотреть файл

@ -120,11 +120,12 @@ ADDON_LPADDON = 6
ADDON_PLUGIN = 7
ADDON_API = 8 # not actually a type but used to identify extensions + themes
ADDON_PERSONA = 9
ADDON_STATICTHEME = 10
# Addon type groupings.
GROUP_TYPE_ADDON = [ADDON_EXTENSION, ADDON_DICT, ADDON_SEARCH, ADDON_LPAPP,
ADDON_LPADDON, ADDON_PLUGIN, ADDON_API]
GROUP_TYPE_THEME = [ADDON_THEME, ADDON_PERSONA]
GROUP_TYPE_THEME = [ADDON_THEME, ADDON_PERSONA, ADDON_STATICTHEME]
# Singular
ADDON_TYPE = {
@ -136,6 +137,7 @@ ADDON_TYPE = {
ADDON_LPADDON: _(u'Language Pack (Add-on)'),
ADDON_PLUGIN: _(u'Plugin'),
ADDON_PERSONA: _(u'Theme'),
ADDON_STATICTHEME: _(u'Theme (Static)'),
}
# Plural
@ -148,6 +150,7 @@ ADDON_TYPES = {
ADDON_LPADDON: _(u'Language Packs (Add-on)'),
ADDON_PLUGIN: _(u'Plugins'),
ADDON_PERSONA: _(u'Themes'),
ADDON_STATICTHEME: _(u'Themes (Static)'),
}
# Searchable Add-on Types
@ -159,12 +162,14 @@ ADDON_SEARCH_TYPES = [
ADDON_SEARCH,
ADDON_LPAPP,
ADDON_PERSONA,
ADDON_STATICTHEME,
]
# Icons
ADDON_ICONS = {
ADDON_ANY: 'default-addon.png',
ADDON_THEME: 'default-theme.png',
ADDON_STATICTHEME: 'default-theme.png',
}
# We use these slugs in browse page urls.
@ -175,6 +180,7 @@ ADDON_SLUGS = {
ADDON_LPAPP: 'language-tools',
ADDON_PERSONA: 'personas',
ADDON_SEARCH: 'search-tools',
ADDON_STATICTHEME: 'static-themes',
}
# These are used in the update API.
@ -187,6 +193,7 @@ ADDON_SLUGS_UPDATE = {
ADDON_LPADDON: 'extension',
ADDON_PERSONA: 'background-theme',
ADDON_PLUGIN: 'plugin',
ADDON_STATICTHEME: 'static-theme',
}
# A slug to ID map for the search API. Included are all ADDON_TYPES that are
@ -199,6 +206,7 @@ ADDON_SEARCH_SLUGS = {
'search': ADDON_SEARCH,
'language': ADDON_LPAPP,
'persona': ADDON_PERSONA,
'statictheme': ADDON_STATICTHEME,
}
ADDON_TYPE_CHOICES_API = {
@ -208,6 +216,7 @@ ADDON_TYPE_CHOICES_API = {
ADDON_SEARCH: 'search',
ADDON_LPAPP: 'language',
ADDON_PERSONA: 'persona',
ADDON_STATICTHEME: 'statictheme',
}
# Edit addon information
@ -401,6 +410,9 @@ DEFAULT_WEBEXT_MIN_VERSION_ANDROID = '48.0'
# The default version of Firefox that supports WebExtensions without an id
DEFAULT_WEBEXT_MIN_VERSION_NO_ID = '48.0'
# The version of Firefox that first supported static themes. Not Android yet.
DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX = '53.0'
E10S_UNKNOWN = 0
E10S_COMPATIBLE = 1
E10S_COMPATIBLE_WEBEXTENSION = 2

Просмотреть файл

@ -5,7 +5,7 @@ from olympia.constants.applications import (
ANDROID, FIREFOX, SEAMONKEY, THUNDERBIRD)
from olympia.constants.base import (
ADDON_DICT, ADDON_EXTENSION, ADDON_LPAPP, ADDON_PERSONA, ADDON_SEARCH,
ADDON_SLUGS, ADDON_THEME)
ADDON_SLUGS, ADDON_THEME, ADDON_STATICTHEME)
class StaticCategory(object):
@ -90,6 +90,23 @@ CATEGORIES = {
'retro': StaticCategory(id=31, name=_(u'Retro')),
'sports': StaticCategory(id=26, name=_(u'Sports'))
},
ADDON_STATICTHEME: {
'abstract': StaticCategory(id=300, name=_(u'Abstract')),
'causes': StaticCategory(id=320, name=_(u'Causes')),
'fashion': StaticCategory(id=324, name=_(u'Fashion')),
'film-and-tv': StaticCategory(id=326, name=_(u'Film and TV')),
'firefox': StaticCategory(id=308, name=_(u'Firefox')),
'foxkeh': StaticCategory(id=310, name=_(u'Foxkeh')),
'holiday': StaticCategory(id=328, name=_(u'Holiday')),
'music': StaticCategory(id=322, name=_(u'Music')),
'nature': StaticCategory(id=302, name=_(u'Nature')),
'other': StaticCategory(id=314, name=_(u'Other'), weight=333),
'scenery': StaticCategory(id=306, name=_(u'Scenery')),
'seasonal': StaticCategory(id=312, name=_(u'Seasonal')),
'solid': StaticCategory(id=318, name=_(u'Solid')),
'sports': StaticCategory(id=304, name=_(u'Sports')),
'websites': StaticCategory(id=316, name=_(u'Websites'))
},
ADDON_DICT: {
'general': StaticCategory(id=95, name=_(u'General'))
},

Просмотреть файл

@ -37,6 +37,7 @@
data-maxlength="{{ form.summary.field.max_length }}"></div>
</div>
</div>
{% if addon.type != amo.ADDON_STATICTHEME %}
<div class="addon-submission-field">
<label for="{{ form.is_experimental.auto_id }}">
{{ form.is_experimental }}
@ -61,6 +62,7 @@
"in order to work.")
}}">?</span>
</div>
{% endif %}
<div id="addon-categories-edit" class="addon-submission-field"
data-max-categories="{{ amo.MAX_CATEGORIES }}">
{{ cat_form.non_form_errors() }}
@ -107,8 +109,9 @@
</div>
</div>
{% endif %}
{% set values = form.data if form.is_bound else form.initial %}
<div class="optional-terms">
{% if addon.type != amo.ADDON_STATICTHEME %}
{% set values = form.data if form.is_bound else form.initial %}
<div class="optional-terms">
<div class="addon-submission-field">
{{ form.has_priv }}
{{ form.has_priv.label_tag() }}
@ -123,15 +126,16 @@
{{ form.privacy_policy }}
</div>
</div>
</div>
<div class="addon-submission-field">
<label for="{{ reviewer_form.approvalnotes.auto_id }}">
{{ _('Notes to Reviewer:') }}
</label>
<p>{{ _('Is there anything our reviewers should bear in mind when reviewing this add-on?') }}</p>
{{ reviewer_form.approvalnotes }}
<p>{{ _('These notes will only be visible to you and our reviewers.') }}</p>
</div>
</div>
<div class="addon-submission-field">
<label for="{{ reviewer_form.approvalnotes.auto_id }}">
{{ _('Notes to Reviewer:') }}
</label>
<p>{{ _('Is there anything our reviewers should bear in mind when reviewing this add-on?') }}</p>
{{ reviewer_form.approvalnotes }}
<p>{{ _('These notes will only be visible to you and our reviewers.') }}</p>
</div>
{% endif %}
<div class="submission-buttons addon-submission-field">
<button class="delete-button" type="sumbit"
formaction="{{ url('devhub.addons.cancel', addon.slug) }}">

Просмотреть файл

@ -28,9 +28,8 @@
{% endif %}
<p>
{% trans %}
Use the fields below to upload your add-on package and select any platform
restrictions. After upload, a series of automated validation tests will be run
on your file.
Use the fields below to upload your add-on package. After upload, a series
of automated validation tests will be run on your file.
{% endtrans %}
</p>
<section id="upload-file">
@ -61,13 +60,13 @@
{% endif %}
<div class="platform">
<div class="supported-platforms">
<div class="supported-platforms{% if not field.errors %} edit_initially_hidden{% endif %}">
<label>{{ _('Which platforms is this file compatible with?') }}</label>
{{ new_addon_form.errors.supported_platforms }}
{{ new_addon_form.supported_platforms.errors }}
{{ new_addon_form.supported_platforms }}
</div>
</div>
{% if submit_page == 'version' and addon.status == amo.STATUS_PUBLIC %}
<div class="beta-status hide">
<label>{{ new_addon_form.beta }} {{ _('Only publish this version to my beta channel.') }}

Двоичные данные
src/olympia/devhub/tests/addons/static_theme.zip Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -1595,6 +1595,21 @@ class TestUploadDetail(BaseUploadTest):
str(amo.PLATFORM_ANDROID.id)
])
@override_switch('allow-static-theme-uploads', active=True)
def test_static_theme_supports_all_desktop_platforms(self):
# Support was added in 53
self.create_appversion('firefox', '53.0')
# No Android support yet, but make sure.
self.create_appversion('android', '53.0')
self.create_appversion('android', '42.*')
self.create_appversion('android', '47.*')
self.create_appversion('android', '48.*')
self.create_appversion('android', '*')
self.check_excluded_platforms('static_theme.zip', [
str(amo.PLATFORM_ANDROID.id)])
def test_no_servererror_on_missing_version(self):
"""https://github.com/mozilla/addons-server/issues/3779

Просмотреть файл

@ -1,4 +1,5 @@
import json
import os
from datetime import datetime, timedelta
from django.conf import settings
@ -366,6 +367,41 @@ class TestAddonSubmitUpload(UploadTest, TestCase):
assert addon.current_version.source
assert Addon.objects.get(pk=addon.pk).admin_review
@override_switch('allow-static-theme-uploads', active=True)
def test_static_theme_submit_listed(self):
assert Addon.objects.count() == 0
path = os.path.join(
settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme.zip')
self.upload = self.get_upload(abspath=path)
response = self.post(
# Throw in platforms for the lols - they will be ignored
supported_platforms=[amo.PLATFORM_MAC, amo.PLATFORM_LINUX])
addon = Addon.objects.get()
self.assert3xx(
response, reverse('devhub.submit.details', args=[addon.slug]))
all_ = sorted([f.filename for f in addon.current_version.all_files])
assert all_ == [u'weta_fade-1.0.xpi'] # One XPI for all platforms.
assert addon.type == amo.ADDON_STATICTHEME
@override_switch('allow-static-theme-uploads', active=True)
def test_static_theme_submit_unlisted(self):
assert Addon.unfiltered.count() == 0
path = os.path.join(
settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme.zip')
self.upload = self.get_upload(abspath=path)
response = self.post(
# Throw in platforms for the lols - they will be ignored
supported_platforms=[amo.PLATFORM_MAC, amo.PLATFORM_LINUX],
listed=False)
addon = Addon.unfiltered.get()
latest_version = addon.find_latest_version(
channel=amo.RELEASE_CHANNEL_UNLISTED)
self.assert3xx(
response, reverse('devhub.submit.finish', args=[addon.slug]))
all_ = sorted([f.filename for f in latest_version.all_files])
assert all_ == [u'weta_fade-1.0.xpi'] # One XPI for all platforms.
assert addon.type == amo.ADDON_STATICTHEME
class TestAddonSubmitDetails(TestSubmitBase):
@ -1260,6 +1296,15 @@ class TestVersionSubmitDetails(TestSubmitBase):
assert self.addon.has_complete_metadata()
assert self.addon.status == amo.STATUS_PUBLIC
def test_submit_static_theme_should_redirect(self):
self.addon.update(type=amo.ADDON_STATICTHEME)
assert all(self.get_addon().get_required_metadata())
response = self.client.get(self.url)
# No extra details for subsequent theme uploads so just redirect.
self.assert3xx(
response, reverse('devhub.submit.version.finish',
args=[self.addon.slug, self.version.pk]))
class TestVersionSubmitDetailsFirstListed(TestAddonSubmitDetails):
""" Testing the case of a listed version being submitted on an add-on that

Просмотреть файл

@ -830,6 +830,7 @@ def json_upload_detail(request, upload, addon_slug=None):
# Does the version number look like it's beta?
result['beta'] = is_beta(pkg.get('version', ''))
result['addon_type'] = pkg.get('type', '')
result['platforms_to_exclude'] = plat_exclude
return result
@ -1482,9 +1483,15 @@ def submit_file(request, addon_id, addon, version_id):
def _submit_details(request, addon, version):
if version and version.channel == amo.RELEASE_CHANNEL_UNLISTED:
# Not a listed version ? Then nothing to do here.
return redirect('devhub.submit.version.finish', addon.slug, version.pk)
if version:
skip_details_step = (version.channel == amo.RELEASE_CHANNEL_UNLISTED or
version.is_beta or
(addon.type == amo.ADDON_STATICTHEME and
addon.has_complete_metadata()))
if skip_details_step:
# Nothing to do here.
return redirect(
'devhub.submit.version.finish', addon.slug, version.pk)
# Figure out the latest version early in order to pass the same instance to
# each form that needs it (otherwise they might overwrite each other).
latest_version = version or addon.find_latest_version(

Просмотреть файл

@ -294,7 +294,7 @@ class TestManifestJSONExtractor(TestCase):
utils.check_xpi_info(manifest)
assert self.parse({'theme': {}})['is_static_theme']
assert self.parse({'theme': {}})['type'] == amo.ADDON_STATICTHEME
def test_is_e10s_compatible(self):
data = self.parse({})
@ -431,6 +431,96 @@ class TestManifestJSONExtractor(TestCase):
assert exc.value.message.startswith('Cannot find min/max version.')
class TestManifestJSONExtractorStaticTheme(TestManifestJSONExtractor):
def parse(self, base_data):
base_data.update(theme={})
return super(
TestManifestJSONExtractorStaticTheme, self).parse(base_data)
def test_type(self):
assert self.parse({})['type'] == amo.ADDON_STATICTHEME
def create_webext_default_versions(self):
self.create_appversion('firefox',
amo.DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX)
return (super(TestManifestJSONExtractorStaticTheme, self)
.create_webext_default_versions())
def test_apps_use_default_versions_if_applications_is_omitted(self):
"""
Override this because static themes have a higher default version.
"""
self.create_webext_default_versions()
data = {}
apps = self.parse(data)['apps']
assert len(apps) == 1
assert apps[0].appdata == amo.FIREFOX
assert apps[0].min.version == (
amo.DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX)
assert apps[0].max.version == amo.DEFAULT_WEBEXT_MAX_VERSION
# Static themes don't support Android yet. So check they aren't there.
self.create_appversion(
'android', amo.DEFAULT_WEBEXT_MIN_VERSION_ANDROID)
self.create_appversion(
'android', amo.DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX)
self.create_appversion('android', amo.DEFAULT_WEBEXT_MAX_VERSION)
assert apps == self.parse(data)['apps'] # Same as before.
def test_apps_use_default_versions_if_none_provided(self):
"""Use the default min and max versions if none provided."""
self.create_webext_default_versions()
data = {'applications': {'gecko': {'id': 'some-id'}}}
apps = self.parse(data)['apps']
assert len(apps) == 1 # Only Firefox for now.
app = apps[0]
assert app.appdata == amo.FIREFOX
assert app.min.version == amo.DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX
assert app.max.version == amo.DEFAULT_WEBEXT_MAX_VERSION
def test_apps_use_provided_versions(self):
"""Use the min and max versions if provided."""
firefox_min_version = self.create_appversion('firefox', '54.0')
firefox_max_version = self.create_appversion('firefox', '54.*')
self.create_webext_default_versions()
data = {
'applications': {
'gecko': {
'strict_min_version': '>=54.0',
'strict_max_version': '=54.*',
'id': '@random'
}
}
}
apps = self.parse(data)['apps']
assert len(apps) == 1
app = apps[0]
assert app.appdata == amo.FIREFOX
assert app.min == firefox_min_version
assert app.max == firefox_max_version
def test_apps_contains_wrong_versions(self):
"""Use the min and max versions if provided."""
self.create_webext_default_versions()
data = {
'applications': {
'gecko': {
'strict_min_version': '54.0.0',
'id': '@random'
}
}
}
with pytest.raises(forms.ValidationError) as exc:
self.parse(data)['apps']
assert exc.value.message.startswith('Cannot find min/max version.')
def test_zip_folder_content():
extension_file = 'src/olympia/files/fixtures/files/extension.xpi'
temp_filename, temp_folder = None, None

Просмотреть файл

@ -375,6 +375,7 @@ class ManifestJSONExtractor(object):
def type(self):
return (
amo.ADDON_LPAPP if self.get('langpack_id')
else amo.ADDON_STATICTHEME if 'theme' in self.data
else amo.ADDON_EXTENSION
)
@ -391,6 +392,8 @@ class ManifestJSONExtractor(object):
apps = (
(amo.FIREFOX, amo.DEFAULT_WEBEXT_MIN_VERSION),
(amo.ANDROID, amo.DEFAULT_WEBEXT_MIN_VERSION_ANDROID)
) if self.type != amo.ADDON_STATICTHEME else (
(amo.FIREFOX, amo.DEFAULT_STATIC_THEME_MIN_VERSION_FIREFOX),
)
doesnt_support_no_id = (
@ -418,7 +421,8 @@ class ManifestJSONExtractor(object):
couldnt_find_version = False
for app, default_min_version in apps:
if self.guid is None and not self.strict_min_version:
strict_min_version = amo.DEFAULT_WEBEXT_MIN_VERSION_NO_ID
strict_min_version = max(amo.DEFAULT_WEBEXT_MIN_VERSION_NO_ID,
default_min_version)
else:
strict_min_version = (
self.strict_min_version or default_min_version)
@ -479,10 +483,13 @@ class ManifestJSONExtractor(object):
# webextensions don't.
'strict_compatibility': data['type'] == amo.ADDON_LPAPP,
'default_locale': self.get('default_locale'),
'permissions': self.get('permissions', []),
'content_scripts': self.get('content_scripts', []),
'is_static_theme': 'theme' in self.data
})
if self.type == amo.ADDON_EXTENSION:
# Only extensions have permissions and content scripts
data.update({
'permissions': self.get('permissions', []),
'content_scripts': self.get('content_scripts', []),
})
return data
@ -841,7 +848,7 @@ def check_xpi_info(xpi_info, addon=None):
ugettext('Version numbers should only contain letters, numbers, '
'and these punctuation characters: +*.-_.'))
if is_webextension and xpi_info.get('is_static_theme', False):
if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
if not waffle.switch_is_active('allow-static-theme-uploads'):
raise forms.ValidationError(ugettext(
'WebExtension theme uploads are currently not supported.'))

Просмотреть файл

@ -0,0 +1,26 @@
-- Insert new category. All fields need to match what's in the constants.
-- id has to be set so it matches our constants. I used +200 the persona categories.
-- addontype_id is 10 (Static Theme)
-- application_id is 1 (Firefox)
-- weight is 0 for normal categories and 333 for 'other' (used for sorting)
-- created and modified are NOW
-- count is 0 (no addons in it yet)
-- slug is the same as the persona slug
-- misc is False, apart from for 'other' (so that the category is used as "My add-on does not fit in any of the categories" in devhub)
INSERT INTO categories (id, addontype_id, application_id, weight, created, modified, count, slug, misc)
VALUES
(300, 10, 1, 0, NOW(), NOW(), 0, 'abstract', false),
(320, 10, 1, 0, NOW(), NOW(), 0, 'causes', false),
(324, 10, 1, 0, NOW(), NOW(), 0, 'fashion', false),
(326, 10, 1, 0, NOW(), NOW(), 0, 'film-and-tv', false),
(308, 10, 1, 0, NOW(), NOW(), 0, 'firefox', false),
(310, 10, 1, 0, NOW(), NOW(), 0, 'foxkeh', false),
(328, 10, 1, 0, NOW(), NOW(), 0, 'holiday', false),
(322, 10, 1, 0, NOW(), NOW(), 0, 'music', false),
(302, 10, 1, 0, NOW(), NOW(), 0, 'nature', false),
(314, 10, 1, 0, NOW(), NOW(), 333, 'other', true),
(306, 10, 1, 0, NOW(), NOW(), 0, 'scenery', false),
(312, 10, 1, 0, NOW(), NOW(), 0, 'seasonal', false),
(318, 10, 1, 0, NOW(), NOW(), 0, 'solid', false),
(304, 10, 1, 0, NOW(), NOW(), 0, 'sports', false),
(316, 10, 1, 0, NOW(), NOW(), 0, 'websites', false);

Просмотреть файл

@ -180,8 +180,8 @@ class Version(OnChangeMixin, ModelBase):
# queries completely.
version.compatible_apps = compatible_apps
if addon.type == amo.ADDON_SEARCH:
# Search extensions are always for all platforms.
if addon.type in [amo.ADDON_SEARCH, amo.ADDON_STATICTHEME]:
# Search extensions and static themes are always for all platforms.
platforms = [amo.PLATFORM_ALL.id]
else:
platforms = cls._make_safe_platform_files(platforms)

Просмотреть файл

@ -250,7 +250,6 @@
}};
upload_progress_inside.animate({'width': '100%'}, animateArgs);
$('.binary-source').show();
});
$upload_field.on("upload_onreadystatechange", function(e, file, xhr, aborted) {
@ -395,6 +394,14 @@
});
}, 1000);
} else {
if (results.addon_type==10) {
// No source or platform selection for static themes.
$('.binary-source').hide();
$('.supported-platforms').hide();
} else {
$('.binary-source').show();
$('.supported-platforms').show();
}
var errors = getErrors(results),
v = results.validation,
timeout = checkTimeout(v);