publize 128px icon as a standard icon size; only generate 32, 64, 128 (#10092)
|
@ -170,7 +170,7 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
:>json boolean has_privacy_policy: The add-on has a Privacy Policy (See :ref:`add-on EULA and privacy policy <addon-eula-policy>`).
|
||||
:>json string|object|null homepage: The add-on homepage (See :ref:`translated fields <api-overview-translations>`).
|
||||
:>json string icon_url: The URL to icon for the add-on (including a cachebusting query string).
|
||||
:>json object icons: An object holding the URLs to an add-ons icon including a cachebusting query string as values and their size as properties. Currently exposes 32 and 64 pixels wide icons.
|
||||
:>json object icons: An object holding the URLs to an add-ons icon including a cachebusting query string as values and their size as properties. Currently exposes 32, 64, 128 pixels wide icons.
|
||||
:>json boolean is_disabled: Whether the add-on is disabled or not.
|
||||
:>json boolean is_experimental: Whether the add-on has been marked by the developer as experimental or not.
|
||||
:>json boolean is_featured: The add-on appears in a featured collection.
|
||||
|
|
|
@ -1125,10 +1125,6 @@ class Addon(OnChangeMixin, ModelBase):
|
|||
def show_adu(self):
|
||||
return self.type != amo.ADDON_SEARCH
|
||||
|
||||
@property
|
||||
def icon_url(self):
|
||||
return self.get_icon_url(32)
|
||||
|
||||
def authors_other_addons(self, app=None):
|
||||
"""
|
||||
Return other addons by the author(s) of this addon,
|
||||
|
|
|
@ -441,14 +441,13 @@ class AddonSerializer(serializers.ModelSerializer):
|
|||
return absolutify(obj.get_icon_url(64))
|
||||
|
||||
def get_icons(self, obj):
|
||||
# We're using only 32 and 64 for compatibility reasons with the
|
||||
# old search API. https://github.com/mozilla/addons-server/issues/7514
|
||||
if self.is_broken_persona(obj):
|
||||
get_icon = obj.get_default_icon_url
|
||||
else:
|
||||
get_icon = obj.get_icon_url
|
||||
|
||||
return {str(size): absolutify(get_icon(size)) for size in (32, 64)}
|
||||
return {str(size): absolutify(get_icon(size))
|
||||
for size in amo.ADDON_ICON_SIZES}
|
||||
|
||||
def get_ratings(self, obj):
|
||||
return {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="install-shell">
|
||||
<div class="install {{ button.install_class|join(' ') }}"
|
||||
data-addon="{{ addon.id }}"
|
||||
data-icon="{{ addon.icon_url }}"
|
||||
data-icon="{{ addon.get_icon_url(32) }}"
|
||||
data-developers="{{ addon.meet_the_dev_url() }}"
|
||||
data-versions="{{ url('addons.versions', addon.id) }}"
|
||||
data-name="{{ addon.name }}"
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<div class="icon">
|
||||
<a href="{{ details_link }}">
|
||||
{% if lazyload %}
|
||||
<img src="{{ addon.icon_url }}" alt="{{ _('Icon for {0}')|format_html(addon.name) }}">
|
||||
<img src="{{ addon.get_icon_url(32) }}" alt="{{ _('Icon for {0}')|format_html(addon.name) }}">
|
||||
{% else %}
|
||||
<img data-defer-src="{{ addon.icon_url }}" alt="{{ _('Icon for {0}')|format_html(addon.name) }}"
|
||||
<img data-defer-src="{{ addon.get_icon_url(32) }}" alt="{{ _('Icon for {0}')|format_html(addon.name) }}"
|
||||
src="{{ static('img/addon-icons/default-32.png') }}">
|
||||
{% endif %}
|
||||
</a>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{% set dl_url = dl_url|urlparams(src=dl_src) %}
|
||||
{% endif %}
|
||||
<a href="{{ dl_url }}">
|
||||
<img src="{{ addon.icon_url }}" alt="">{{ addon.name }}</a>
|
||||
<img src="{{ addon.get_icon_url(32) }}" alt="">{{ addon.name }}</a>
|
||||
{{ addon_flags(addon, addon.current_version) }}
|
||||
{% if addon.is_featured(request.APP, request.LANG) %}
|
||||
<span class="featured">{{ _('Featured') }}</span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{# these are custom attributes on <div class="install ..."> found in src/olympia/addons/templates/addons/impala/button.html #}
|
||||
data-addon="{{ addon.id }}"
|
||||
data-icon="{{ addon.icon_url }}"
|
||||
data-icon="{{ addon.get_icon_url(32) }}"
|
||||
data-developers="{{ addon.meet_the_dev_url() }}"
|
||||
data-versions="{{ url('addons.versions', addon.id) }}"
|
||||
data-name="{{ addon.name }}"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% endif %}
|
||||
|
||||
<a href="{{ addon.get_url_path() + collection_path }}">
|
||||
<img class="icon" width="32" height="32" src="{{ addon.icon_url }}" alt="">
|
||||
<img class="icon" width="32" height="32" src="{{ addon.get_icon_url(32) }}" alt="">
|
||||
{{ addon.name }}</a>
|
||||
<span>
|
||||
{{ addon_users_list(addon) }}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<hgroup>
|
||||
<h2 class="addon"{{ addon.name|locale_html }}>
|
||||
<img src="{{ addon.icon_url }}" class="icon" alt="" />
|
||||
<img src="{{ addon.get_icon_url(32) }}" class="icon" alt="" />
|
||||
<span>
|
||||
{{ addon.name }}
|
||||
</span>
|
||||
|
|
|
@ -301,7 +301,7 @@ class TestButtonHtml(ButtonTest):
|
|||
def test_basics(self):
|
||||
a = self.addon
|
||||
a.id = '12345'
|
||||
a.icon_url = 'icon url'
|
||||
a.get_icon_url.return_value = 'icon url'
|
||||
a.meet_the_dev_url.return_value = 'meet.dev'
|
||||
a.name = 'addon name'
|
||||
self.file.hash = 'file hash'
|
||||
|
|
|
@ -714,25 +714,27 @@ class TestAddonModels(TestCase):
|
|||
4. Test for default non-THEME icon.
|
||||
"""
|
||||
addon = Addon.objects.get(pk=3615)
|
||||
assert addon.icon_url.endswith('/3/3615-32.png?modified=1275037317')
|
||||
assert addon.get_icon_url(32).endswith(
|
||||
'/3/3615-32.png?modified=1275037317')
|
||||
|
||||
addon.icon_hash = 'somehash'
|
||||
assert addon.icon_url.endswith('/3/3615-32.png?modified=somehash')
|
||||
assert addon.get_icon_url(32).endswith(
|
||||
'/3/3615-32.png?modified=somehash')
|
||||
|
||||
addon = Addon.objects.get(pk=6704)
|
||||
addon.icon_type = None
|
||||
assert addon.icon_url.endswith('/icons/default-theme.png'), (
|
||||
'No match for %s' % addon.icon_url)
|
||||
assert addon.get_icon_url(32).endswith('/icons/default-theme.png'), (
|
||||
'No match for %s' % addon.get_icon_url(32))
|
||||
|
||||
addon = Addon.objects.get(pk=3615)
|
||||
addon.icon_type = None
|
||||
assert addon.icon_url.endswith('icons/default-32.png')
|
||||
assert addon.get_icon_url(32).endswith('icons/default-32.png')
|
||||
|
||||
def test_icon_url_default(self):
|
||||
a = Addon.objects.get(pk=3615)
|
||||
a.update(icon_type='')
|
||||
default = 'icons/default-32.png'
|
||||
assert a.icon_url.endswith(default)
|
||||
assert a.get_icon_url(32).endswith(default)
|
||||
assert a.get_icon_url(32).endswith(default)
|
||||
assert a.get_icon_url(32, use_default=True).endswith(default)
|
||||
assert a.get_icon_url(32, use_default=False) is None
|
||||
|
|
|
@ -229,7 +229,8 @@ class AddonSerializerOutputTestMixin(object):
|
|||
assert result['icon_url'] == absolutify(self.addon.get_icon_url(64))
|
||||
assert result['icons'] == {
|
||||
'32': absolutify(self.addon.get_icon_url(32)),
|
||||
'64': absolutify(self.addon.get_icon_url(64))
|
||||
'64': absolutify(self.addon.get_icon_url(64)),
|
||||
'128': absolutify(self.addon.get_icon_url(128)),
|
||||
}
|
||||
assert result['is_disabled'] == self.addon.is_disabled
|
||||
assert result['is_experimental'] == self.addon.is_experimental is False
|
||||
|
@ -387,7 +388,8 @@ class AddonSerializerOutputTestMixin(object):
|
|||
assert result['icon_url'] == absolutify(self.addon.get_icon_url(64))
|
||||
assert result['icons'] == {
|
||||
'32': absolutify(self.addon.get_icon_url(32)),
|
||||
'64': absolutify(self.addon.get_icon_url(64))
|
||||
'64': absolutify(self.addon.get_icon_url(64)),
|
||||
'128': absolutify(self.addon.get_icon_url(128)),
|
||||
}
|
||||
|
||||
def test_no_current_version(self):
|
||||
|
@ -542,7 +544,8 @@ class AddonSerializerOutputTestMixin(object):
|
|||
'http://testserver/static/img/addon-icons/default-64.png')
|
||||
assert result['icons'] == {
|
||||
'32': 'http://testserver/static/img/addon-icons/default-32.png',
|
||||
'64': 'http://testserver/static/img/addon-icons/default-64.png'
|
||||
'64': 'http://testserver/static/img/addon-icons/default-64.png',
|
||||
'128': 'http://testserver/static/img/addon-icons/default-128.png',
|
||||
}
|
||||
|
||||
def test_webextension(self):
|
||||
|
|
После Ширина: | Высота: | Размер: 12 KiB |
|
@ -31,7 +31,7 @@
|
|||
<tr>
|
||||
<td class="item">
|
||||
<input type="hidden" value="{{ addon.id }}" name="addon">
|
||||
<img src="{{ addon.icon_url }}" alt="">
|
||||
<img src="{{ addon.get_icon_url(32) }}" alt="">
|
||||
<h3>{{ addon.name }}</h3>
|
||||
<p class="comments{% if comments[addon.id] %} hascomment{% endif %}">
|
||||
<textarea name="addon_comment">{{ comments[addon.id] }}</textarea>
|
||||
|
|
|
@ -228,7 +228,7 @@ VALID_CONTRIBUTION_DOMAINS = (
|
|||
)
|
||||
|
||||
# Icon upload sizes
|
||||
ADDON_ICON_SIZES = [32, 48, 64, 128, 256, 512]
|
||||
ADDON_ICON_SIZES = [32, 64, 128]
|
||||
|
||||
_size_tuple = namedtuple('SizeTuple', 'width height')
|
||||
# Preview upload sizes - see mozilla/addons-server#9487 for background.
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{% if pager.object_list %}
|
||||
{% for item in pager.object_list %}
|
||||
<div class="item">
|
||||
<img class="icon" width="32" height="32" src="{{ addon.icon_url }}" alt="">
|
||||
<img class="icon" width="32" height="32" src="{{ addon.get_icon_url(32) if addon else '' }}" alt="">
|
||||
<p>
|
||||
<span class="action {{ log_action_class(item.action) }}"></span>
|
||||
{{ item }}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
{% if editable %}
|
||||
<div id="icon_preview">
|
||||
<div class="icon_preview" id="icon_preview_32">
|
||||
<img src="{{ addon.icon_url }}" alt="">
|
||||
<img src="{{ addon.get_icon_url(32) }}" alt="">
|
||||
</div>
|
||||
<div class="edit-media-details">
|
||||
{# L10n: The size of the icon #}
|
||||
|
@ -48,6 +48,15 @@
|
|||
{{ _('64x64px') }}
|
||||
{{ tip(None, _("Used in add-on detail pages.")) }}
|
||||
</div>
|
||||
|
||||
<div class="icon_preview" id="icon_preview_128">
|
||||
<img src="{{ addon.get_icon_url(128) }}" alt="">
|
||||
</div>
|
||||
<div class="edit-media-details">
|
||||
{# L10n: The size of the icon #}
|
||||
{{ _('128x128px') }}
|
||||
{{ tip(None, _("Will be the standard in future add-on detail pages.")) }}
|
||||
</div>
|
||||
</div>
|
||||
<ul id="icons_default">
|
||||
{{ form.icon_type }}
|
||||
|
@ -62,17 +71,20 @@
|
|||
{{ form.icon_upload_hash }}
|
||||
</div>
|
||||
<div class="edit-media-details">
|
||||
{% trans %}
|
||||
PNG and JPG supported. Icons resized to 64x64 pixels if larger.
|
||||
{% endtrans %}
|
||||
{{ _('PNG and JPG supported.') }}
|
||||
{% if waffle.switch('content-optimization') %}
|
||||
{{ _('Icons must be square and a minimum size of 128x128 pixels.') }}
|
||||
{% endif %}
|
||||
{{ _('Icons will be resized to 128x128 pixels if larger.') }}
|
||||
</div>
|
||||
<ul class="errorlist">
|
||||
<li id="edit-icon-error"></li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<div id="icon_preview_readonly">
|
||||
<img src="{{ addon.get_icon_url(128) }}" alt="">
|
||||
<img src="{{ addon.get_icon_url(64) }}" alt="">
|
||||
<img src="{{ addon.icon_url }}" alt="">
|
||||
<img src="{{ addon.get_icon_url(32) }}" alt="">
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<div class="island swagger theme-info c">
|
||||
<div class="addon-name-and-icon c">
|
||||
<img class="addon-icon" src="{{ addon.icon_url }}" alt="">
|
||||
<img class="addon-icon" src="{{ addon.get_icon_url(32) }}" alt="">
|
||||
<h2 class="addon-name">{{ addon.name }}</h2>
|
||||
<h3 class="author">
|
||||
{{ _('by {0}')|format_html(users_list(addon.listed_authors)|safe or persona.display_username|safe) }}
|
||||
|
|
|
@ -1048,6 +1048,37 @@ class TestEditMedia(BaseTestEdit):
|
|||
self.check_image_animated(self.preview_upload,
|
||||
'Images cannot be animated.')
|
||||
|
||||
@override_switch('content-optimization', active=True)
|
||||
def test_icon_dimensions_and_ratio(self):
|
||||
size_msg = 'Icon must be at least 128 pixels wide and tall.'
|
||||
ratio_msg = 'Icon must be square (same width and height).'
|
||||
|
||||
# mozilla-snall.png is too small and not square
|
||||
response = self.client.post(
|
||||
self.icon_upload,
|
||||
{'upload_image': open(get_image_path('mozilla-small.png'), 'rb')})
|
||||
assert json.loads(response.content)['errors'] == [size_msg, ratio_msg]
|
||||
|
||||
# icon64.png is the right ratio, but only 64x64
|
||||
response = self.client.post(
|
||||
self.icon_upload,
|
||||
{'upload_image': open(
|
||||
get_image_path('icon64.png'), 'rb')})
|
||||
assert json.loads(response.content)['errors'] == [size_msg]
|
||||
|
||||
# mozilla.png is big enough but still not square
|
||||
response = self.client.post(
|
||||
self.icon_upload,
|
||||
{'upload_image': open(get_image_path('mozilla.png'), 'rb')})
|
||||
assert json.loads(response.content)['errors'] == [ratio_msg]
|
||||
|
||||
# and mozilla-sq is the right ratio and big enough
|
||||
response = self.client.post(
|
||||
self.icon_upload,
|
||||
{'upload_image': open(get_image_path('mozilla-sq.png'), 'rb')})
|
||||
assert json.loads(response.content)['errors'] == []
|
||||
assert json.loads(response.content)['upload_hash']
|
||||
|
||||
def preview_add(self, amount=1, image_name='preview_4x3.jpg'):
|
||||
src_image = open(get_image_path(image_name), 'rb')
|
||||
|
||||
|
|
|
@ -955,6 +955,18 @@ def ajax_upload_image(request, upload_type, addon_id=None):
|
|||
errors.append(
|
||||
ugettext('Image dimensions must be in the ratio 4:3.'))
|
||||
|
||||
if image_check.is_image() and content_waffle and is_icon:
|
||||
standard_size = amo.ADDON_ICON_SIZES[-1]
|
||||
icon_size = image_check.size
|
||||
if icon_size[0] < standard_size or icon_size[1] < standard_size:
|
||||
# L10n: {0} is an image width/height (in pixels).
|
||||
errors.append(
|
||||
ugettext(u'Icon must be at least {0} pixels wide and '
|
||||
u'tall.').format(standard_size))
|
||||
if icon_size[0] != icon_size[1]:
|
||||
errors.append(
|
||||
ugettext(u'Icon must be square (same width and height).'))
|
||||
|
||||
if errors and is_preview and os.path.exists(loc):
|
||||
# Delete the temporary preview file in case of error.
|
||||
os.unlink(loc)
|
||||
|
|
|
@ -43,7 +43,7 @@ def addon_to_dict(addon, disco=False, src='api'):
|
|||
'summary': (
|
||||
strip_tags(unicode(addon.summary)) if addon.summary else None),
|
||||
'description': strip_tags(unicode(addon.description)),
|
||||
'icon': addon.icon_url,
|
||||
'icon': addon.get_icon_url(32),
|
||||
'learnmore': learnmore,
|
||||
'reviews': url(addon.ratings_url),
|
||||
'total_dls': addon.total_downloads,
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<h3 class="htruncate">{{ addon.name }}</h3>
|
||||
{{ persona_preview(addon.persona, linked=False) }}
|
||||
{% else %}
|
||||
<img src="{{ addon.icon_url }}" width="32" height="32" alt="">
|
||||
<img src="{{ addon.get_icon_url(32) }}" width="32" height="32" alt="">
|
||||
<h3 class="htruncate">{{ addon.name }}</h3>
|
||||
<p class="desc vtruncate">{{ descriptions.get(addon.id, addon.summary|clean(true)) }}</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<hgroup>
|
||||
<h2 class="addon"{{ addon.name|locale_html }}>
|
||||
<img src="{{ addon.icon_url }}" class="icon" alt="" />
|
||||
<img src="{{ addon.get_icon_url(32) }}" class="icon" alt="" />
|
||||
{% if content_review_only %}
|
||||
{# L10n: "Content Review [add-on name]" #}
|
||||
<span>
|
||||
|
|
|
@ -506,7 +506,7 @@
|
|||
#icon_preview {
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
width: 76px;
|
||||
width: 140px;
|
||||
|
||||
.icon_preview {
|
||||
border: 1px solid #6A89AC;
|
||||
|
|
После Ширина: | Высота: | Размер: 16 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 18 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
После Ширина: | Высота: | Размер: 15 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 22 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
После Ширина: | Высота: | Размер: 17 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 17 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
После Ширина: | Высота: | Размер: 13 KiB |
После Ширина: | Высота: | Размер: 17 KiB |
После Ширина: | Высота: | Размер: 16 KiB |