Shows mobile platform choices when submitting a mobile add-on (bug 624226)
This commit is contained in:
Родитель
12d36dd474
Коммит
3db71441f2
|
@ -375,11 +375,27 @@ class PLATFORM_SUN:
|
||||||
shortname = 'solaris'
|
shortname = 'solaris'
|
||||||
api_name = 'SunOS'
|
api_name = 'SunOS'
|
||||||
|
|
||||||
|
|
||||||
|
class PLATFORM_ANDROID:
|
||||||
|
id = 7
|
||||||
|
name = _(u'Android')
|
||||||
|
shortname = u'android'
|
||||||
|
api_name = u'Android'
|
||||||
|
|
||||||
|
|
||||||
|
class PLATFORM_MAEMO:
|
||||||
|
id = 8
|
||||||
|
name = _(u'Maemo')
|
||||||
|
shortname = u'maemo'
|
||||||
|
api_name = u'Maemo'
|
||||||
|
|
||||||
# Order matters
|
# Order matters
|
||||||
PLATFORMS = {PLATFORM_ANY.id: PLATFORM_ANY, PLATFORM_ALL.id: PLATFORM_ALL,
|
PLATFORMS = {PLATFORM_ANY.id: PLATFORM_ANY, PLATFORM_ALL.id: PLATFORM_ALL,
|
||||||
PLATFORM_LINUX.id: PLATFORM_LINUX, PLATFORM_MAC.id: PLATFORM_MAC,
|
PLATFORM_LINUX.id: PLATFORM_LINUX, PLATFORM_MAC.id: PLATFORM_MAC,
|
||||||
PLATFORM_BSD.id: PLATFORM_BSD, PLATFORM_WIN.id: PLATFORM_WIN,
|
PLATFORM_BSD.id: PLATFORM_BSD, PLATFORM_WIN.id: PLATFORM_WIN,
|
||||||
PLATFORM_SUN.id: PLATFORM_SUN}
|
PLATFORM_SUN.id: PLATFORM_SUN,
|
||||||
|
PLATFORM_ANDROID.id: PLATFORM_ANDROID,
|
||||||
|
PLATFORM_MAEMO.id: PLATFORM_MAEMO}
|
||||||
|
|
||||||
SUPPORTED_PLATFORMS = {PLATFORM_ALL.id: PLATFORM_ALL,
|
SUPPORTED_PLATFORMS = {PLATFORM_ALL.id: PLATFORM_ALL,
|
||||||
PLATFORM_LINUX.id: PLATFORM_LINUX,
|
PLATFORM_LINUX.id: PLATFORM_LINUX,
|
||||||
|
|
|
@ -381,5 +381,27 @@
|
||||||
},
|
},
|
||||||
"model": "applications.appversion",
|
"model": "applications.appversion",
|
||||||
"pk": 282
|
"pk": 282
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 324,
|
||||||
|
"model": "applications.appversion",
|
||||||
|
"fields": {
|
||||||
|
"version_int": 2000000001000,
|
||||||
|
"application": 60,
|
||||||
|
"version": "2.0a1pre",
|
||||||
|
"modified": null,
|
||||||
|
"created": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 352,
|
||||||
|
"model": "applications.appversion",
|
||||||
|
"fields": {
|
||||||
|
"version_int": 4000000102000,
|
||||||
|
"application": 60,
|
||||||
|
"version": "4.0b2pre",
|
||||||
|
"modified": null,
|
||||||
|
"created": null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
||||||
|
This is not a valid XPI!
|
|
@ -3419,13 +3419,24 @@ class TestUpload(files.tests.UploadTest):
|
||||||
|
|
||||||
|
|
||||||
class TestUploadDetail(files.tests.UploadTest):
|
class TestUploadDetail(files.tests.UploadTest):
|
||||||
fixtures = ['base/apps', 'base/users']
|
fixtures = ['base/apps', 'base/appversion', 'base/users']
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
# Has to be a binary, non xpi file.
|
# Has to be a binary, non xpi file.
|
||||||
data = open(get_image_path('animated.png'), 'rb')
|
data = open(get_image_path('animated.png'), 'rb')
|
||||||
return self.client.post(reverse('devhub.upload'), {'upload': data})
|
return self.client.post(reverse('devhub.upload'), {'upload': data})
|
||||||
|
|
||||||
|
def validation_ok(self):
|
||||||
|
return {
|
||||||
|
'errors': 0,
|
||||||
|
'success': True,
|
||||||
|
'warnings': 0,
|
||||||
|
'notices': 0,
|
||||||
|
'message_tree': {},
|
||||||
|
'messages': [],
|
||||||
|
'rejected': False
|
||||||
|
}
|
||||||
|
|
||||||
@attr('validator')
|
@attr('validator')
|
||||||
def test_detail_json(self):
|
def test_detail_json(self):
|
||||||
self.post()
|
self.post()
|
||||||
|
@ -3457,6 +3468,61 @@ class TestUploadDetail(files.tests.UploadTest):
|
||||||
eq_(suite.attr('data-validateurl'),
|
eq_(suite.attr('data-validateurl'),
|
||||||
reverse('devhub.upload_detail', args=[upload.uuid, 'json']))
|
reverse('devhub.upload_detail', args=[upload.uuid, 'json']))
|
||||||
|
|
||||||
|
@mock.patch('devhub.tasks._validator')
|
||||||
|
def test_multi_app_addon_cannot_have_platforms(self, v):
|
||||||
|
v.return_value = json.dumps(self.validation_ok())
|
||||||
|
addon = os.path.join(settings.ROOT, 'apps', 'devhub', 'tests',
|
||||||
|
'addons', 'mobile-2.9.10-fx+fn.xpi')
|
||||||
|
with open(addon, 'rb') as f:
|
||||||
|
r = self.client.post(reverse('devhub.upload'),
|
||||||
|
{'upload': f})
|
||||||
|
eq_(r.status_code, 302)
|
||||||
|
upload = FileUpload.objects.get()
|
||||||
|
r = self.client.get(reverse('devhub.upload_detail',
|
||||||
|
args=[upload.uuid, 'json']))
|
||||||
|
eq_(r.status_code, 200)
|
||||||
|
data = json.loads(r.content)
|
||||||
|
eq_(data['new_platform_choices'], [
|
||||||
|
{'text': unicode(amo.PLATFORM_ALL.name), 'checked': True,
|
||||||
|
'value': amo.PLATFORM_ALL.id}])
|
||||||
|
|
||||||
|
@mock.patch('devhub.tasks._validator')
|
||||||
|
def test_new_platform_choices_for_mobile(self, v):
|
||||||
|
v.return_value = json.dumps(self.validation_ok())
|
||||||
|
addon = os.path.join(settings.ROOT, 'apps', 'devhub', 'tests',
|
||||||
|
'addons', 'mobile-0.1-fn.xpi')
|
||||||
|
with open(addon, 'rb') as f:
|
||||||
|
r = self.client.post(reverse('devhub.upload'),
|
||||||
|
{'upload': f})
|
||||||
|
eq_(r.status_code, 302)
|
||||||
|
upload = FileUpload.objects.get()
|
||||||
|
r = self.client.get(reverse('devhub.upload_detail',
|
||||||
|
args=[upload.uuid, 'json']))
|
||||||
|
eq_(r.status_code, 200)
|
||||||
|
data = json.loads(r.content)
|
||||||
|
eq_(data['new_platform_choices'], [
|
||||||
|
{'text': unicode(amo.PLATFORM_ALL.name), 'checked': True,
|
||||||
|
'value': amo.PLATFORM_ALL.id},
|
||||||
|
{'text': unicode(amo.PLATFORM_MAEMO.name),
|
||||||
|
'value': amo.PLATFORM_MAEMO.id},
|
||||||
|
{'text': unicode(amo.PLATFORM_ANDROID.name),
|
||||||
|
'value': amo.PLATFORM_ANDROID.id}])
|
||||||
|
|
||||||
|
@mock.patch('devhub.tasks._validator')
|
||||||
|
def test_unparsable_xpi(self, v):
|
||||||
|
v.return_value = json.dumps(self.validation_ok())
|
||||||
|
addon = os.path.join(settings.ROOT, 'apps', 'devhub', 'tests',
|
||||||
|
'addons', 'unopenable.xpi')
|
||||||
|
with open(addon, 'rb') as f:
|
||||||
|
r = self.client.post(reverse('devhub.upload'),
|
||||||
|
{'upload': f})
|
||||||
|
upload = FileUpload.objects.get()
|
||||||
|
r = self.client.get(reverse('devhub.upload_detail',
|
||||||
|
args=[upload.uuid, 'json']))
|
||||||
|
eq_(r.status_code, 200)
|
||||||
|
data = json.loads(r.content)
|
||||||
|
eq_(data['new_platform_choices'], None)
|
||||||
|
|
||||||
|
|
||||||
class TestUploadValidation(files.tests.UploadTest):
|
class TestUploadValidation(files.tests.UploadTest):
|
||||||
fixtures = ['base/apps', 'base/users',
|
fixtures = ['base/apps', 'base/users',
|
||||||
|
|
|
@ -10,6 +10,7 @@ import uuid
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django import forms as django_forms
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.utils.http import urlquote
|
from django.utils.http import urlquote
|
||||||
|
@ -34,6 +35,7 @@ from addons.models import Addon, AddonUser
|
||||||
from addons.views import BaseFilter
|
from addons.views import BaseFilter
|
||||||
from devhub.models import ActivityLog, RssKey, SubmitStep
|
from devhub.models import ActivityLog, RssKey, SubmitStep
|
||||||
from files.models import File, FileUpload
|
from files.models import File, FileUpload
|
||||||
|
from files.utils import parse_addon
|
||||||
from translations.models import delete_translation
|
from translations.models import delete_translation
|
||||||
from versions.models import License, Version
|
from versions.models import License, Version
|
||||||
|
|
||||||
|
@ -532,13 +534,38 @@ def json_upload_detail(upload):
|
||||||
validation = json.loads(upload.validation) if upload.validation else ""
|
validation = json.loads(upload.validation) if upload.validation else ""
|
||||||
url = reverse('devhub.upload_detail', args=[upload.uuid, 'json'])
|
url = reverse('devhub.upload_detail', args=[upload.uuid, 'json'])
|
||||||
full_report_url = reverse('devhub.upload_detail', args=[upload.uuid])
|
full_report_url = reverse('devhub.upload_detail', args=[upload.uuid])
|
||||||
|
new_platform_choices = None
|
||||||
|
|
||||||
if validation:
|
if validation:
|
||||||
prepare_validation_results(validation)
|
prepare_validation_results(validation)
|
||||||
|
if validation['errors'] == 0:
|
||||||
|
try:
|
||||||
|
app_ids = [a.id for a in parse_addon(upload.path)['apps']]
|
||||||
|
if amo.MOBILE.id in app_ids:
|
||||||
|
# For multiple apps, choosing a platform no longer makes
|
||||||
|
# sense, so only allow ALL
|
||||||
|
new_platform_choices = [
|
||||||
|
dict(value=amo.PLATFORM_ALL.id,
|
||||||
|
text=unicode(amo.PLATFORM_ALL.name),
|
||||||
|
checked=True)]
|
||||||
|
if len(app_ids) == 1:
|
||||||
|
# For mobile only add-ons, show mobile platforms:
|
||||||
|
new_platform_choices.extend([
|
||||||
|
dict(value=amo.PLATFORM_MAEMO.id,
|
||||||
|
text=unicode(amo.PLATFORM_MAEMO.name)),
|
||||||
|
dict(value=amo.PLATFORM_ANDROID.id,
|
||||||
|
text=unicode(amo.PLATFORM_ANDROID.name))])
|
||||||
|
except django_forms.ValidationError:
|
||||||
|
# XPI parsing errors will be reported in the form submission
|
||||||
|
# (next request).
|
||||||
|
# TODO(Kumar) It would be nicer to present errors to the user
|
||||||
|
# right here to avoid confusion about platform selection.
|
||||||
|
log.exception("XPI parsing error, ignored")
|
||||||
|
|
||||||
r = dict(upload=upload.uuid, validation=validation,
|
r = dict(upload=upload.uuid, validation=validation,
|
||||||
error=upload.task_error, url=url,
|
error=upload.task_error, url=url,
|
||||||
full_report_url=full_report_url)
|
full_report_url=full_report_url,
|
||||||
|
new_platform_choices=new_platform_choices)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -883,9 +883,30 @@ function addonUploaded(json) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.validation.detected_type == 'search') {
|
if (json.validation.detected_type == 'search') {
|
||||||
|
// TODO(Kumar) this probably broke the versions page
|
||||||
|
// which does not use #create-addon. Remove the id.
|
||||||
$("#create-addon .platform").hide();
|
$("#create-addon .platform").hide();
|
||||||
} else {
|
} else {
|
||||||
$("#create-addon .platform:hidden").show();
|
$("#create-addon .platform:hidden").show();
|
||||||
|
if (json.new_platform_choices) {
|
||||||
|
// e.g. after uploading a Mobile add-on
|
||||||
|
$('.platform ul').empty();
|
||||||
|
$.each(json.new_platform_choices, function(i, pl) {
|
||||||
|
var li = $(format('<li><label><input name="platforms" ' +
|
||||||
|
'type="checkbox" class="platform" />' +
|
||||||
|
'{0}</label></li>', [pl.text])),
|
||||||
|
id = format('id_platforms_{0}', [i]),
|
||||||
|
label = $('label', li),
|
||||||
|
input = $('input', li);
|
||||||
|
label.attr('for', id);
|
||||||
|
input.attr('id', id);
|
||||||
|
input.attr('value', pl.value);
|
||||||
|
if (pl.checked) {
|
||||||
|
input.attr('checked', 'checked');
|
||||||
|
}
|
||||||
|
$('.platform ul').append(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statusclass = v.errors ? 'status-fail' : 'status-pass';
|
statusclass = v.errors ? 'status-fail' : 'status-pass';
|
||||||
|
|
|
@ -1026,4 +1026,68 @@ asyncTest('customized', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module('switch addon platforms', {
|
||||||
|
setup: function() {
|
||||||
|
this.sandbox = tests.createSandbox('#addon-platform-switching');
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
this.sandbox.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mobile', function() {
|
||||||
|
addonUploaded({
|
||||||
|
validation: {
|
||||||
|
"errors": 0,
|
||||||
|
"detected_type": "mobile",
|
||||||
|
"success": true,
|
||||||
|
"warnings": 0,
|
||||||
|
"notices": 0,
|
||||||
|
"message_tree": {},
|
||||||
|
"messages": [],
|
||||||
|
"rejected": false
|
||||||
|
},
|
||||||
|
new_platform_choices: [
|
||||||
|
{value: 1, checked: true, text: 'All Platforms'},
|
||||||
|
{value: 2, checked: false, text: 'Maemo'},
|
||||||
|
{value: 3, checked: false, text: 'Android'}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
equals($('.platform input:eq(0)', this.sandbox).attr('value'), '1');
|
||||||
|
equals($('.platform input:eq(0)', this.sandbox).attr('id'),
|
||||||
|
'id_platforms_0');
|
||||||
|
equals($('.platform input:eq(0)', this.sandbox).attr('checked'), true);
|
||||||
|
equals($('.platform li:eq(0)', this.sandbox).text(),
|
||||||
|
'All Platforms');
|
||||||
|
equals($('.platform input:eq(1)', this.sandbox).attr('value'), '2');
|
||||||
|
equals($('.platform input:eq(1)', this.sandbox).attr('id'),
|
||||||
|
'id_platforms_1');
|
||||||
|
equals($('.platform li:eq(1)', this.sandbox).text(), 'Maemo');
|
||||||
|
equals($('.platform input:eq(2)', this.sandbox).attr('value'), '3');
|
||||||
|
equals($('.platform input:eq(2)', this.sandbox).attr('id'),
|
||||||
|
'id_platforms_2');
|
||||||
|
equals($('.platform li:eq(2)', this.sandbox).text(), 'Android');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-ascii', function() {
|
||||||
|
addonUploaded({
|
||||||
|
validation: {
|
||||||
|
"errors": 0,
|
||||||
|
"detected_type": "mobile",
|
||||||
|
"success": true,
|
||||||
|
"warnings": 0,
|
||||||
|
"notices": 0,
|
||||||
|
"message_tree": {},
|
||||||
|
"messages": [],
|
||||||
|
"rejected": false
|
||||||
|
},
|
||||||
|
new_platform_choices: [
|
||||||
|
{value: 1, checked: true, text: 'フォクすけといっしょ'}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
equals($('.platform li:eq(0)', this.sandbox).text(),
|
||||||
|
'フォクすけといっしょ');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
UPDATE translations_seq SET id=LAST_INSERT_ID(id + 1);
|
||||||
|
SELECT LAST_INSERT_ID() FROM translations_seq INTO @id;
|
||||||
|
insert into translations (id, locale, localized_string) VALUES
|
||||||
|
((SELECT @id), 'en-US', 'Android');
|
||||||
|
INSERT INTO platforms (id, name) VALUES (7, (SELECT @id));
|
||||||
|
|
||||||
|
UPDATE translations_seq SET id=LAST_INSERT_ID(id + 1);
|
||||||
|
SELECT LAST_INSERT_ID() FROM translations_seq INTO @id;
|
||||||
|
INSERT INTO translations (id, locale, localized_string) VALUES
|
||||||
|
((SELECT @id), 'en-US', 'android');
|
||||||
|
UPDATE platforms SET shortname = @id WHERE id=7;
|
||||||
|
|
||||||
|
UPDATE translations_seq SET id=LAST_INSERT_ID(id + 1);
|
||||||
|
SELECT LAST_INSERT_ID() FROM translations_seq INTO @id;
|
||||||
|
INSERT INTO translations (id, locale, localized_string) VALUES
|
||||||
|
((SELECT @id), 'en-US', 'Maemo');
|
||||||
|
INSERT INTO platforms (id, name) VALUES (8, (SELECT @id));
|
||||||
|
|
||||||
|
UPDATE translations_seq SET id=LAST_INSERT_ID(id + 1);
|
||||||
|
SELECT LAST_INSERT_ID() FROM translations_seq INTO @id;
|
||||||
|
INSERT INTO translations (id, locale, localized_string) VALUES
|
||||||
|
((SELECT @id), 'en-US', 'maemo');
|
||||||
|
UPDATE platforms SET shortname = @id WHERE id=8;
|
|
@ -123,6 +123,24 @@
|
||||||
</ul>
|
</ul>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="addon-platform-switching">
|
||||||
|
<div class="platform">
|
||||||
|
<ul>
|
||||||
|
<li><label for="id_platforms_0">
|
||||||
|
<input checked="checked" name="platforms" value="100" id="id_platforms_0" type="checkbox" class="platform" />
|
||||||
|
All Platforms</label></li>
|
||||||
|
<li><label for="id_platforms_1">
|
||||||
|
<input name="platforms" value="101" id="id_platforms_1" type="checkbox" class="platform" />
|
||||||
|
Linux</label></li>
|
||||||
|
<li><label for="id_platforms_2">
|
||||||
|
<input name="platforms" value="102" id="id_platforms_2" type="checkbox" class="platform" />
|
||||||
|
Mac OS X</label></li>
|
||||||
|
<li><label for="id_platforms_3">
|
||||||
|
<input name="platforms" value="103" id="id_platforms_3" type="checkbox" class="platform" />
|
||||||
|
Windows</label></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="slugified-field">
|
<div id="slugified-field">
|
||||||
<input id="id_name" />
|
<input id="id_name" />
|
||||||
<span id="slug_edit" class="edit_with_prefix edit_initially_hidden">
|
<span id="slug_edit" class="edit_with_prefix edit_initially_hidden">
|
||||||
|
|
Загрузка…
Ссылка в новой задаче