merge in packaged uploads, device and payment choices to submission (bug 778882)

This commit is contained in:
Andy McKay 2012-10-23 17:37:02 -07:00
Родитель 48146b9673
Коммит 1e89fb6fd5
20 изменённых файлов: 550 добавлений и 258 удалений

Просмотреть файл

@ -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;
}
}
}

Двоичные данные
media/img/developers/firefox_android_phone.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.7 KiB

Двоичные данные
media/img/developers/firefox_android_tablet.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 10 KiB

Двоичные данные
media/img/developers/firefox_logo.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 31 KiB

Двоичные данные
media/img/developers/firefox_phone.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 31 KiB

Просмотреть файл

@ -212,6 +212,8 @@
$upload_field.trigger('upload_success_results', [file, results]);
}};
$('#id_upload').val(results.upload);
$('#id_packaged').val(true);
upload_progress_inside.animate({'width': '100%'}, animateArgs);
});
@ -303,8 +305,10 @@
upload_progress_outside.attr('class', 'bar-success');
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');
$('<strong>').text(message).appendTo(upload_results);

Просмотреть файл

@ -180,6 +180,7 @@ $(document).ready(function() {
function check_webapp_validation(results) {
var $upload_field = $('#upload-webapp-url');
$('#id_upload').val(results.upload);
$('#id_packaged').val('');
if(results.error) {
$upload_field.trigger("upload_finished", [false, results, results.error]);
} else if(! results.validation) {

Просмотреть файл

@ -1,6 +1,14 @@
(function(exports) {
"use strict";
function _pd(func) {
// Prevent-default function wrapper.
return function(e) {
e.preventDefault();
func.apply(this, arguments);
};
}
exports.houdini = function() {
// Initialize magic labels.
$(document).delegate('.houdini.ready .edit', 'click', _pd(function(e) {
@ -34,9 +42,63 @@
$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);

37
media/js/devreg/tabs.js Normal file
Просмотреть файл

@ -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/validation.less',
'css/devreg/submit.less',
'css/devreg/tabs.less',
'css/impala/personas.less',
'css/impala/colorpicker.less',
@ -206,6 +207,7 @@ JS = {
# New stuff.
'js/devreg/devhub.js',
'js/devreg/submit.js',
'js/devreg/tabs.js',
'js/devreg/edit.js',
'js/impala/persona_creation.js',
'js/lib/jquery.minicolors.js',

Просмотреть файл

@ -4,6 +4,7 @@ from django import forms
import happyforms
from tower import ugettext as _, ugettext_lazy as _lazy
import waffle
from addons.forms import AddonFormBasic
from addons.models import Addon, AddonUpsell
@ -38,31 +39,125 @@ class DevAgreementForm(happyforms.Form):
notification_id=app_surveys.id, update={'enabled': True})
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,
queryset=FileUpload.objects.filter(valid=True),
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):
self.addon = kw.pop('addon', None)
self.is_packaged = kw.pop('is_packaged', False)
super(NewWebappForm, self).__init__(*args, **kw)
packaged = forms.BooleanField(required=False)
free = forms.MultipleChoiceField(choices=FREE, required=False)
paid = forms.MultipleChoiceField(choices=PAID, required=False)
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')
if (ver and self.addon and
self.addon.versions.filter(version=ver).exists()):
raise forms.ValidationError(
_(u'Version %s already exists') % ver)
self._errors['upload'] = _(u'Version %s already exists') % ver
return
else:
# Throw an error if this is a dupe.
# (JS sets manifest as `upload.name`.)
verify_app_domain(upload.name)
return upload
try:
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):
@ -87,13 +182,6 @@ class PaypalSetupForm(happyforms.Form):
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):
price = forms.ModelChoiceField(queryset=Price.objects.active(),
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">&nbsp;</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 %}
{% 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 %}
{{ hub_breadcrumbs(items=[(None, _('Submit App'))]) }}
<header class="submit-header c">
<h1>{{ _('Submit an App') }}</h1>
{{ progress(request, addon=None, step=step) }}
</header>
<section id="submit-manifest" class="primary">
<h2><a href="{{ doc_url }}">{{ _("Where's Your Manifest?") }}</a></h2>
<p>
{% trans %}
Kick off things by creating your app's manifest and entering its URL
below. <a href="{{ doc_url }}" target="_blank">Learn about manifests.</a>
{% endtrans %}
</p>
<section id="upload-file" class="island">
<section id="submit-payment-type" class="island tabbable">
<div class="free tab active">
{% if waffle.switch('allow-b2g-paid-submission') %}
<h2><a href="#">{{ _('Free') }}</a></h2>
{% else %}
<h2>{{ _('Device Type') }}</h2>
{% 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">
<label>
{{ _('Submit your app manifest URL:') }}
<form id="validate-field">
<div class="vf-text">
<input type="text" id="upload-webapp-url" name="manifest" class="large"
@ -34,47 +66,58 @@
</div>
</form>
</label>
<div class="upload-details">
<div id="validate-error-protocol" class="pretty-tooltip tl">
<span class="protocol">
{% trans http='http://', https='https://' %}
<strong>Don't forget a protocol!</strong>
Try adding either <a href="#">{{ http }}</a> or
<a href="#">{{ https }}</a>.
{% endtrans %}
</span>
</div>
<div class="hint">
{{ _('Manifest URLs must start with a protocol (for example, '
'<code>http://</code> or <code>https://</code>) and '
</div>
<div class="upload-details">
<div id="validate-error-protocol" class="pretty-tooltip tl">
<span class="protocol">
{% trans http='http://', https='https://' %}
<strong>Don't forget a protocol!</strong>
Try adding either <a href="#">{{ http }}</a> or
<a href="#">{{ https }}</a>.
{% endtrans %}
</span>
</div>
<div class="hint">
{{ _('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 }}
</div>
</div>
</div>
<form method="post" id="upload-webapp">
{{ csrf() }}
<!--
{{ form.non_field_errors() }}
{{ form.upload.errors }}
-->
<div class="hidden">
{{ form.upload }}
</div>
<footer class="listing-footer hidden">
{% trans %}
<b>What's next:</b> fill out the rest of your app's details
{% endtrans %}
<button class="upload-file-submit prominent" type="submit">
{{ _('Continue') }}
</button>
</footer>
</form>
</section>
{# TODO: Feel free to add more content, links to tips, sample manifests, builders. Please do. Really. #}
<p class="learn-mdn"><a href="{{ doc_url }}" target="_blank">
{% trans %}
Learn more about <b>app manifests</b> on MDN.
{% endtrans %}
</a></p>
</div>
<div class="packaged tab">
{% if waffle.switch('allow-packaged-app-uploads') %}
<h2><a href="#">Packaged</a></h2>
<input type="file" id="upload-app" data-upload-url="{{ url('mkt.developers.upload') }}" />
{% endif %}
</div>
<form method="post" id="upload-webapp">
{{ csrf() }}
<!--
{{ form.non_field_errors() }}
{{ form.upload.errors }}
-->
<div class="hidden">
{{ form.upload }}
{{ form.free }}
{{ form.paid }}
{{ form.packaged }}
</div>
<footer class="listing-footer hidden">
{% trans %}
<b>What's next:</b> fill out the rest of your app's details
{% endtrans %}
<button class="upload-file-submit prominent" type="submit">
{{ _('Continue') }}
</button>
</footer>
</form>
</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 %}

Просмотреть файл

@ -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')
def test_agree(self):
self.create_switch(name='allow-packaged-app-uploads')
r = self.client.post(self.url, {'read_dev_agreement': True})
self.assert3xx(r, reverse('submit.app.choose'))
self.client.post(self.url, {'read_dev_agreement': True})
dt = self.get_user().read_dev_agreement
assert close_to_now(dt), (
'Expected date of agreement read to be close to now. Was %s' % dt)
eq_(UserNotification.objects.count(), 0)
def test_agree_and_sign_me_up(self):
self.create_switch(name='allow-packaged-app-uploads')
r = self.client.post(self.url, {'read_dev_agreement':
datetime.datetime.now(),
'newsletter': True})
self.assert3xx(r, reverse('submit.app.choose'))
self.client.post(self.url, {'read_dev_agreement':
datetime.datetime.now(),
'newsletter': True})
dt = self.get_user().read_dev_agreement
assert close_to_now(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.
r = self.client.get(reverse('submit.app'), follow=True)
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):
self._step()
r = self.client.get(self.url)
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):
self._step()
@ -208,12 +200,11 @@ class TestManifest(TestSubmit):
class UploadAddon(object):
def post(self, desktop_platforms=[amo.PLATFORM_ALL], mobile_platforms=[],
expect_errors=False):
d = dict(upload=self.upload.pk,
desktop_platforms=[p.id for p in desktop_platforms],
mobile_platforms=[p.id for p in mobile_platforms])
r = self.client.post(self.url, d, follow=True)
def post(self, expect_errors=False, data=None):
if data is None:
data = {'free': ['free-desktop']}
data.update(upload=self.upload.pk)
r = self.client.post(self.url, data, follow=True)
eq_(r.status_code, 200)
if not expect_errors:
# Show any unexpected form errors.
@ -238,9 +229,9 @@ class BaseWebAppTest(BaseUploadTest, UploadAddon, amo.tests.TestCase):
self.client.post(reverse('submit.app.terms'),
{'read_dev_agreement': True})
def post_addon(self):
def post_addon(self, data=None):
eq_(Addon.objects.count(), 0)
self.post()
self.post(data=data)
return Addon.objects.get()
@ -320,6 +311,20 @@ class TestCreateWebApp(BaseWebAppTest):
eq_(len(files), 1)
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):
@ -380,16 +385,16 @@ class BasePackagedAppTest(BaseUploadTest, UploadAddon, amo.tests.TestCase):
self.package = self.packaged_app_path('mozball.zip')
self.upload = self.get_upload(abspath=self.package)
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',
password='password')
# Complete first step.
self.client.post(reverse('submit.app.terms'),
{'read_dev_agreement': True})
def post_addon(self):
def post_addon(self, data=None):
eq_(Addon.objects.count(), 1)
self.post()
self.post(data=data)
return Addon.objects.order_by('-id')[0]
def setup_files(self):
@ -413,7 +418,7 @@ class TestCreatePackagedApp(BasePackagedAppTest):
reverse('submit.app.details', args=[webapp.app_slug]))
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.current_version.version, '1.0')
eq_(addon.is_packaged, True)
@ -429,7 +434,7 @@ class TestCreatePackagedApp(BasePackagedAppTest):
@mock.patch('mkt.submit.forms.verify_app_domain')
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'
' for packaged apps.')

Просмотреть файл

@ -31,8 +31,6 @@ urlpatterns = decorate(write, patterns('',
url('^app$', views.submit, name='submit.app'),
url('^app/proceed$', views.proceed, name='submit.app.proceed'),
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/package$', views.package, name='submit.app.package'),
('^app/', include(submit_apps_patterns)),
))

Просмотреть файл

@ -37,8 +37,6 @@ def submit(request):
# If dev has already agreed, continue to next step.
user = UserProfile.objects.get(pk=request.user.id)
if user.read_dev_agreement:
if waffle.switch_is_active('allow-packaged-app-uploads'):
return redirect('submit.app.choose')
return redirect('submit.app.manifest')
else:
return redirect('submit.app.terms')
@ -66,8 +64,6 @@ def terms(request):
# If dev has already agreed, continue to next step.
if (getattr(request, 'amo_user', None) and
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')
agreement_form = forms.DevAgreementForm(
@ -75,8 +71,6 @@ def terms(request):
instance=request.amo_user)
if request.POST and agreement_form.is_valid():
agreement_form.save()
if waffle.switch_is_active('allow-packaged-app-uploads'):
return redirect('submit.app.choose')
return redirect('submit.app.manifest')
return jingo.render(request, 'submit/terms.html', {
'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
@read_dev_agreement_required
@submit_step('manifest')
@transaction.commit_on_success
def manifest(request):
form = forms.NewWebappForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
addon = Addon.from_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():
# Fetch the icon, do polling.
@ -123,37 +117,7 @@ def manifest(request):
return jingo.render(request, 'submit/manifest.html', {
'step': 'manifest',
'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',
'form': form
})