serve previous theme background image as json via devhub endpoint (#9847)
This commit is contained in:
Родитель
5fcdbd7218
Коммит
c09a5fb62f
|
@ -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'] == {
|
||||
assert result == {
|
||||
"colors": {
|
||||
"accentcolor": "#adb09f",
|
||||
"textcolor": "#000"
|
||||
},
|
||||
"images": {
|
||||
"headerURL": "weta.png"
|
||||
}
|
||||
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')
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче