serve previous theme background image as json via devhub endpoint (#9847)

This commit is contained in:
Andrew Williamson 2018-10-30 16:56:33 +00:00 коммит произвёл GitHub
Родитель 5fcdbd7218
Коммит c09a5fb62f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 177 добавлений и 27 удалений

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

@ -431,14 +431,14 @@ def test_extract_theme_properties():
copy_stored_file(zip_file, addon.current_version.all_files[0].file_path)
result = utils.extract_theme_properties(
addon, addon.current_version.channel)
assert result['colors'] == {
"accentcolor": "#adb09f",
"textcolor": "#000"
}
assert result['images'] == {
"headerURL": '%s%s//%s/%s/%s' % (
settings.MEDIA_URL, 'addons', text_type(addon.id),
text_type(addon.current_version.id), 'weta.png')
assert result == {
"colors": {
"accentcolor": "#adb09f",
"textcolor": "#000"
},
"images": {
"headerURL": "weta.png"
}
}

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

@ -24,6 +24,7 @@ from olympia import amo, core
from olympia.activity.models import ActivityLog
from olympia.addons.models import (
Addon, AddonCategory, AddonFeatureCompatibility, AddonUser)
from olympia.amo.storage_utils import copy_stored_file
from olympia.amo.templatetags.jinja_helpers import (
format_date, url as url_reverse)
from olympia.amo.tests import (
@ -1755,3 +1756,39 @@ def test_get_next_version_number():
version_factory(addon=addon, version='36.0').delete()
assert addon.current_version.version == '34.45.0a1pre'
assert get_next_version_number(addon) == '37.0'
class TestThemeBackgroundImage(TestCase):
def setUp(self):
user = user_factory(email='regular@mozilla.com')
assert self.client.login(email='regular@mozilla.com')
self.addon = addon_factory(users=[user])
self.url = reverse(
'devhub.submit.version.previous_background',
args=[self.addon.slug, 'listed'])
def test_wrong_user(self):
user_factory(email='irregular@mozilla.com')
assert self.client.login(email='irregular@mozilla.com')
response = self.client.post(self.url, follow=True)
assert response.status_code == 403
def test_no_header_image(self):
response = self.client.post(self.url, follow=True)
assert response.status_code == 200
data = json.loads(response.content)
assert data == {}
def test_header_image(self):
destination = self.addon.current_version.all_files[0].current_file_path
zip_file = os.path.join(
settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme.zip')
copy_stored_file(zip_file, destination)
response = self.client.post(self.url, follow=True)
assert response.status_code == 200
data = json.loads(response.content)
assert data
assert len(data.items()) == 1
assert 'weta.png' in data
assert len(data['weta.png']) == 168596 # base64-encoded size

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

@ -83,6 +83,9 @@ detail_patterns = [
url('^versions/submit/wizard-(?P<channel>listed|unlisted)$',
views.submit_version_theme_wizard,
name='devhub.submit.version.wizard'),
url('^versions/submit/wizard-(?P<channel>listed|unlisted)/background$',
views.theme_background_image,
name='devhub.submit.version.previous_background'),
url('^file/(?P<file_id>[^/]+)/validation$', views.file_validation,
name='devhub.file_validation'),

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

@ -13,7 +13,6 @@ import olympia.core.logger
from olympia import amo, core
from olympia.addons.models import Addon
from olympia.amo.templatetags.jinja_helpers import user_media_url
from olympia.amo.urlresolvers import linkify_escape
from olympia.files.models import File, FileUpload
from olympia.files.utils import parse_addon, parse_xpi
@ -376,16 +375,6 @@ def extract_theme_properties(addon, channel):
theme_props['colors'] = dict(
process_color_value(prop, color)
for prop, color in theme_props.get('colors', {}).items())
# replace headerURL with path to existing background
if 'images' in theme_props:
if 'theme_frame' in theme_props['images']:
header_url = theme_props['images'].pop('theme_frame')
if 'headerURL' in theme_props['images']:
header_url = theme_props['images'].pop('headerURL')
if header_url:
theme_props['images']['headerURL'] = '/'.join((
user_media_url('addons'), text_type(addon.id),
text_type(version.id), header_url))
return theme_props

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

@ -1,7 +1,7 @@
import datetime
import os
import time
from base64 import b64encode
from uuid import UUID, uuid4
from django import forms as django_forms, http
@ -46,7 +46,7 @@ from olympia.devhub.utils import (
get_addon_akismet_reports, wizard_unsupported_properties,
extract_theme_properties)
from olympia.files.models import File, FileUpload, FileValidation
from olympia.files.utils import parse_addon
from olympia.files.utils import get_background_images, parse_addon
from olympia.lib.crypto.packaged import sign_file
from olympia.reviewers.forms import PublicWhiteboardForm
from olympia.reviewers.models import Whiteboard
@ -1783,3 +1783,18 @@ def send_key_revoked_email(to_email, key):
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[to_email],
)
@dev_required
@json_view
def theme_background_image(request, addon_id, addon, channel):
channel_id = amo.CHANNEL_CHOICES_LOOKUP[channel]
version = addon.find_latest_version(channel_id)
if not version or not version.all_files:
return {}
backgrounds = get_background_images(
version.all_files[0], theme_data=None, header_only=True)
backgrounds = {
name: b64encode(background)
for name, background in backgrounds.items()}
return backgrounds

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

@ -1020,3 +1020,60 @@ class TestExtractHeaderImg(TestCase):
assert default_storage.size(additional_file_1) == 42
assert default_storage.exists(additional_file_2)
assert default_storage.size(additional_file_2) == 93371
class TestGetBackgroundImages(TestCase):
file_obj = os.path.join(
settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme.zip')
def test_get_background_images(self):
data = {'images': {'headerURL': 'weta.png'}}
images = utils.get_background_images(self.file_obj, data)
assert 'weta.png' in images
assert len(images.items()) == 1
assert len(images['weta.png']) == 126447
def test_get_background_images_no_theme_data_provided(self):
images = utils.get_background_images(self.file_obj, theme_data=None)
assert 'weta.png' in images
assert len(images.items()) == 1
assert len(images['weta.png']) == 126447
def test_get_background_images_missing(self):
data = {'images': {'headerURL': 'missing_file.png'}}
images = utils.get_background_images(self.file_obj, data)
assert not images
def test_get_background_images_not_image(self):
self.file_obj = os.path.join(
settings.ROOT,
'src/olympia/devhub/tests/addons/static_theme_non_image.zip')
data = {'images': {'headerURL': 'not_an_image.js'}}
images = utils.get_background_images(self.file_obj, data)
assert not images
def test_get_background_images_with_additional_imgs(self):
self.file_obj = os.path.join(
settings.ROOT,
'src/olympia/devhub/tests/addons/static_theme_tiled.zip')
data = {'images': {
'headerURL': 'empty.png',
'additional_backgrounds': [
'transparent.gif', 'missing_&_ignored.png',
'weta_for_tiling.png']
}}
images = utils.get_background_images(self.file_obj, data)
assert len(images.items()) == 3
assert len(images['empty.png']) == 332
assert len(images['transparent.gif']) == 42
assert len(images['weta_for_tiling.png']) == 93371
# And again but only with the header image
images = utils.get_background_images(
self.file_obj, data, header_only=True)
assert len(images.items()) == 1
assert len(images['empty.png']) == 332

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

@ -530,6 +530,9 @@ class ManifestJSONExtractor(object):
if self.certinfo is not None:
data.update(self.certinfo.parse())
if self.type == amo.ADDON_STATICTHEME:
data['theme'] = self.get('theme', {})
if not minimal:
data.update({
'is_restart_required': False,
@ -546,8 +549,6 @@ class ManifestJSONExtractor(object):
'permissions': self.get('permissions', []),
'content_scripts': self.get('content_scripts', []),
})
elif self.type == amo.ADDON_STATICTHEME:
data['theme'] = self.get('theme', {})
elif self.type == amo.ADDON_DICT:
data['target_locale'] = self.target_locale()
return data
@ -1266,6 +1267,43 @@ def extract_header_img(file_obj, theme_data, dest_path):
log.debug(ioerror)
def get_background_images(file_obj, theme_data, header_only=False):
"""Extract static theme header image from `file_obj` and return in dict."""
xpi = get_filepath(file_obj)
if not theme_data:
# we might already have theme_data, but otherwise get it from the xpi.
try:
parsed_data = parse_xpi(xpi, minimal=True)
theme_data = parsed_data.get('theme', {})
except forms.ValidationError:
# If we can't parse the existing manifest safely return.
return {}
images_dict = theme_data.get('images', {})
# Get the reference in the manifest. theme_frame is the Chrome variant.
header_url = images_dict.get(
'headerURL', images_dict.get('theme_frame'))
# And any additional backgrounds too.
additional_urls = (
images_dict.get('additional_backgrounds', []) if not header_only
else [])
image_urls = [header_url] + additional_urls
images = {}
try:
with zipfile.ZipFile(xpi, 'r') as source:
for url in image_urls:
_, file_ext = os.path.splitext(text_type(url).lower())
if file_ext not in amo.THEME_BACKGROUND_EXTS:
# Just extract image files.
continue
try:
images[url] = source.read(url)
except KeyError:
pass
except IOError as ioerror:
log.debug(ioerror)
return images
@contextlib.contextmanager
def atomic_lock(lock_dir, lock_name, lifetime=60):
"""A atomic, NFS safe implementation of a file lock.

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

@ -53,17 +53,28 @@ $(document).ready(function() {
$svg_img.attr('preserveAspectRatio', 'xMaxYMin '+ meetOrSlice);
});
function b64toBlob(data) {
var b64str = atob(data);
var counter = b64str.length;
var u8arr = new Uint8Array(counter);
while(counter--){
u8arr[counter] = b64str.charCodeAt(counter);
}
return new Blob([u8arr]);
}
$wizard.find('#theme-header').each(function(index, element) {
var img_src = $(element).data('existing-header');
// If we already have a preview from a selected file don't overwrite it.
if (getFile() || !img_src) return;
var xhr = new XMLHttpRequest();
xhr.open("GET", img_src);
xhr.responseType = "blob";
xhr.open("GET", window.location.href + "/background");
xhr.responseType = "json";
// load the image as a blob so we can treat it as a File
xhr.onload = function() {
preLoadBlob = xhr.response;
preLoadBlob.name = img_src.split('/').slice(-1)[0];
jsonResponse = xhr.response;
preLoadBlob = b64toBlob(jsonResponse[img_src]);
preLoadBlob.name = img_src;
$wizard.find('input[type="file"]').trigger('change');
};
xhr.send();