merge in packaged uploads, device and payment choices to submission (bug 778882)
This commit is contained in:
Родитель
48146b9673
Коммит
1e89fb6fd5
|
@ -93,3 +93,47 @@ h2 .soon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#submit-payment-type {
|
||||||
|
.wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 222px;
|
||||||
|
margin-right: 10px;
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
width: 221px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
text-align: center;
|
||||||
|
padding: 180px 10px 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
&#free-os, &#paid-os {
|
||||||
|
background: url(../../img/developers/firefox_phone.png) no-repeat center 25px;
|
||||||
|
}
|
||||||
|
&#free-desktop {
|
||||||
|
background: url(../../img/developers/firefox_logo.png) no-repeat center 25px;
|
||||||
|
}
|
||||||
|
&#free-phone {
|
||||||
|
background: url(../../img/developers/firefox_android_phone.png) no-repeat center 25px;
|
||||||
|
}
|
||||||
|
&#free-tablet {
|
||||||
|
background: url(../../img/developers/firefox_android_tablet.png) no-repeat center 40px;
|
||||||
|
}
|
||||||
|
&#free-os, &#free-desktop, &#free-phone, &#free-tablet, &#paid-os {
|
||||||
|
&.selected {
|
||||||
|
background-color: @notice-yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
color: #484848;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#upload-file {
|
||||||
|
&.spacer {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
@import 'lib';
|
||||||
|
|
||||||
|
.tabbable {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 52px;
|
||||||
|
.border-radius(0 0 5px 5px);
|
||||||
|
hgroup {
|
||||||
|
position: absolute;
|
||||||
|
top: -44px;
|
||||||
|
width: 100%;
|
||||||
|
left: -1px;
|
||||||
|
h2 {
|
||||||
|
.border-box();
|
||||||
|
display: inline-block;
|
||||||
|
a {
|
||||||
|
.border-box();
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #e5e2db;
|
||||||
|
.border-radius(5px 5px 0 0);
|
||||||
|
line-height: 42px;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-bottom: 0;
|
||||||
|
color: #949494;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.active a {
|
||||||
|
border: 1px solid @taupe;
|
||||||
|
border-bottom: 1px solid @white;
|
||||||
|
background-color: @white;
|
||||||
|
color: #484848;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
position: absolute;
|
||||||
|
right: -2px;
|
||||||
|
a {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
display: none;
|
||||||
|
&.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 8.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 10 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 31 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 31 KiB |
|
@ -212,6 +212,8 @@
|
||||||
$upload_field.trigger('upload_success_results', [file, results]);
|
$upload_field.trigger('upload_success_results', [file, results]);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
$('#id_upload').val(results.upload);
|
||||||
|
$('#id_packaged').val(true);
|
||||||
upload_progress_inside.animate({'width': '100%'}, animateArgs);
|
upload_progress_inside.animate({'width': '100%'}, animateArgs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -303,8 +305,10 @@
|
||||||
upload_progress_outside.attr('class', 'bar-success');
|
upload_progress_outside.attr('class', 'bar-success');
|
||||||
upload_progress_inside.fadeOut();
|
upload_progress_inside.fadeOut();
|
||||||
|
|
||||||
$upload_field.trigger('reenable_uploader');
|
$('footer.listing-footer').removeClass('hidden');
|
||||||
|
$('button.upload-file-submit').removeAttr('disabled').focus();
|
||||||
|
|
||||||
|
$upload_field.trigger('reenable_uploader');
|
||||||
upload_results.addClass('status-pass');
|
upload_results.addClass('status-pass');
|
||||||
|
|
||||||
$('<strong>').text(message).appendTo(upload_results);
|
$('<strong>').text(message).appendTo(upload_results);
|
||||||
|
|
|
@ -180,6 +180,7 @@ $(document).ready(function() {
|
||||||
function check_webapp_validation(results) {
|
function check_webapp_validation(results) {
|
||||||
var $upload_field = $('#upload-webapp-url');
|
var $upload_field = $('#upload-webapp-url');
|
||||||
$('#id_upload').val(results.upload);
|
$('#id_upload').val(results.upload);
|
||||||
|
$('#id_packaged').val('');
|
||||||
if(results.error) {
|
if(results.error) {
|
||||||
$upload_field.trigger("upload_finished", [false, results, results.error]);
|
$upload_field.trigger("upload_finished", [false, results, results.error]);
|
||||||
} else if(! results.validation) {
|
} else if(! results.validation) {
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
(function(exports) {
|
(function(exports) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
function _pd(func) {
|
||||||
|
// Prevent-default function wrapper.
|
||||||
|
return function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
func.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
exports.houdini = function() {
|
exports.houdini = function() {
|
||||||
// Initialize magic labels.
|
// Initialize magic labels.
|
||||||
$(document).delegate('.houdini.ready .edit', 'click', _pd(function(e) {
|
$(document).delegate('.houdini.ready .edit', 'click', _pd(function(e) {
|
||||||
|
@ -34,9 +42,63 @@
|
||||||
$ctx.addClass('active');
|
$ctx.addClass('active');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset selected device buttons and values.
|
||||||
|
$('#submit-payment-type h2 a').click(function(e) {
|
||||||
|
$('#submit-payment-type a.choice').removeClass('selected');
|
||||||
|
$('#id_free').val([]);
|
||||||
|
$('#id_paid').val([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// When a big device button is clicked, update the form.
|
||||||
|
$('#submit-payment-type a.choice').on('click',
|
||||||
|
function(event) {
|
||||||
|
var $this = $(this),
|
||||||
|
$input = $('#id_' + this.id.split('-')[0]),
|
||||||
|
old = $input.val() || [],
|
||||||
|
val = $this.data('value');
|
||||||
|
|
||||||
|
if (old.indexOf(val) === -1) {
|
||||||
|
$this.addClass('selected');
|
||||||
|
old.push(val);
|
||||||
|
$input.val(old);
|
||||||
|
} else {
|
||||||
|
$this.removeClass('selected');
|
||||||
|
delete old[old.indexOf(val)];
|
||||||
|
$input.val(old);
|
||||||
|
}
|
||||||
|
show_packaged();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show packaged.
|
||||||
|
function show_packaged() {
|
||||||
|
var $target = $('#upload-file hgroup h2');
|
||||||
|
if ($target.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($('#id_free option[value=free-os]:selected').length ||
|
||||||
|
$('#id_paid option[value=paid-os]:selected').length) {
|
||||||
|
$target.eq(1).css({'display': 'inline'});
|
||||||
|
} else {
|
||||||
|
$target.eq(1).css({'display': 'none'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On page load, update the big device buttons with the values in the form.
|
||||||
|
$('#upload-webapp select').each(function(i, e) {
|
||||||
|
$.each($(e).val() || [], function() {
|
||||||
|
$('#submit-payment-type #' + this).addClass('selected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide the packaged tab, if needed.
|
||||||
|
show_packaged();
|
||||||
|
|
||||||
})(typeof exports === 'undefined' ? (this.submit_details = {}) : exports);
|
})(typeof exports === 'undefined' ? (this.submit_details = {}) : exports);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
(function() {
|
||||||
|
// Tabbable
|
||||||
|
$('.tabbable').each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
$this.find('.active h2').addClass('active');
|
||||||
|
|
||||||
|
var $headers = $this.find('.tab h2').detach(),
|
||||||
|
numTabs = $headers.length;
|
||||||
|
|
||||||
|
if (numTabs === (0 || 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var w = Math.floor(100 / numTabs),
|
||||||
|
$hgroup = $('<hgroup></hgroup>');
|
||||||
|
|
||||||
|
$headers.css({'width': w + '%'});
|
||||||
|
$hgroup.append($headers);
|
||||||
|
$this.prepend($hgroup);
|
||||||
|
|
||||||
|
$hgroup.find('a').each(function(i, e) {
|
||||||
|
$(this).on('click.switchtab', function(evt) {
|
||||||
|
var $myParent = $(evt.target).parent();
|
||||||
|
if ($myParent.hasClass('active')) {
|
||||||
|
evt.preventDefault();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$hgroup.find('h2').removeClass('active');
|
||||||
|
$myParent.addClass('active');
|
||||||
|
}
|
||||||
|
$this.find('.tab').removeClass('active');
|
||||||
|
$this.find('.tab:eq(' + i + ')').addClass('active');
|
||||||
|
evt.preventDefault();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `waffle_switch_mkt` (name, active, note)
|
||||||
|
VALUES ('allow-b2g-paid-submission', 0,
|
||||||
|
'Enable this to allow paid apps in the submission process.');
|
|
@ -58,6 +58,7 @@ CSS = {
|
||||||
'css/devreg/submit-details.less',
|
'css/devreg/submit-details.less',
|
||||||
'css/devreg/validation.less',
|
'css/devreg/validation.less',
|
||||||
'css/devreg/submit.less',
|
'css/devreg/submit.less',
|
||||||
|
'css/devreg/tabs.less',
|
||||||
'css/impala/personas.less',
|
'css/impala/personas.less',
|
||||||
'css/impala/colorpicker.less',
|
'css/impala/colorpicker.less',
|
||||||
|
|
||||||
|
@ -206,6 +207,7 @@ JS = {
|
||||||
# New stuff.
|
# New stuff.
|
||||||
'js/devreg/devhub.js',
|
'js/devreg/devhub.js',
|
||||||
'js/devreg/submit.js',
|
'js/devreg/submit.js',
|
||||||
|
'js/devreg/tabs.js',
|
||||||
'js/devreg/edit.js',
|
'js/devreg/edit.js',
|
||||||
'js/impala/persona_creation.js',
|
'js/impala/persona_creation.js',
|
||||||
'js/lib/jquery.minicolors.js',
|
'js/lib/jquery.minicolors.js',
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django import forms
|
||||||
|
|
||||||
import happyforms
|
import happyforms
|
||||||
from tower import ugettext as _, ugettext_lazy as _lazy
|
from tower import ugettext as _, ugettext_lazy as _lazy
|
||||||
|
import waffle
|
||||||
|
|
||||||
from addons.forms import AddonFormBasic
|
from addons.forms import AddonFormBasic
|
||||||
from addons.models import Addon, AddonUpsell
|
from addons.models import Addon, AddonUpsell
|
||||||
|
@ -38,31 +39,125 @@ class DevAgreementForm(happyforms.Form):
|
||||||
notification_id=app_surveys.id, update={'enabled': True})
|
notification_id=app_surveys.id, update={'enabled': True})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NewWebappForm(happyforms.Form):
|
class NewWebappForm(happyforms.Form):
|
||||||
|
# The selections for free.
|
||||||
|
FREE = (
|
||||||
|
('free-os', _lazy('Firefox OS')),
|
||||||
|
('free-desktop', _lazy('Firefox')),
|
||||||
|
('free-phone', _lazy('Firefox Mobile')),
|
||||||
|
('free-tablet', _lazy('Firefox Tablet')),
|
||||||
|
)
|
||||||
|
|
||||||
|
# The selections for paid.
|
||||||
|
PAID = (
|
||||||
|
('paid-os', _lazy('Firefox OS')),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extra information about those values for display in the page.
|
||||||
|
DEVICE_LOOKUP = {
|
||||||
|
'free-os': _lazy('Fully open mobile ecosystem'),
|
||||||
|
'free-desktop': _lazy('Windows, Mac and Linux'),
|
||||||
|
'free-phone': _lazy('Android smartphones'),
|
||||||
|
'free-tablet': _lazy('Android tablets'),
|
||||||
|
'paid-os': _lazy('Fully open mobile ecosystem'),
|
||||||
|
}
|
||||||
|
|
||||||
|
ERRORS = {'both': _lazy(u'Cannot be free and paid.'),
|
||||||
|
'none': _lazy(u'Please select a device.'),
|
||||||
|
'packaged': _lazy(u'Packaged apps are only valid '
|
||||||
|
u'for Firefox OS.')}
|
||||||
|
|
||||||
upload = forms.ModelChoiceField(widget=forms.HiddenInput,
|
upload = forms.ModelChoiceField(widget=forms.HiddenInput,
|
||||||
queryset=FileUpload.objects.filter(valid=True),
|
queryset=FileUpload.objects.filter(valid=True),
|
||||||
error_messages={'invalid_choice': _lazy(u'There was an error with your'
|
error_messages={'invalid_choice': _lazy(u'There was an error with your'
|
||||||
' upload. Please try again.')})
|
u' upload. Please try'
|
||||||
|
u' again.')})
|
||||||
|
|
||||||
def __init__(self, *args, **kw):
|
packaged = forms.BooleanField(required=False)
|
||||||
self.addon = kw.pop('addon', None)
|
free = forms.MultipleChoiceField(choices=FREE, required=False)
|
||||||
self.is_packaged = kw.pop('is_packaged', False)
|
paid = forms.MultipleChoiceField(choices=PAID, required=False)
|
||||||
super(NewWebappForm, self).__init__(*args, **kw)
|
|
||||||
|
|
||||||
|
def _add_error(self, msg):
|
||||||
|
self._errors['free'] = self._errors['paid'] = self.ERRORS[msg]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_combined(self):
|
||||||
|
return set(self.cleaned_data.get('free', []) +
|
||||||
|
self.cleaned_data.get('paid', []))
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
data = self.cleaned_data
|
||||||
|
|
||||||
|
# Check that they didn't select both.
|
||||||
|
if data.get('free') and data.get('paid'):
|
||||||
|
self._add_error('both')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check that they selected one.
|
||||||
|
if not data.get('free') and not data.get('paid'):
|
||||||
|
self._add_error('none')
|
||||||
|
self._errors['free'] = self._errors['paid'] = self.ERRORS['none']
|
||||||
|
return
|
||||||
|
|
||||||
|
# Packaged apps are only valid for firefox os.
|
||||||
|
if self.is_packaged():
|
||||||
|
if not set(self._get_combined()).issubset(['paid-os', 'free-os']):
|
||||||
|
self._add_error('packaged')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Now run the packaged app check, done in clean, because
|
||||||
|
# clean_packaged needs to be processed first.
|
||||||
|
try:
|
||||||
|
pkg = parse_addon(data['upload'], self.addon)
|
||||||
|
except forms.ValidationError, e:
|
||||||
|
self._errors['upload'] = self.error_class(e.messages)
|
||||||
|
return
|
||||||
|
|
||||||
def clean_upload(self):
|
|
||||||
upload = self.cleaned_data['upload']
|
|
||||||
if self.is_packaged:
|
|
||||||
pkg = parse_addon(upload, self.addon)
|
|
||||||
ver = pkg.get('version')
|
ver = pkg.get('version')
|
||||||
if (ver and self.addon and
|
if (ver and self.addon and
|
||||||
self.addon.versions.filter(version=ver).exists()):
|
self.addon.versions.filter(version=ver).exists()):
|
||||||
raise forms.ValidationError(
|
self._errors['upload'] = _(u'Version %s already exists') % ver
|
||||||
_(u'Version %s already exists') % ver)
|
return
|
||||||
else:
|
else:
|
||||||
# Throw an error if this is a dupe.
|
# Throw an error if this is a dupe.
|
||||||
# (JS sets manifest as `upload.name`.)
|
# (JS sets manifest as `upload.name`.)
|
||||||
verify_app_domain(upload.name)
|
try:
|
||||||
return upload
|
verify_app_domain(data['upload'].name)
|
||||||
|
except forms.ValidationError, e:
|
||||||
|
self._errors['upload'] = self.error_class(e.messages)
|
||||||
|
return
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_devices(self):
|
||||||
|
"""Returns a device based on the requested free or paid."""
|
||||||
|
platforms = {'os': amo.DEVICE_MOBILE,
|
||||||
|
'desktop': amo.DEVICE_DESKTOP,
|
||||||
|
'phone': amo.DEVICE_MOBILE,
|
||||||
|
'tablet': amo.DEVICE_TABLET}
|
||||||
|
return [platforms[t.split('-', 1)[1]] for t in self._get_combined()]
|
||||||
|
|
||||||
|
def get_paid(self):
|
||||||
|
"""Returns the premium type."""
|
||||||
|
if self.cleaned_data.get('paid', False):
|
||||||
|
return amo.ADDON_PREMIUM
|
||||||
|
return amo.ADDON_FREE
|
||||||
|
|
||||||
|
def is_packaged(self):
|
||||||
|
return self.cleaned_data.get('packaged', False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kw):
|
||||||
|
self.addon = kw.pop('addon', None)
|
||||||
|
super(NewWebappForm, self).__init__(*args, **kw)
|
||||||
|
if not waffle.switch_is_active('allow-b2g-paid-submission'):
|
||||||
|
del self.fields['paid']
|
||||||
|
|
||||||
|
if not waffle.switch_is_active('allow-packaged-app-uploads'):
|
||||||
|
del self.fields['packaged']
|
||||||
|
|
||||||
|
|
||||||
class PaypalSetupForm(happyforms.Form):
|
class PaypalSetupForm(happyforms.Form):
|
||||||
|
@ -87,13 +182,6 @@ class PaypalSetupForm(happyforms.Form):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class PremiumTypeForm(happyforms.Form):
|
|
||||||
premium_type = forms.TypedChoiceField(coerce=lambda x: int(x),
|
|
||||||
choices=amo.ADDON_PREMIUM_TYPES.items(),
|
|
||||||
widget=forms.RadioSelect(),
|
|
||||||
label=_lazy(u'Will your app use payments?'))
|
|
||||||
|
|
||||||
|
|
||||||
class UpsellForm(happyforms.Form):
|
class UpsellForm(happyforms.Form):
|
||||||
price = forms.ModelChoiceField(queryset=Price.objects.active(),
|
price = forms.ModelChoiceField(queryset=Price.objects.active(),
|
||||||
label=_lazy(u'App Price'),
|
label=_lazy(u'App Price'),
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
{% extends 'developers/base_impala.html' %}
|
|
||||||
|
|
||||||
{% set hosted_url = 'https://developer.mozilla.org/en-US/docs/Apps/Manifest' %}
|
|
||||||
{# TODO(bug 7884448): Change this link when that bug gets closed. #}
|
|
||||||
{% set packaged_url = 'https://bugzilla.mozilla.org/show_bug.cgi?id=784448' %}
|
|
||||||
|
|
||||||
{% set title = _('Submit an App') %}
|
|
||||||
|
|
||||||
{% block title %}{{ hub_page_title(title) }}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
|
|
||||||
<header class="submit-header c">
|
|
||||||
<h1 class="submit">{{ _('Submit an App') }}</h1>
|
|
||||||
{{ progress(request, addon=None, step=step) }}
|
|
||||||
</header>
|
|
||||||
<section id="submit-choose" class="primary">
|
|
||||||
<h2>{{ _('Choose an App Type') }}</h2>
|
|
||||||
<section id="upload-file" class="c">
|
|
||||||
<div class="island">
|
|
||||||
<p>
|
|
||||||
{% trans url=url('submit.app.manifest') %}
|
|
||||||
If the app is <b>hosted on your own server</b>,
|
|
||||||
then your app type is<br>
|
|
||||||
<a href="{{ url }}" class="button">Hosted</a>
|
|
||||||
{% endtrans %}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{% trans %}
|
|
||||||
<b>What's next</b>: submit your app manifest URL
|
|
||||||
{% endtrans %}
|
|
||||||
</p>
|
|
||||||
<p class="learn-mdn"><a href="{{ hosted_url }}" target="_blank">
|
|
||||||
{% trans %}
|
|
||||||
Learn more about <b>app manifests</b> on MDN.
|
|
||||||
{% endtrans %}
|
|
||||||
</a></p>
|
|
||||||
{# TODO(bug 784448): Remove when other a.soon is gone. #}
|
|
||||||
<span class="soon"> </a>
|
|
||||||
</div>
|
|
||||||
<div class="island">
|
|
||||||
<p>
|
|
||||||
{% trans url=url('submit.app.package') %}
|
|
||||||
If the app is going to be <b>hosted by Mozilla</b>,
|
|
||||||
then your app type is<br>
|
|
||||||
<a href="{{ url }}" class="button">Packaged</a>
|
|
||||||
{% endtrans %}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{% trans %}
|
|
||||||
<b>What's next</b>: upload your packaged app
|
|
||||||
{% endtrans %}
|
|
||||||
</p>
|
|
||||||
<p class="learn-mdn"><a href="{{ packaged_url }}" target="_blank">
|
|
||||||
{% trans %}
|
|
||||||
Learn more about <b>packaged apps</b> on MDN.
|
|
||||||
{% endtrans %}
|
|
||||||
</a></p>
|
|
||||||
<a href="{{ packaged_url }}" class="soon">Coming soon</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<div class="island full">
|
|
||||||
<h2>{{ _('Frequently Asked Questions') }} <a href="{{ packaged_url }}" class="soon">Coming soon</a></h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ packaged_url }}">What are the differences between a Hosted App and a Packaged App?</a></li>
|
|
||||||
<li><a href="{{ packaged_url }}">Hosted App submission checklist</a></li>
|
|
||||||
<li><a href="{{ packaged_url }}">Packaged App submission checklist</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</secion>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
|
@ -6,24 +6,56 @@
|
||||||
|
|
||||||
{% block title %}{{ hub_page_title(title) }}{% endblock %}
|
{% block title %}{{ hub_page_title(title) }}{% endblock %}
|
||||||
|
|
||||||
|
{% macro button(form, item) %}
|
||||||
|
<div class="wrapper">
|
||||||
|
<a href="#" class="island choice{{ ' selected' if form.data[item[0]] == 'on' else '' }}"
|
||||||
|
id="{{ item[0] }}" data-value="{{ item[0] }}">
|
||||||
|
<h3>{{ item[1] }}</h3>
|
||||||
|
<div>{{ form.DEVICE_LOOKUP[item[0]] }}</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
|
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
|
||||||
<header class="submit-header c">
|
<header class="submit-header c">
|
||||||
<h1>{{ _('Submit an App') }}</h1>
|
<h1>{{ _('Submit an App') }}</h1>
|
||||||
{{ progress(request, addon=None, step=step) }}
|
{{ progress(request, addon=None, step=step) }}
|
||||||
</header>
|
</header>
|
||||||
<section id="submit-manifest" class="primary">
|
|
||||||
<h2><a href="{{ doc_url }}">{{ _("Where's Your Manifest?") }}</a></h2>
|
|
||||||
<p>
|
<section id="submit-payment-type" class="island tabbable">
|
||||||
{% trans %}
|
<div class="free tab active">
|
||||||
Kick off things by creating your app's manifest and entering its URL
|
{% if waffle.switch('allow-b2g-paid-submission') %}
|
||||||
below. <a href="{{ doc_url }}" target="_blank">Learn about manifests.</a>
|
<h2><a href="#">{{ _('Free') }}</a></h2>
|
||||||
{% endtrans %}
|
{% else %}
|
||||||
</p>
|
<h2>{{ _('Device Type') }}</h2>
|
||||||
<section id="upload-file" class="island">
|
{% endif %}
|
||||||
|
<div class="error">{{ form.errors.free }}</div>
|
||||||
|
{% for item in form.fields['free'].choices %}
|
||||||
|
{{ button(form, item) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if waffle.switch('allow-b2g-paid-submission') %}
|
||||||
|
<div class="paid tab">
|
||||||
|
<h2><a href="#">{{ _('Paid') }}</a></h2>
|
||||||
|
<div class="error">{{ form.errors.paid }}</div>
|
||||||
|
{% for item in form.fields['paid'].choices %}
|
||||||
|
{{ button(form, item) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="upload-file" class="island tabbable {% if waffle.switch('allow-packaged-app-uploads') %}spacer{% endif %}">
|
||||||
|
<div class="hosted tab active">
|
||||||
|
{% if waffle.switch('allow-packaged-app-uploads') %}
|
||||||
|
<h2><a href="#">Hosted</a></h2>
|
||||||
|
{% endif %}
|
||||||
|
<h3>{{ _("Submit your app manifest URL") }}</h3>
|
||||||
<div class="upload-status">
|
<div class="upload-status">
|
||||||
<label>
|
<label>
|
||||||
{{ _('Submit your app manifest URL:') }}
|
|
||||||
<form id="validate-field">
|
<form id="validate-field">
|
||||||
<div class="vf-text">
|
<div class="vf-text">
|
||||||
<input type="text" id="upload-webapp-url" name="manifest" class="large"
|
<input type="text" id="upload-webapp-url" name="manifest" class="large"
|
||||||
|
@ -34,47 +66,58 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</label>
|
</label>
|
||||||
<div class="upload-details">
|
</div>
|
||||||
<div id="validate-error-protocol" class="pretty-tooltip tl">
|
<div class="upload-details">
|
||||||
<span class="protocol">
|
<div id="validate-error-protocol" class="pretty-tooltip tl">
|
||||||
{% trans http='http://', https='https://' %}
|
<span class="protocol">
|
||||||
<strong>Don't forget a protocol!</strong>
|
{% trans http='http://', https='https://' %}
|
||||||
Try adding either <a href="#">{{ http }}</a> or
|
<strong>Don't forget a protocol!</strong>
|
||||||
<a href="#">{{ https }}</a>.
|
Try adding either <a href="#">{{ http }}</a> or
|
||||||
{% endtrans %}
|
<a href="#">{{ https }}</a>.
|
||||||
</span>
|
{% endtrans %}
|
||||||
</div>
|
</span>
|
||||||
<div class="hint">
|
</div>
|
||||||
{{ _('Manifest URLs must start with a protocol (for example, '
|
<div class="hint">
|
||||||
'<code>http://</code> or <code>https://</code>) and '
|
{{ _('Manifest URLs must start with a protocol (for example, '
|
||||||
|
'<code>http://</code> or <code>https://</code>) and '
|
||||||
'typically use the <code>.webapp</code> extension.')|safe }}
|
'typically use the <code>.webapp</code> extension.')|safe }}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" id="upload-webapp">
|
</div>
|
||||||
{{ csrf() }}
|
|
||||||
<!--
|
<div class="packaged tab">
|
||||||
{{ form.non_field_errors() }}
|
{% if waffle.switch('allow-packaged-app-uploads') %}
|
||||||
{{ form.upload.errors }}
|
<h2><a href="#">Packaged</a></h2>
|
||||||
-->
|
<input type="file" id="upload-app" data-upload-url="{{ url('mkt.developers.upload') }}" />
|
||||||
<div class="hidden">
|
{% endif %}
|
||||||
{{ form.upload }}
|
</div>
|
||||||
</div>
|
|
||||||
<footer class="listing-footer hidden">
|
<form method="post" id="upload-webapp">
|
||||||
{% trans %}
|
{{ csrf() }}
|
||||||
<b>What's next:</b> fill out the rest of your app's details
|
<!--
|
||||||
{% endtrans %}
|
{{ form.non_field_errors() }}
|
||||||
<button class="upload-file-submit prominent" type="submit">
|
{{ form.upload.errors }}
|
||||||
{{ _('Continue') }}
|
-->
|
||||||
</button>
|
<div class="hidden">
|
||||||
</footer>
|
{{ form.upload }}
|
||||||
</form>
|
{{ form.free }}
|
||||||
</section>
|
{{ form.paid }}
|
||||||
{# TODO: Feel free to add more content, links to tips, sample manifests, builders. Please do. Really. #}
|
{{ form.packaged }}
|
||||||
<p class="learn-mdn"><a href="{{ doc_url }}" target="_blank">
|
</div>
|
||||||
{% trans %}
|
<footer class="listing-footer hidden">
|
||||||
Learn more about <b>app manifests</b> on MDN.
|
{% trans %}
|
||||||
{% endtrans %}
|
<b>What's next:</b> fill out the rest of your app's details
|
||||||
</a></p>
|
{% endtrans %}
|
||||||
|
<button class="upload-file-submit prominent" type="submit">
|
||||||
|
{{ _('Continue') }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<p class="learn-mdn"><a href="{{ doc_url }}" target="_blank">
|
||||||
|
{% trans %}Learn more about <b>app manifests</b> on MDN.{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
{% extends 'developers/base_impala.html' %}
|
|
||||||
|
|
||||||
{% set title = _('Upload Your App') %}
|
|
||||||
{% block title %}{{ hub_page_title(title) }}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{{ hub_breadcrumbs(items=[(None, title)]) }}
|
|
||||||
<header class="submit-header c">
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
{{ progress(request, addon=None, step=step) }}
|
|
||||||
</header>
|
|
||||||
<section id="submit-upload" class="primary">
|
|
||||||
<h2>{{ _("Where's Your Packaged App?") }}</h2>
|
|
||||||
<form method="post" id="create-addon" class="item">
|
|
||||||
{{ csrf() }}
|
|
||||||
<p>
|
|
||||||
{% trans %}
|
|
||||||
Use the fields below to upload your packaged app. After upload, a
|
|
||||||
series of automated validation tests will be run on your file.
|
|
||||||
{% endtrans %}
|
|
||||||
</p>
|
|
||||||
<section id="upload-file" class="island">
|
|
||||||
<div class="hidden">
|
|
||||||
{{ form.upload }}
|
|
||||||
</div>
|
|
||||||
<input type="file" id="upload-app" data-upload-url="{{ url('mkt.developers.upload') }}">
|
|
||||||
{{ form.non_field_errors() }}
|
|
||||||
{{ form.upload.errors }}
|
|
||||||
<div class="submission-buttons addon-submission-field">
|
|
||||||
<button class="prominent addon-upload-dependant" disabled id="submit-upload-file-finish" type="submit">
|
|
||||||
{{ _('Continue') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import mock
|
||||||
|
from nose.tools import eq_
|
||||||
|
|
||||||
|
import amo
|
||||||
|
import amo.tests
|
||||||
|
from files.models import FileUpload
|
||||||
|
from mkt.submit import forms
|
||||||
|
|
||||||
|
|
||||||
|
class TestNewWebappForm(amo.tests.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.file = FileUpload.objects.create(valid=True)
|
||||||
|
|
||||||
|
def test_not_free_or_paid(self):
|
||||||
|
form = forms.NewWebappForm({})
|
||||||
|
assert not form.is_valid()
|
||||||
|
eq_(form.ERRORS['none'], form.errors['free'])
|
||||||
|
eq_(form.ERRORS['none'], form.errors['paid'])
|
||||||
|
|
||||||
|
def test_not_paid(self):
|
||||||
|
form = forms.NewWebappForm({'paid': ['paid-os']})
|
||||||
|
assert not form.is_valid()
|
||||||
|
eq_(form.ERRORS['none'], form.errors['free'])
|
||||||
|
eq_(form.ERRORS['none'], form.errors['paid'])
|
||||||
|
|
||||||
|
def test_paid(self):
|
||||||
|
self.create_switch('allow-b2g-paid-submission')
|
||||||
|
form = forms.NewWebappForm({'paid': ['paid-os'],
|
||||||
|
'upload': self.file.uuid})
|
||||||
|
assert form.is_valid()
|
||||||
|
eq_(form.get_paid(), amo.ADDON_PREMIUM)
|
||||||
|
|
||||||
|
def test_free(self):
|
||||||
|
self.create_switch('allow-b2g-paid-submission')
|
||||||
|
form = forms.NewWebappForm({'free': ['free-os'],
|
||||||
|
'upload': self.file.uuid})
|
||||||
|
assert form.is_valid()
|
||||||
|
eq_(form.get_paid(), amo.ADDON_FREE)
|
||||||
|
|
||||||
|
def test_platform(self):
|
||||||
|
self.create_switch('allow-b2g-paid-submission')
|
||||||
|
for data, res in (
|
||||||
|
({'free': ['free-os']}, [amo.DEVICE_MOBILE]),
|
||||||
|
({'paid': ['paid-os']}, [amo.DEVICE_MOBILE]),
|
||||||
|
({'free': ['free-os',
|
||||||
|
'free-phone']}, [amo.DEVICE_MOBILE]),
|
||||||
|
({'free': ['free-phone',
|
||||||
|
'free-tablet']},
|
||||||
|
[amo.DEVICE_MOBILE, amo.DEVICE_TABLET]),
|
||||||
|
):
|
||||||
|
data['upload'] = self.file.uuid
|
||||||
|
form = forms.NewWebappForm(data)
|
||||||
|
assert form.is_valid(), form.errors
|
||||||
|
assert set(res) == set(form.get_devices())
|
||||||
|
|
||||||
|
def test_both(self):
|
||||||
|
self.create_switch('allow-b2g-paid-submission')
|
||||||
|
form = forms.NewWebappForm({'paid': ['paid-os'],
|
||||||
|
'free': ['free-os']})
|
||||||
|
assert not form.is_valid()
|
||||||
|
eq_(form.ERRORS['both'], form.errors['free'])
|
||||||
|
eq_(form.ERRORS['both'], form.errors['paid'])
|
||||||
|
|
||||||
|
def test_multiple(self):
|
||||||
|
form = forms.NewWebappForm({'free': ['free-os',
|
||||||
|
'free-desktop'],
|
||||||
|
'upload': self.file.uuid})
|
||||||
|
assert form.is_valid()
|
||||||
|
|
||||||
|
def test_not_packaged(self):
|
||||||
|
form = forms.NewWebappForm({'free': ['free-os'],
|
||||||
|
'upload': self.file.uuid,
|
||||||
|
'packaged': True})
|
||||||
|
assert form.is_valid(), form.errors
|
||||||
|
assert not form.is_packaged()
|
||||||
|
|
||||||
|
def test_not_packaged_allowed(self):
|
||||||
|
self.create_switch('allow-packaged-app-uploads')
|
||||||
|
form = forms.NewWebappForm({'free': ['free-os'],
|
||||||
|
'upload': self.file.uuid})
|
||||||
|
assert form.is_valid(), form.errors
|
||||||
|
assert not form.is_packaged()
|
||||||
|
|
||||||
|
@mock.patch('mkt.submit.forms.parse_addon')
|
||||||
|
def test_packaged_allowed(self, parse_addon):
|
||||||
|
self.create_switch('allow-packaged-app-uploads')
|
||||||
|
form = forms.NewWebappForm({'free': ['free-os'],
|
||||||
|
'upload': self.file.uuid,
|
||||||
|
'packaged': True})
|
||||||
|
assert form.is_valid()
|
||||||
|
assert form.is_packaged()
|
||||||
|
|
||||||
|
def test_packaged_wrong_device(self):
|
||||||
|
self.create_switch('allow-packaged-app-uploads')
|
||||||
|
form = forms.NewWebappForm({'free': ['free-desktop'],
|
||||||
|
'upload': self.file.uuid,
|
||||||
|
'packaged': True})
|
||||||
|
assert not form.is_valid(), form.errors
|
||||||
|
eq_(form.ERRORS['packaged'], form.errors['paid'])
|
|
@ -124,20 +124,16 @@ class TestTerms(TestSubmit):
|
||||||
self._test_progress_display([], 'terms')
|
self._test_progress_display([], 'terms')
|
||||||
|
|
||||||
def test_agree(self):
|
def test_agree(self):
|
||||||
self.create_switch(name='allow-packaged-app-uploads')
|
self.client.post(self.url, {'read_dev_agreement': True})
|
||||||
r = self.client.post(self.url, {'read_dev_agreement': True})
|
|
||||||
self.assert3xx(r, reverse('submit.app.choose'))
|
|
||||||
dt = self.get_user().read_dev_agreement
|
dt = self.get_user().read_dev_agreement
|
||||||
assert close_to_now(dt), (
|
assert close_to_now(dt), (
|
||||||
'Expected date of agreement read to be close to now. Was %s' % dt)
|
'Expected date of agreement read to be close to now. Was %s' % dt)
|
||||||
eq_(UserNotification.objects.count(), 0)
|
eq_(UserNotification.objects.count(), 0)
|
||||||
|
|
||||||
def test_agree_and_sign_me_up(self):
|
def test_agree_and_sign_me_up(self):
|
||||||
self.create_switch(name='allow-packaged-app-uploads')
|
self.client.post(self.url, {'read_dev_agreement':
|
||||||
r = self.client.post(self.url, {'read_dev_agreement':
|
datetime.datetime.now(),
|
||||||
datetime.datetime.now(),
|
'newsletter': True})
|
||||||
'newsletter': True})
|
|
||||||
self.assert3xx(r, reverse('submit.app.choose'))
|
|
||||||
dt = self.get_user().read_dev_agreement
|
dt = self.get_user().read_dev_agreement
|
||||||
assert close_to_now(dt), (
|
assert close_to_now(dt), (
|
||||||
'Expected date of agreement read to be close to now. Was %s' % dt)
|
'Expected date of agreement read to be close to now. Was %s' % dt)
|
||||||
|
@ -190,16 +186,12 @@ class TestManifest(TestSubmit):
|
||||||
# So jump me to the Manifest step.
|
# So jump me to the Manifest step.
|
||||||
r = self.client.get(reverse('submit.app'), follow=True)
|
r = self.client.get(reverse('submit.app'), follow=True)
|
||||||
self.assert3xx(r, reverse('submit.app.manifest'))
|
self.assert3xx(r, reverse('submit.app.manifest'))
|
||||||
# Now with waffles!
|
|
||||||
self.create_switch(name='allow-packaged-app-uploads')
|
|
||||||
r = self.client.get(reverse('submit.app'), follow=True)
|
|
||||||
self.assert3xx(r, reverse('submit.app.choose'))
|
|
||||||
|
|
||||||
def test_page(self):
|
def test_page(self):
|
||||||
self._step()
|
self._step()
|
||||||
r = self.client.get(self.url)
|
r = self.client.get(self.url)
|
||||||
eq_(r.status_code, 200)
|
eq_(r.status_code, 200)
|
||||||
eq_(pq(r.content)('#submit-manifest').length, 1)
|
eq_(pq(r.content)('#upload-file').length, 1)
|
||||||
|
|
||||||
def test_progress_display(self):
|
def test_progress_display(self):
|
||||||
self._step()
|
self._step()
|
||||||
|
@ -208,12 +200,11 @@ class TestManifest(TestSubmit):
|
||||||
|
|
||||||
class UploadAddon(object):
|
class UploadAddon(object):
|
||||||
|
|
||||||
def post(self, desktop_platforms=[amo.PLATFORM_ALL], mobile_platforms=[],
|
def post(self, expect_errors=False, data=None):
|
||||||
expect_errors=False):
|
if data is None:
|
||||||
d = dict(upload=self.upload.pk,
|
data = {'free': ['free-desktop']}
|
||||||
desktop_platforms=[p.id for p in desktop_platforms],
|
data.update(upload=self.upload.pk)
|
||||||
mobile_platforms=[p.id for p in mobile_platforms])
|
r = self.client.post(self.url, data, follow=True)
|
||||||
r = self.client.post(self.url, d, follow=True)
|
|
||||||
eq_(r.status_code, 200)
|
eq_(r.status_code, 200)
|
||||||
if not expect_errors:
|
if not expect_errors:
|
||||||
# Show any unexpected form errors.
|
# Show any unexpected form errors.
|
||||||
|
@ -238,9 +229,9 @@ class BaseWebAppTest(BaseUploadTest, UploadAddon, amo.tests.TestCase):
|
||||||
self.client.post(reverse('submit.app.terms'),
|
self.client.post(reverse('submit.app.terms'),
|
||||||
{'read_dev_agreement': True})
|
{'read_dev_agreement': True})
|
||||||
|
|
||||||
def post_addon(self):
|
def post_addon(self, data=None):
|
||||||
eq_(Addon.objects.count(), 0)
|
eq_(Addon.objects.count(), 0)
|
||||||
self.post()
|
self.post(data=data)
|
||||||
return Addon.objects.get()
|
return Addon.objects.get()
|
||||||
|
|
||||||
|
|
||||||
|
@ -320,6 +311,20 @@ class TestCreateWebApp(BaseWebAppTest):
|
||||||
eq_(len(files), 1)
|
eq_(len(files), 1)
|
||||||
eq_(files[0].status, amo.STATUS_PENDING)
|
eq_(files[0].status, amo.STATUS_PENDING)
|
||||||
|
|
||||||
|
def test_set_platform(self):
|
||||||
|
app = self.post_addon({'free': ['free-tablet', 'free-desktop']})
|
||||||
|
eq_(set(app.device_types),
|
||||||
|
set([amo.DEVICE_TABLET, amo.DEVICE_DESKTOP]))
|
||||||
|
|
||||||
|
def test_free(self):
|
||||||
|
app = self.post_addon({'free': ['free-os']})
|
||||||
|
eq_(app.premium_type, amo.ADDON_FREE)
|
||||||
|
|
||||||
|
def test_premium(self):
|
||||||
|
self.create_switch('allow-b2g-paid-submission')
|
||||||
|
app = self.post_addon({'paid': ['paid-os']})
|
||||||
|
eq_(app.premium_type, amo.ADDON_PREMIUM)
|
||||||
|
|
||||||
|
|
||||||
class TestCreateWebAppFromManifest(BaseWebAppTest):
|
class TestCreateWebAppFromManifest(BaseWebAppTest):
|
||||||
|
|
||||||
|
@ -380,16 +385,16 @@ class BasePackagedAppTest(BaseUploadTest, UploadAddon, amo.tests.TestCase):
|
||||||
self.package = self.packaged_app_path('mozball.zip')
|
self.package = self.packaged_app_path('mozball.zip')
|
||||||
self.upload = self.get_upload(abspath=self.package)
|
self.upload = self.get_upload(abspath=self.package)
|
||||||
self.upload.update(name='mozball.zip', is_webapp=True)
|
self.upload.update(name='mozball.zip', is_webapp=True)
|
||||||
self.url = reverse('submit.app.package')
|
self.url = reverse('submit.app.manifest')
|
||||||
assert self.client.login(username='regular@mozilla.com',
|
assert self.client.login(username='regular@mozilla.com',
|
||||||
password='password')
|
password='password')
|
||||||
# Complete first step.
|
# Complete first step.
|
||||||
self.client.post(reverse('submit.app.terms'),
|
self.client.post(reverse('submit.app.terms'),
|
||||||
{'read_dev_agreement': True})
|
{'read_dev_agreement': True})
|
||||||
|
|
||||||
def post_addon(self):
|
def post_addon(self, data=None):
|
||||||
eq_(Addon.objects.count(), 1)
|
eq_(Addon.objects.count(), 1)
|
||||||
self.post()
|
self.post(data=data)
|
||||||
return Addon.objects.order_by('-id')[0]
|
return Addon.objects.order_by('-id')[0]
|
||||||
|
|
||||||
def setup_files(self):
|
def setup_files(self):
|
||||||
|
@ -413,7 +418,7 @@ class TestCreatePackagedApp(BasePackagedAppTest):
|
||||||
reverse('submit.app.details', args=[webapp.app_slug]))
|
reverse('submit.app.details', args=[webapp.app_slug]))
|
||||||
|
|
||||||
def test_app_from_uploaded_package(self):
|
def test_app_from_uploaded_package(self):
|
||||||
addon = self.post_addon()
|
addon = self.post_addon(data={'packaged': True, 'free': ['free-os']})
|
||||||
eq_(addon.type, amo.ADDON_WEBAPP)
|
eq_(addon.type, amo.ADDON_WEBAPP)
|
||||||
eq_(addon.current_version.version, '1.0')
|
eq_(addon.current_version.version, '1.0')
|
||||||
eq_(addon.is_packaged, True)
|
eq_(addon.is_packaged, True)
|
||||||
|
@ -429,7 +434,7 @@ class TestCreatePackagedApp(BasePackagedAppTest):
|
||||||
|
|
||||||
@mock.patch('mkt.submit.forms.verify_app_domain')
|
@mock.patch('mkt.submit.forms.verify_app_domain')
|
||||||
def test_packaged_app_not_unique_by_domain(self, _verify):
|
def test_packaged_app_not_unique_by_domain(self, _verify):
|
||||||
self.post()
|
self.post(data={'packaged': True, 'free': ['free-os']})
|
||||||
assert not _verify.called, ('`verify_app_domain` should not be called'
|
assert not _verify.called, ('`verify_app_domain` should not be called'
|
||||||
' for packaged apps.')
|
' for packaged apps.')
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,6 @@ urlpatterns = decorate(write, patterns('',
|
||||||
url('^app$', views.submit, name='submit.app'),
|
url('^app$', views.submit, name='submit.app'),
|
||||||
url('^app/proceed$', views.proceed, name='submit.app.proceed'),
|
url('^app/proceed$', views.proceed, name='submit.app.proceed'),
|
||||||
url('^app/terms$', views.terms, name='submit.app.terms'),
|
url('^app/terms$', views.terms, name='submit.app.terms'),
|
||||||
url('^app/choose$', views.choose, name='submit.app.choose'),
|
|
||||||
url('^app/manifest$', views.manifest, name='submit.app.manifest'),
|
url('^app/manifest$', views.manifest, name='submit.app.manifest'),
|
||||||
url('^app/package$', views.package, name='submit.app.package'),
|
|
||||||
('^app/', include(submit_apps_patterns)),
|
('^app/', include(submit_apps_patterns)),
|
||||||
))
|
))
|
||||||
|
|
|
@ -37,8 +37,6 @@ def submit(request):
|
||||||
# If dev has already agreed, continue to next step.
|
# If dev has already agreed, continue to next step.
|
||||||
user = UserProfile.objects.get(pk=request.user.id)
|
user = UserProfile.objects.get(pk=request.user.id)
|
||||||
if user.read_dev_agreement:
|
if user.read_dev_agreement:
|
||||||
if waffle.switch_is_active('allow-packaged-app-uploads'):
|
|
||||||
return redirect('submit.app.choose')
|
|
||||||
return redirect('submit.app.manifest')
|
return redirect('submit.app.manifest')
|
||||||
else:
|
else:
|
||||||
return redirect('submit.app.terms')
|
return redirect('submit.app.terms')
|
||||||
|
@ -66,8 +64,6 @@ def terms(request):
|
||||||
# If dev has already agreed, continue to next step.
|
# If dev has already agreed, continue to next step.
|
||||||
if (getattr(request, 'amo_user', None) and
|
if (getattr(request, 'amo_user', None) and
|
||||||
request.amo_user.read_dev_agreement):
|
request.amo_user.read_dev_agreement):
|
||||||
if waffle.switch_is_active('allow-packaged-app-uploads'):
|
|
||||||
return redirect('submit.app.choose')
|
|
||||||
return redirect('submit.app.manifest')
|
return redirect('submit.app.manifest')
|
||||||
|
|
||||||
agreement_form = forms.DevAgreementForm(
|
agreement_form = forms.DevAgreementForm(
|
||||||
|
@ -75,8 +71,6 @@ def terms(request):
|
||||||
instance=request.amo_user)
|
instance=request.amo_user)
|
||||||
if request.POST and agreement_form.is_valid():
|
if request.POST and agreement_form.is_valid():
|
||||||
agreement_form.save()
|
agreement_form.save()
|
||||||
if waffle.switch_is_active('allow-packaged-app-uploads'):
|
|
||||||
return redirect('submit.app.choose')
|
|
||||||
return redirect('submit.app.manifest')
|
return redirect('submit.app.manifest')
|
||||||
return jingo.render(request, 'submit/terms.html', {
|
return jingo.render(request, 'submit/terms.html', {
|
||||||
'step': 'terms',
|
'step': 'terms',
|
||||||
|
@ -84,27 +78,27 @@ def terms(request):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@read_dev_agreement_required
|
|
||||||
@submit_step('manifest')
|
|
||||||
def choose(request):
|
|
||||||
if not waffle.switch_is_active('allow-packaged-app-uploads'):
|
|
||||||
return redirect('submit.app.manifest')
|
|
||||||
return jingo.render(request, 'submit/choose.html', {
|
|
||||||
'step': 'manifest',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@read_dev_agreement_required
|
@read_dev_agreement_required
|
||||||
@submit_step('manifest')
|
@submit_step('manifest')
|
||||||
@transaction.commit_on_success
|
@transaction.commit_on_success
|
||||||
def manifest(request):
|
def manifest(request):
|
||||||
form = forms.NewWebappForm(request.POST or None)
|
form = forms.NewWebappForm(request.POST or None)
|
||||||
|
|
||||||
if request.method == 'POST' and form.is_valid():
|
if request.method == 'POST' and form.is_valid():
|
||||||
addon = Addon.from_upload(
|
addon = Addon.from_upload(
|
||||||
form.cleaned_data['upload'],
|
form.cleaned_data['upload'],
|
||||||
[Platform.objects.get(id=amo.PLATFORM_ALL.id)])
|
[Platform.objects.get(id=amo.PLATFORM_ALL.id)],
|
||||||
|
is_packaged=form.is_packaged())
|
||||||
|
|
||||||
|
# Set the device type.
|
||||||
|
for device in form.get_devices():
|
||||||
|
addon.addondevicetype_set.create(device_type=device.id)
|
||||||
|
|
||||||
|
# Set the premium type, only bother if it's not free.
|
||||||
|
premium = form.get_paid()
|
||||||
|
if premium:
|
||||||
|
addon.update(premium_type=premium)
|
||||||
|
|
||||||
if addon.has_icon_in_manifest():
|
if addon.has_icon_in_manifest():
|
||||||
# Fetch the icon, do polling.
|
# Fetch the icon, do polling.
|
||||||
|
@ -123,37 +117,7 @@ def manifest(request):
|
||||||
|
|
||||||
return jingo.render(request, 'submit/manifest.html', {
|
return jingo.render(request, 'submit/manifest.html', {
|
||||||
'step': 'manifest',
|
'step': 'manifest',
|
||||||
'form': form,
|
'form': form
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@read_dev_agreement_required
|
|
||||||
@submit_step('manifest')
|
|
||||||
def package(request):
|
|
||||||
form = forms.NewWebappForm(request.POST or None, is_packaged=True)
|
|
||||||
if request.method == 'POST' and form.is_valid():
|
|
||||||
addon = Addon.from_upload(
|
|
||||||
form.cleaned_data['upload'],
|
|
||||||
[Platform.objects.get(id=amo.PLATFORM_ALL.id)], is_packaged=True)
|
|
||||||
|
|
||||||
if addon.has_icon_in_manifest():
|
|
||||||
# Fetch the icon, do polling.
|
|
||||||
addon.update(icon_type='image/png')
|
|
||||||
tasks.fetch_icon.delay(addon)
|
|
||||||
else:
|
|
||||||
# In this case there is no need to do any polling.
|
|
||||||
addon.update(icon_type='')
|
|
||||||
|
|
||||||
AddonUser(addon=addon, user=request.amo_user).save()
|
|
||||||
AppSubmissionChecklist.objects.create(addon=addon, terms=True,
|
|
||||||
manifest=True)
|
|
||||||
|
|
||||||
return redirect('submit.app.details', addon.app_slug)
|
|
||||||
|
|
||||||
return jingo.render(request, 'submit/upload.html', {
|
|
||||||
'form': form,
|
|
||||||
'step': 'manifest',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче