add-on update compatibility modal (bug 592544)
This commit is contained in:
Родитель
946ded1b19
Коммит
75fc0864dc
|
@ -502,6 +502,10 @@ class Addon(amo.models.ModelBase):
|
|||
else:
|
||||
return {}
|
||||
|
||||
def accepts_compatible_apps(self):
|
||||
"""True if this add-on lists compatible apps."""
|
||||
return self.type not in amo.NO_COMPAT
|
||||
|
||||
def incompatible_latest_apps(self):
|
||||
"""Returns a list of applications with which this add-on is
|
||||
incompatible (based on the latest version).
|
||||
|
|
|
@ -251,7 +251,7 @@ class CompatForm(happyforms.ModelForm):
|
|||
min = self.cleaned_data.get('min')
|
||||
max = self.cleaned_data.get('max')
|
||||
if not (min and max and min.version_int < max.version_int):
|
||||
raise forms.ValidationError(_('Invalid version range'))
|
||||
raise forms.ValidationError(_('Invalid version range.'))
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
|
@ -271,8 +271,8 @@ class BaseCompatFormSet(BaseModelFormSet):
|
|||
def clean(self):
|
||||
if any(self.errors):
|
||||
return
|
||||
apps = [f for f in self.forms
|
||||
if not f.cleaned_data.get('DELETE', False)]
|
||||
apps = filter(None, [f.cleaned_data for f in self.forms
|
||||
if not f.cleaned_data.get('DELETE', False)])
|
||||
if not apps:
|
||||
raise forms.ValidationError(
|
||||
_('Need at least one compatible application.'))
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{% if addon.accepts_compatible_apps() and addon.current_version %}
|
||||
{% set apps = addon.incompatible_latest_apps() %}
|
||||
{% set update_url = url('devhub.ajax.compat.update', addon.id, addon.current_version.id) %}
|
||||
<a class="tooltip {% if apps %}compat-error{% else %}compat-update{% endif %}"
|
||||
data-updateurl="{{ update_url }}" href="#"
|
||||
title="{{ _('View and update application compatibility ranges.') }}">
|
||||
{{ _('Compatibility') }}</a>
|
||||
{% if apps %}
|
||||
{% set app = apps[0] %}
|
||||
<div class="compat-error-popup popup error hidden">
|
||||
<div class="{{ app.short }} app">
|
||||
<p class="msg">
|
||||
{% trans app_name=app.pretty, app_version=app.latest_version %}
|
||||
This add-on is incompatible with <b>{{ app_name }}
|
||||
{{ app_version }}</b>, the latest release of {{ app_name }}.
|
||||
Please consider updating your add-on's compatibility info, or
|
||||
uploading a newer version of this add-on.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
<p>
|
||||
<a href="#" class="button compat-update"
|
||||
data-updateurl="{{ update_url }}">{{ _('Update Compatibility') }}</a>
|
||||
<a href="{{ url('devhub.versions', addon.id) }}" class="button">
|
||||
{{ _('Upload New Version') }}</a>
|
||||
{% trans %}or <a href="#" class="close">Ignore</a>{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="compat-update-modal modal hidden"></div>
|
||||
{% endif %}
|
|
@ -0,0 +1,38 @@
|
|||
{% from "devhub/includes/macros.html" import compat %}
|
||||
<h3>{{ _('Update Compatibility') }}</h3>
|
||||
<p>
|
||||
{% trans %}
|
||||
Adjusting application information here will allow users to install
|
||||
your add-on even if the install manifest in the package indicates
|
||||
that the add-on is incompatible.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
<form action="{{ url('devhub.ajax.compat.update', addon.id, version.id) }}"
|
||||
class="compat-versions" data-addonid="{{ addon.id }}">
|
||||
{{ csrf() }}
|
||||
{{ compat_form.non_form_errors()|safe }}
|
||||
{{ compat_form.management_form|safe }}
|
||||
<table>
|
||||
<thead>
|
||||
<th>{{ _('Application') }}</th>
|
||||
<th>{{ _('Supported Versions') }}</th>
|
||||
<th>{{ _('Remove') }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for form in compat_form.initial_forms %}
|
||||
{{ compat(form) }}
|
||||
{% endfor %}
|
||||
{% for form in compat_form.extra_forms %}
|
||||
{{ compat(form, is_extra_form=True) }}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="add-app">
|
||||
<a href="#">{{ _('Add Another Application…')|safe }}</a>
|
||||
</p>
|
||||
<div class="new-apps"></div>
|
||||
<p class="compat-save">
|
||||
<button type="submit">{{ _('Save Changes') }}</button>
|
||||
{% trans %}or <a href="#" class="close">Cancel</a>{% endtrans %}
|
||||
</p>
|
||||
</form>
|
|
@ -2,12 +2,12 @@
|
|||
{% set collection = collection or None %}
|
||||
{% set username = request.amo_user.username if request.user.is_authenticated() else '' %}
|
||||
{% for addon in addons %}
|
||||
<div class="item">
|
||||
<div class="item" data-addonid="{{ addon.id }}">
|
||||
{{ dev_heading(addon) }}
|
||||
<div class="item-info">
|
||||
{{ dev_item_info(addon, amo) }}
|
||||
</div>
|
||||
<blockquote>
|
||||
<div class="item-main">
|
||||
<ul class="item-details">
|
||||
{# L10n: {0} is a version number. #}
|
||||
<li>{{ _('<strong>Latest version:</strong> {0}'|
|
||||
|
@ -33,6 +33,6 @@
|
|||
<div class="item-actions">
|
||||
{{ dev_item_actions(addon, amo) }}
|
||||
</div>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -39,16 +39,13 @@
|
|||
{{ _('Edit Listing') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{# TODO: Add URL for 'Manage Version' page. #}
|
||||
<a href="#" class="tooltip"
|
||||
<a href="{{ url('devhub.versions', addon.id) }}" class="tooltip"
|
||||
title="{{ _('Upload a new version of this add-on.') }}">
|
||||
{{ _('New Version') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
class="tooltip{% if addon.incompatible_latest_apps() %} compat-error{% endif %}"
|
||||
title="{{ _('View and update application compatibility ranges.') }}">
|
||||
{{ _('Compatibility') }}</a>
|
||||
<li class="compat"
|
||||
data-src="{{ url('devhub.ajax.compat.status', addon.id) }}">
|
||||
{% include "devhub/addons/ajax_compat_status.html" %}
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url('stats.overview', addon.id) }}" class="tooltip"
|
||||
|
@ -56,18 +53,18 @@
|
|||
{{ _('Statistics') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="more-actions-view more-actions">{{ _('More') }}</a>
|
||||
<div class="more-actions-view-dropdown popup hidden">
|
||||
<ul>
|
||||
<a href="#" class="more-actions">{{ _('More') }}</a>
|
||||
<div class="more-actions-popup popup hidden">
|
||||
<ul>
|
||||
{# TODO: Add URLs for 'Manage Add-on' pages. #}
|
||||
<li><a href="#">{{ _('Manage Authors & License') }}</a></li>
|
||||
<li><a href="#">{{ _('Manage Payments') }}</a></li>
|
||||
<li><a href="#">{{ _('Manage Status & Versions') }}</a></li>
|
||||
<li class="item-group"><a href="#">
|
||||
{{ _('View Add-on Listing') }}</a></li>
|
||||
<li><a href="#">{{ _('View Recent Changes') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<li><a href="#">{{ _('Manage Authors & License') }}</a></li>
|
||||
<li><a href="#">{{ _('Manage Payments') }}</a></li>
|
||||
<li><a href="#">{{ _('Manage Status & Versions') }}</a></li>
|
||||
<li class="item-group"><a href="#">
|
||||
{{ _('View Add-on Listing') }}</a></li>
|
||||
<li><a href="#">{{ _('View Recent Changes') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
|
|
|
@ -27,3 +27,29 @@
|
|||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro compat(form, is_extra_form=False) %}
|
||||
<tr{% if is_extra_form %} class="app-extra-orig{{ ' app-extra' if not form.errors }}"{% endif %}>
|
||||
<td class="app {{ form.app.short }}">{{ form.app.pretty }}</td>
|
||||
<td class="select">
|
||||
<div>
|
||||
<label>{{ form.min.label|safe }}</label> {{ form.min|safe }}
|
||||
{{ form.non_field_errors()|safe }}
|
||||
{{ form.min.errors|safe }}
|
||||
</div>
|
||||
<div><span class="range">–</span></div>
|
||||
<div>
|
||||
<label>{{ form.max.label|safe }}</label> {{ form.max|safe }}
|
||||
{{ form.max.errors|safe }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="remove">
|
||||
<div>
|
||||
<label>{{ form.DELETE.label|safe }} {{ form.DELETE|safe }}</label>
|
||||
{{ form.application|safe }}
|
||||
{{ form.id|safe }}
|
||||
<a href="#" class="remove"
|
||||
title="{{ _('Remove this application') }}">x</a>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
{% endmacro %}
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
</table>
|
||||
<table>
|
||||
{% if compat_form %}
|
||||
{{ compat_form.non_form_errors()|safe }}
|
||||
{{ compat_form.management_form|safe }}
|
||||
{% for form in compat_form.initial_forms %}
|
||||
{{ compat(form) }}
|
||||
|
|
|
@ -68,4 +68,3 @@ class TestActivityLog(test_utils.TestCase):
|
|||
eq_(len(entries), 1)
|
||||
entries = ActivityLog.objects.for_user(u)
|
||||
eq_(len(entries), 1)
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ from devhub.forms import ContribForm
|
|||
from devhub.models import ActivityLog
|
||||
from files.models import File, Platform
|
||||
from users.models import UserProfile
|
||||
from versions.models import License, Version
|
||||
from versions.models import ApplicationsVersions, License, Version
|
||||
|
||||
|
||||
class HubTest(test_utils.TestCase):
|
||||
fixtures = ('browse/nameless-addon', 'base/users')
|
||||
fixtures = ['browse/nameless-addon', 'base/users']
|
||||
|
||||
def setUp(self):
|
||||
translation.activate('en-US')
|
||||
|
@ -131,10 +131,7 @@ class TestDashboard(HubTest):
|
|||
r = self.client.get(self.url)
|
||||
doc = pq(r.content)
|
||||
|
||||
# There should be 10 add-on listing items.
|
||||
eq_(len(doc('.item .item-info')), 10)
|
||||
|
||||
# There should be neither a listing header nor a pagination footer.
|
||||
eq_(doc('#addon-list-options').length, 0)
|
||||
eq_(doc('.listing-footer .pagination').length, 0)
|
||||
|
||||
|
@ -144,14 +141,60 @@ class TestDashboard(HubTest):
|
|||
r = self.client.get(self.url + '?page=2')
|
||||
doc = pq(r.content)
|
||||
|
||||
# There should be 10 add-on listing items.
|
||||
eq_(len(doc('.item .item-info')), 5)
|
||||
|
||||
# There should be a listing header and pagination footer.
|
||||
eq_(doc('#addon-list-options').length, 1)
|
||||
eq_(doc('.listing-footer .pagination').length, 1)
|
||||
|
||||
|
||||
class TestUpdateCompatibility(test_utils.TestCase):
|
||||
fixtures = ['base/apps', 'base/users', 'base/addon_4594_a9',
|
||||
'base/addon_3615']
|
||||
|
||||
def setUp(self):
|
||||
self.url = reverse('devhub.addons')
|
||||
|
||||
def test_no_compat(self):
|
||||
assert self.client.login(username='admin@mozilla.com',
|
||||
password='password')
|
||||
r = self.client.get(self.url)
|
||||
doc = pq(r.content)
|
||||
assert not doc('.item[data-addonid=4594] .tooltip.compat-update')
|
||||
a = Addon.objects.get(pk=4594)
|
||||
r = self.client.get(reverse('devhub.ajax.compat.update',
|
||||
args=[a.id, a.current_version.id]))
|
||||
eq_(r.status_code, 404)
|
||||
|
||||
def test_compat(self):
|
||||
a = Addon.objects.get(pk=3615)
|
||||
assert self.client.login(username='del@icio.us', password='password')
|
||||
|
||||
r = self.client.get(self.url)
|
||||
doc = pq(r.content)
|
||||
cu = doc('.item[data-addonid=3615] .tooltip.compat-update')
|
||||
assert cu
|
||||
|
||||
update_url = reverse('devhub.ajax.compat.update',
|
||||
args=[a.id, a.current_version.id])
|
||||
eq_(cu.attr('data-updateurl'), update_url)
|
||||
|
||||
status_url = reverse('devhub.ajax.compat.status', args=[a.id])
|
||||
eq_(doc('.item[data-addonid=3615] li.compat').attr('data-src'),
|
||||
status_url)
|
||||
|
||||
assert doc('.item[data-addonid=3615] .compat-update-modal')
|
||||
|
||||
def test_incompat(self):
|
||||
av = ApplicationsVersions.objects.get(pk=47881)
|
||||
av.max = AppVersion.objects.get(pk=97) # Firefox 2.0
|
||||
av.save()
|
||||
assert self.client.login(username='del@icio.us', password='password')
|
||||
r = self.client.get(self.url)
|
||||
doc = pq(r.content)
|
||||
assert doc('.item[data-addonid=3615] .tooltip.compat-error')
|
||||
assert doc('.item[data-addonid=3615] .compat-error-popup .app.%s' %
|
||||
amo.FIREFOX.short)
|
||||
|
||||
|
||||
def formset(*args, **kw):
|
||||
"""
|
||||
Build up a formset-happy POST.
|
||||
|
@ -1060,7 +1103,7 @@ class TestVersionEditCompat(TestVersionEdit):
|
|||
r = self.client.post(self.url, self.formset(d, initial_count=1))
|
||||
eq_(r.status_code, 200)
|
||||
eq_(r.context['compat_form'].forms[0].non_field_errors(),
|
||||
['Invalid version range'])
|
||||
['Invalid version range.'])
|
||||
|
||||
|
||||
class TestAddonSubmission(test_utils.TestCase):
|
||||
|
|
|
@ -29,11 +29,20 @@ detail_patterns = patterns('',
|
|||
views.submit_finished, name='devhub.submit.finished'),
|
||||
)
|
||||
|
||||
ajax_patterns = patterns('',
|
||||
url('^versions/compatibility/status$',
|
||||
views.ajax_compat_status, name='devhub.ajax.compat.status'),
|
||||
url('^versions/(?P<version_id>\d+)/compatibility$',
|
||||
views.ajax_compat_update, name='devhub.ajax.compat.update'),
|
||||
)
|
||||
|
||||
urlpatterns = decorate(write, patterns('',
|
||||
url('^$', views.index, name='devhub.index'),
|
||||
|
||||
# URLs for a single add-on.
|
||||
('^addon/(?P<addon_id>\d+)/', include(detail_patterns)),
|
||||
('^ajax/addon/(?P<addon_id>\d+)/', include(ajax_patterns)),
|
||||
|
||||
# Redirect people who have /addons/ instead of /addon/.
|
||||
('^addons/\d+/.*',
|
||||
lambda r: redirect(r.path.replace('addons', 'addon', 1))),
|
||||
|
|
|
@ -92,6 +92,28 @@ def dashboard(request):
|
|||
'sort_opts': filter.opts})
|
||||
|
||||
|
||||
@dev_required
|
||||
def ajax_compat_status(request, addon_id, addon):
|
||||
return jingo.render(request, 'devhub/addons/ajax_compat_status.html',
|
||||
dict(addon=addon))
|
||||
|
||||
|
||||
@dev_required
|
||||
def ajax_compat_update(request, addon_id, addon, version_id):
|
||||
if not addon.accepts_compatible_apps():
|
||||
raise http.Http404()
|
||||
version = get_object_or_404(Version, pk=version_id, addon=addon)
|
||||
compat_form = forms.CompatFormSet(request.POST or None,
|
||||
queryset=version.apps.all())
|
||||
if request.method == 'POST' and compat_form.is_valid():
|
||||
for compat in compat_form.save(commit=False):
|
||||
compat.version = version
|
||||
compat.save()
|
||||
return jingo.render(request, 'devhub/addons/ajax_compat_update.html',
|
||||
dict(addon=addon, version=version,
|
||||
compat_form=compat_form))
|
||||
|
||||
|
||||
@login_required
|
||||
def activity(request):
|
||||
return jingo.render(request, 'devhub/addons/activity.html')
|
||||
|
@ -297,7 +319,7 @@ def version_edit(request, addon_id, addon, version_id):
|
|||
|
||||
file_form = forms.FileFormSet(request.POST or None, prefix='files',
|
||||
queryset=version.files.all())
|
||||
data= {'version_form': version_form, 'file_form': file_form}
|
||||
data = {'version_form': version_form, 'file_form': file_form}
|
||||
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=605941
|
||||
# remove compatability from the version edit page for search engines
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#edit-addon .listing-footer {
|
||||
-moz-border-radius: 0 0 8px 8px;
|
||||
-webkit-border-radius: 0 0 8px 8px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border-top: 1px solid #C9E8F3;
|
||||
padding: 9px;
|
||||
}
|
||||
|
@ -82,7 +83,7 @@
|
|||
}
|
||||
|
||||
#edit-addon .help,
|
||||
#edit-addon .remove {
|
||||
a.remove {
|
||||
-moz-border-radius: 20px;
|
||||
-webkit-border-radius :20px;
|
||||
border-radius: 20px;
|
||||
|
@ -99,7 +100,7 @@
|
|||
width: 18px;
|
||||
}
|
||||
|
||||
#edit-addon .remove {
|
||||
a.remove {
|
||||
cursor: pointer;
|
||||
line-height: 16px;
|
||||
float: right;
|
||||
|
@ -107,6 +108,7 @@
|
|||
}
|
||||
|
||||
#edit-addon .author:hover .remove,
|
||||
a.remove:hover,
|
||||
#edit-addon tr:hover .help {
|
||||
background-color: #CCC;
|
||||
}
|
||||
|
@ -158,7 +160,107 @@
|
|||
|
||||
/* @end */
|
||||
|
||||
/* @group Version Compatibility */
|
||||
|
||||
.compat-update-modal {
|
||||
min-height: 425px;
|
||||
}
|
||||
.compat-update-modal > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.compat-update-modal > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.compat-versions table {
|
||||
width: 100%;
|
||||
}
|
||||
.compat-versions tbody {
|
||||
border: 1px solid #000;
|
||||
border-width: 1px 0;
|
||||
}
|
||||
.compat-versions thead th {
|
||||
color: #888;
|
||||
}
|
||||
.compat-versions tbody tr:not(:last-child) {
|
||||
border-bottom: 1px dotted #a5bfce;
|
||||
}
|
||||
|
||||
.compat-versions td.app,
|
||||
.new-apps a.app {
|
||||
font-weight: bold;
|
||||
min-height: 24px;
|
||||
padding-left: 35px;
|
||||
}
|
||||
.compat-versions td.app {
|
||||
background-position: 0 0.5em;
|
||||
-moz-background-size: auto 24px;
|
||||
-wekbkit-background-size: auto 24px;
|
||||
background-size: auto 24px;
|
||||
line-height: 30px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.new-apps a.app {
|
||||
background-position: 0 50%;
|
||||
-moz-background-size: auto 16px;
|
||||
-wekbkit-background-size: auto 16px;
|
||||
background-size: auto 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.compat-versions p.add-app {
|
||||
margin-top: -1em;
|
||||
}
|
||||
.compat-versions p.add-app a,
|
||||
.new-apps a.app {
|
||||
display: block;
|
||||
}
|
||||
.compat-versions p.add-app + .new-apps {
|
||||
margin: -1em 0 1em;
|
||||
}
|
||||
|
||||
.compat-versions thead th,
|
||||
.compat-versions table + p,
|
||||
.compat-versions .errorlist {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.compat-versions .errorlist {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.compat-versions td.select {
|
||||
display: table-row;
|
||||
}
|
||||
.compat-versions td.select div {
|
||||
display: table-cell;
|
||||
padding: 0 0.5em 0;
|
||||
}
|
||||
.compat-versions td.select div:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
.compat-versions td.select select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.compat-versions td label,
|
||||
.compat-versions td.remove input {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
.compat-versions td.remove div {
|
||||
position: relative;
|
||||
top: 0.308em;
|
||||
}
|
||||
|
||||
.compat-versions tr.app-extra,
|
||||
.compat-versions .new-apps {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
|
||||
/* @group Dashboard */
|
||||
|
||||
.action-needed {
|
||||
background-color: #fff;
|
||||
border: 3px solid #c8e8f3;
|
||||
|
@ -177,8 +279,7 @@
|
|||
|
||||
.action-needed .button-wrapper:last-child,
|
||||
.item ul.item-details,
|
||||
.item .item-actions ul,
|
||||
.popup ul {
|
||||
.item .item-actions ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
@ -201,14 +302,6 @@
|
|||
color: #05e;
|
||||
}
|
||||
|
||||
.compat-error {
|
||||
background: url(../../img/zamboni/notifications.png) 0px -164px no-repeat;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.item-actions a.compat-error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.item-actions h5,
|
||||
.item-actions>ul,
|
||||
.item-actions>ul>li {
|
||||
|
@ -217,6 +310,9 @@
|
|||
.item-actions>ul>li {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.item-actions>ul>li:after {
|
||||
margin: 0 0 4px 4px;
|
||||
}
|
||||
.item-actions>ul>li:not(:last-child):after {
|
||||
background-color: #aaa;
|
||||
-moz-border-radius: 5em;
|
||||
|
@ -231,32 +327,25 @@
|
|||
.item-actions:hover>ul>li:not(:last-child):after {
|
||||
background-color: #555;
|
||||
}
|
||||
.item-actions ul li ul {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.item-group {
|
||||
border-top: 1px dotted #aecfda;
|
||||
margin-top: 0.5em;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.item-actions h5 {
|
||||
font-size: 1em;
|
||||
margin-right: 8px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.item-actions h5:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
.listing .item blockquote {
|
||||
overflow-x: visible; /* Undoing ``hidden`` so 'More' popup can breathe. */
|
||||
a.more-actions {
|
||||
position: relative;
|
||||
}
|
||||
a.more-actions:after {
|
||||
border-color: #aaa transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 3px 3px 0;
|
||||
content: "";
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
height: 0;
|
||||
margin: 8px 0 0 4px;
|
||||
width: 0;
|
||||
|
@ -268,6 +357,34 @@ a.more-actions:after:hover {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.more-actions-popup li a {
|
||||
display: block;
|
||||
}
|
||||
.more-actions-popup .item-group {
|
||||
border-top: 1px dotted #aecfda;
|
||||
margin-top: 0.5em;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
/* @group Compatibility */
|
||||
|
||||
.compat-error {
|
||||
background: url(../../img/zamboni/notifications.png) 0px -164px no-repeat;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.item-actions a.compat-error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.compat-error-popup.popup {
|
||||
padding: 15px;
|
||||
}
|
||||
.compat-error-popup.popup .msg {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
|
||||
#submit-addon {
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
|
@ -275,7 +392,7 @@ a.more-actions:after:hover {
|
|||
#submit-addon a.button:visited {
|
||||
background: #5af;
|
||||
background: -moz-linear-gradient(top, #acf, #5af);
|
||||
background: -webkit-gradient(linear, left top, left bottom, #acf, #5af);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#acf), to(#5af));
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
|
@ -287,15 +404,15 @@ a.more-actions:after:hover {
|
|||
#submit-addon a.button:hover,
|
||||
#submit-addon a.button:focus {
|
||||
border-color: #25f;
|
||||
-moz-box-shadow: inset 0 0 2px rgba(255, 255, 255, 1);
|
||||
-webkit-box-shadow: inset 0 0 2px rgba(255, 255, 255, 1);
|
||||
box-shadow: inset 0 0 2px rgba(255, 255, 255, 1);
|
||||
-moz-box-shadow: inset 0 0 2px #fff;
|
||||
-webkit-box-shadow: inset 0 0 2px #fff;
|
||||
box-shadow: inset 0 0 2px #fff;
|
||||
}
|
||||
#submit-addon a.button:active {
|
||||
border-color: #03c;
|
||||
background: #7bf;
|
||||
background: -moz-linear-gradient(top, #5af, #7bf);
|
||||
background: -webkit-gradient(linear, left top, left bottom, #39f, #7bf);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#5af), to(#7bf));
|
||||
-moz-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
-webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
|
|
|
@ -620,21 +620,23 @@ div.popup-shim {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.popup p:last-child {
|
||||
.popup p:last-child,
|
||||
.popup ul:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.install-note .msg,
|
||||
.popup .msg {
|
||||
.popup .msg,
|
||||
.app {
|
||||
background-repeat: no-repeat;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.firefox .install-note .msg { background-image: url(../../img/amo2009/app-icons/small/firefox.png); }
|
||||
.thunderbird .install-note .msg { background-image: url(../../img/amo2009/app-icons/small/thunderbird.png); }
|
||||
.mobile .install-note .msg { background-image: url(../../img/amo2009/app-icons/small/mobile.png); }
|
||||
.sunbird .install-note .msg { background-image: url(../../img/amo2009/app-icons/small/sunbird.png); }
|
||||
.seamonkey .install-note .msg { background-image: url(../../img/amo2009/app-icons/small/seamonkey.png); }
|
||||
.firefox .install-note .msg, .firefox.app { background-image: url(../../img/amo2009/app-icons/small/firefox.png); }
|
||||
.thunderbird .install-note .msg, .thunderbird.app { background-image: url(../../img/amo2009/app-icons/small/thunderbird.png); }
|
||||
.mobile .install-note .msg, .mobile.app { background-image: url(../../img/amo2009/app-icons/small/mobile.png); }
|
||||
.sunbird .install-note .msg, .sunbird.app { background-image: url(../../img/amo2009/app-icons/small/sunbird.png); }
|
||||
.seamonkey .install-note .msg, .seamonkey.app { background-image: url(../../img/amo2009/app-icons/small/seamonkey.png); }
|
||||
|
||||
.install-note .msg.m-unreviewed,
|
||||
.install-note .msg.m-selfhosted,
|
||||
|
@ -652,7 +654,6 @@ div.popup-shim {
|
|||
position: absolute;
|
||||
left: -15px;
|
||||
margin-top: 5px;
|
||||
z-index: 999;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border: 3px solid #2e5186;
|
||||
|
@ -691,6 +692,24 @@ div.popup-shim {
|
|||
border-color: #c90;
|
||||
}
|
||||
|
||||
.popup.error {
|
||||
border-color: #6c1a1a;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
-khtml-opacity: 0.75;
|
||||
-moz-opacity: 0.75;
|
||||
opacity: 0.75;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
/* http://nicolasgallagher.com/demo/pure-css-speech-bubbles/bubbles.html */
|
||||
/* Creates the larger triangle. */
|
||||
.install-note:before,
|
||||
|
@ -712,6 +731,10 @@ div.popup-shim {
|
|||
border-bottom-color: #c90;
|
||||
}
|
||||
|
||||
.popup.error:before {
|
||||
border-bottom-color: #6c1a1a;
|
||||
}
|
||||
|
||||
/* creates the smaller triangle */
|
||||
.install-note:after,
|
||||
.popup:after {
|
||||
|
@ -2980,7 +3003,8 @@ a.outlink:hover {
|
|||
|
||||
.widgets .widget.ajax-loading,
|
||||
.widgets .widget:hover.ajax-loading,
|
||||
input.ui-autocomplete-loading {
|
||||
input.ui-autocomplete-loading,
|
||||
.modal.ajax-loading {
|
||||
background-image: url(../../img/zamboni/loading-white.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: left bottom;
|
||||
|
@ -2988,6 +3012,9 @@ input.ui-autocomplete-loading {
|
|||
input.ui-autocomplete-loading {
|
||||
background-position: right center;
|
||||
}
|
||||
.modal.ajax-loading {
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
#popup-staging .popup {
|
||||
display: none;
|
||||
|
|
|
@ -23,7 +23,9 @@ $(document).ready(function() {
|
|||
|
||||
|
||||
$(document).ready(function() {
|
||||
$(".more-actions-view-dropdown").popup(".more-actions-view", {
|
||||
$.ajaxSetup({cache: false});
|
||||
|
||||
$('.more-actions-popup').popup('.more-actions', {
|
||||
width: 'inherit',
|
||||
offset: {x: 15},
|
||||
callback: function(obj) {
|
||||
|
@ -32,6 +34,9 @@ $(document).ready(function() {
|
|||
});
|
||||
|
||||
truncateFields();
|
||||
|
||||
initCompatibility();
|
||||
|
||||
$('#edit-addon').delegate('h3 a', 'click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -484,6 +489,7 @@ function initAuthorFields() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function resetModal(obj) {
|
||||
|
||||
upload = $("<input type='file'>").attr('name', 'upload')
|
||||
|
@ -504,3 +510,112 @@ function resetModal(obj) {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function initCompatibility() {
|
||||
$('p.add-app a').live('click', function(e) {
|
||||
e.preventDefault();
|
||||
var outer = $(this).closest('form');
|
||||
|
||||
$('tr.app-extra', outer).each(function() {
|
||||
addAppRow(this);
|
||||
});
|
||||
|
||||
$('.new-apps', outer).toggle();
|
||||
|
||||
$('.new-apps ul').delegate('a', 'click', function(e) {
|
||||
e.preventDefault();
|
||||
var extraAppRow = $('tr.app-extra td[class=' + $(this).attr('class') + ']', outer);
|
||||
extraAppRow.parents('tr').find('input:checkbox').removeAttr('checked')
|
||||
.closest('tr').removeClass('app-extra');
|
||||
|
||||
$(this).closest('li').remove();
|
||||
|
||||
if (!$('tr.app-extra', outer).length)
|
||||
$('p.add-app', outer).hide();
|
||||
});
|
||||
});
|
||||
|
||||
$('.compat-versions .remove').live('click', function(e) {
|
||||
e.preventDefault();
|
||||
var appRow = $(this).closest('tr');
|
||||
|
||||
appRow.addClass('app-extra');
|
||||
|
||||
if (!appRow.hasClass('app-extra-orig'))
|
||||
appRow.find('input:checkbox').attr('checked', true);
|
||||
|
||||
$('p.add-app:hidden', $(this).closest('form')).show();
|
||||
addAppRow(appRow);
|
||||
});
|
||||
|
||||
$('.compat-update-modal').modal('a.compat-update', {
|
||||
delegate: $('.item-actions li.compat, .compat-error-popup'),
|
||||
hideme: false,
|
||||
emptyme: true,
|
||||
callback: compatModalCallback
|
||||
});
|
||||
|
||||
$('.compat-error-popup').popup('.compat-error', {
|
||||
width: '450px',
|
||||
callback: function(obj) {
|
||||
var $popup = this;
|
||||
$popup.delegate('.close, .compat-update', 'click', function(e) {
|
||||
e.preventDefault();
|
||||
$popup.hideMe();
|
||||
});
|
||||
return {pointTo: $(obj.click_target)};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function addAppRow(obj) {
|
||||
var outer = $(obj).closest('form'),
|
||||
appClass = $('td.app', obj).attr('class');
|
||||
if (!$('.new-apps ul', outer).length)
|
||||
$('.new-apps', outer).html('<ul></ul>');
|
||||
if ($('.new-apps ul a[class=' + appClass + ']', outer).length)
|
||||
return;
|
||||
var appLabel = $('td.app', obj).text(),
|
||||
appHTML = '<li><a href="#" class="' + appClass + '">' + appLabel + '</a></li>';
|
||||
$('.new-apps ul', outer).append(appHTML);
|
||||
}
|
||||
|
||||
|
||||
function compatModalCallback(obj) {
|
||||
var $widget = this,
|
||||
ct = $(obj.click_target),
|
||||
form_url = ct.attr('data-updateurl');
|
||||
|
||||
if ($widget.hasClass('ajax-loading'))
|
||||
return;
|
||||
$widget.addClass('ajax-loading');
|
||||
|
||||
$widget.load(form_url, function(e) {
|
||||
$widget.removeClass('ajax-loading');
|
||||
});
|
||||
|
||||
$('form.compat-versions').live('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$widget.empty();
|
||||
|
||||
if ($widget.hasClass('ajax-loading'))
|
||||
return;
|
||||
$widget.addClass('ajax-loading');
|
||||
|
||||
var widgetForm = $(this);
|
||||
$.post(widgetForm.attr('action'), widgetForm.serialize(), function(data) {
|
||||
$widget.removeClass('ajax-loading');
|
||||
if ($(data).find('.errorlist').length) {
|
||||
$widget.html(data);
|
||||
} else {
|
||||
var c = $('.item[data-addonid=' + widgetForm.attr('data-addonid') + '] .item-actions li.compat');
|
||||
c.load(c.attr('data-src'));
|
||||
$widget.hideMe();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {pointTo: ct};
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ jQuery.fn.tooltip = function(tip_el) {
|
|||
}).live("mouseout", function (e) {
|
||||
$tip.hide()
|
||||
.removeClass("error");
|
||||
if ($title.length) {
|
||||
if ($title && $title.length) {
|
||||
$tgt = $(this);
|
||||
$title.attr('title', $title.attr('data-oldtitle'))
|
||||
.attr('data-oldtitle', '');
|
||||
|
@ -195,18 +195,19 @@ $.fn.popup = function(click_target, o) {
|
|||
return $popup;
|
||||
};
|
||||
|
||||
|
||||
// makes an element into a modal.
|
||||
// click_target defines the element/elements that trigger the popup.
|
||||
// click_target defines the element/elements that trigger the modal.
|
||||
// currently presumes the given element uses the '.modal' style
|
||||
// o takes the following optional fields:
|
||||
// callback: a function to run before displaying the popup. Returning
|
||||
// false from the function cancels the popup.
|
||||
// container: if set the popup will be appended to the container before
|
||||
// callback: a function to run before displaying the modal. Returning
|
||||
// false from the function cancels the modal.
|
||||
// container: if set the modal will be appended to the container before
|
||||
// being displayed.
|
||||
// width: the width of the popup.
|
||||
// width: the width of the modal.
|
||||
// delegate: delegates the click handling of the click_target to the
|
||||
// specified parent element.
|
||||
// hideme: defaults to true, if set to false, popup will not be hidden
|
||||
// hideme: defaults to true, if set to false, modal will not be hidden
|
||||
// when the user clicks outside of it.
|
||||
// note: all options may be overridden and modified by returning them in an
|
||||
// object from the callback.
|
||||
|
@ -221,8 +222,9 @@ $.fn.modal = function(click_target, o) {
|
|||
callback: false,
|
||||
onresize: function(){$modal.setPos();},
|
||||
hideme: true,
|
||||
emptyme: false,
|
||||
offset: {},
|
||||
width: 300
|
||||
width: 450
|
||||
}, o);
|
||||
|
||||
$modal.setWidth = function(w) {
|
||||
|
@ -247,11 +249,16 @@ $.fn.modal = function(click_target, o) {
|
|||
};
|
||||
|
||||
$modal.hideMe = function() {
|
||||
var p = $modal.o;
|
||||
$modal.hide();
|
||||
$modal.unbind();
|
||||
$modal.undelegate();
|
||||
$(document.body).unbind('click newmodal', $modal.hider);
|
||||
$(window).bind('resize', $modal.o.onresize);
|
||||
$('.modal-overlay').remove();
|
||||
if (p.emptyme) {
|
||||
$modal.empty();
|
||||
}
|
||||
return $modal;
|
||||
};
|
||||
|
||||
|
@ -282,6 +289,7 @@ $.fn.modal = function(click_target, o) {
|
|||
$ct.trigger("modal_show", [$modal]);
|
||||
if (p.container && p.container.length)
|
||||
$modal.detach().appendTo(p.container);
|
||||
$('<div class="modal-overlay"></div>').appendTo('body');
|
||||
$modal.setPos();
|
||||
setTimeout(function(){
|
||||
$modal.show();
|
||||
|
|
Загрузка…
Ссылка в новой задаче