Replace inline new version dialog with upload flow a-la new addon (#3940)
This commit is contained in:
Родитель
433c033979
Коммит
4b46b39506
|
@ -16,7 +16,7 @@ from quieter_formset.formset import BaseModelFormSet
|
|||
from olympia.access import acl
|
||||
from olympia import amo, paypal
|
||||
from olympia.amo.helpers import mark_safe_lazy
|
||||
from olympia.addons.forms import AddonFormBasic
|
||||
from olympia.addons.forms import AddonFormBase, clean_addon_name
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonDependency, AddonUser, Charity, Preview)
|
||||
from olympia.amo.fields import HttpHttpsOnlyURLField
|
||||
|
@ -530,23 +530,6 @@ class AddonUploadForm(WithSourceMixin, happyforms.Form):
|
|||
u'upload. Please try again.'))
|
||||
|
||||
|
||||
class NewAddonForm(AddonUploadForm):
|
||||
supported_platforms = forms.TypedMultipleChoiceField(
|
||||
choices=amo.SUPPORTED_PLATFORMS_CHOICES,
|
||||
widget=forms.CheckboxSelectMultiple(attrs={'class': 'platform'}),
|
||||
initial=[amo.PLATFORM_ALL.id],
|
||||
coerce=int,
|
||||
error_messages={'required': 'Need at least one platform.'}
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if not self.errors:
|
||||
self._clean_upload()
|
||||
# parse and validate the add-on
|
||||
parse_addon(self.cleaned_data['upload'])
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class StandaloneValidationForm(AddonUploadForm):
|
||||
is_unlisted = forms.BooleanField(
|
||||
initial=False,
|
||||
|
@ -557,7 +540,15 @@ class StandaloneValidationForm(AddonUploadForm):
|
|||
u'your own and only need it to be signed by Mozilla.'))
|
||||
|
||||
|
||||
class NewVersionForm(NewAddonForm):
|
||||
class NewVersionForm(AddonUploadForm):
|
||||
supported_platforms = forms.TypedMultipleChoiceField(
|
||||
choices=amo.SUPPORTED_PLATFORMS_CHOICES,
|
||||
widget=forms.CheckboxSelectMultiple(attrs={'class': 'platform'}),
|
||||
initial=[amo.PLATFORM_ALL.id],
|
||||
coerce=int,
|
||||
error_messages={'required': 'Need at least one platform.'}
|
||||
)
|
||||
|
||||
beta = forms.BooleanField(
|
||||
required=False,
|
||||
help_text=_lazy(u'A file with a version ending with '
|
||||
|
@ -565,7 +556,7 @@ class NewVersionForm(NewAddonForm):
|
|||
u'detected as beta.'))
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
self.addon = kw.pop('addon')
|
||||
self.addon = kw.pop('addon', None)
|
||||
super(NewVersionForm, self).__init__(*args, **kw)
|
||||
|
||||
def clean(self):
|
||||
|
@ -573,7 +564,7 @@ class NewVersionForm(NewAddonForm):
|
|||
self._clean_upload()
|
||||
xpi = parse_addon(self.cleaned_data['upload'], self.addon)
|
||||
# Make sure we don't already have the same non-rejected version.
|
||||
version_exists = Version.unfiltered.filter(
|
||||
version_exists = self.addon and Version.unfiltered.filter(
|
||||
addon=self.addon, version=xpi['version']).exists()
|
||||
if version_exists:
|
||||
msg = _(u'Version %s already exists, or was uploaded before.')
|
||||
|
@ -685,24 +676,45 @@ FileFormSet = modelformset_factory(File, formset=BaseFileFormSet,
|
|||
form=FileForm, can_delete=True, extra=0)
|
||||
|
||||
|
||||
class DescribeForm(AddonFormBasic):
|
||||
tags = None
|
||||
class DescribeForm(AddonFormBase):
|
||||
name = TransField(max_length=50)
|
||||
slug = forms.CharField(max_length=30)
|
||||
summary = TransField(widget=TransTextarea(attrs={'rows': 4}),
|
||||
max_length=250)
|
||||
is_experimental = forms.BooleanField(required=False)
|
||||
support_url = TransField.adapt(HttpHttpsOnlyURLField)(required=False)
|
||||
support_email = TransField.adapt(forms.EmailField)(required=False)
|
||||
has_priv = forms.BooleanField(
|
||||
required=False, label=_lazy(u"This add-on has a Privacy Policy"),
|
||||
label_suffix='')
|
||||
privacy_policy = TransField(
|
||||
widget=TransTextarea(), required=False,
|
||||
label=_lazy(u"Please specify your add-on's Privacy Policy:"))
|
||||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('name', 'slug', 'summary', 'is_experimental', 'support_url',
|
||||
'support_email')
|
||||
'support_email', 'privacy_policy')
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
kw['initial'] = {
|
||||
'has_priv': self._has_field('privacy_policy', kw['instance'])}
|
||||
super(DescribeForm, self).__init__(*args, **kw)
|
||||
|
||||
class ReviewerNotesForm(happyforms.ModelForm):
|
||||
approvalnotes = forms.CharField(
|
||||
widget=TranslationTextarea(attrs={'rows': 4}), required=False)
|
||||
def clean_name(self):
|
||||
return clean_addon_name(self.cleaned_data['name'], self.instance)
|
||||
|
||||
class Meta:
|
||||
model = Version
|
||||
fields = ('approvalnotes',)
|
||||
def _has_field(self, name, instance=None):
|
||||
# If there's a policy in any language, this addon has a policy.
|
||||
n = getattr(instance or self.instance, u'%s_id' % name)
|
||||
return any(map(bool, Translation.objects.filter(id=n)))
|
||||
|
||||
def save(self, commit=True):
|
||||
obj = super(DescribeForm, self).save(commit)
|
||||
if not self.cleaned_data['has_priv']:
|
||||
delete_translation(self.instance, 'privacy_policy')
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class PreviewForm(happyforms.ModelForm):
|
||||
|
@ -840,7 +852,7 @@ class DistributionChoiceForm(happyforms.Form):
|
|||
u'self-distribution. Updates should be handled by you via an '
|
||||
u'updateURL or external application updates.</span>')
|
||||
|
||||
choices = forms.ChoiceField(
|
||||
channel = forms.ChoiceField(
|
||||
choices=(
|
||||
('listed', mark_safe_lazy(LISTED_LABEL)),
|
||||
('unlisted', mark_safe_lazy(UNLISTED_LABEL))),
|
||||
|
|
|
@ -26,7 +26,12 @@
|
|||
{{ _('Edit Information') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ addon.get_dev_url('versions') }}#version-upload" class="tooltip"
|
||||
{% if waffle.switch('step-version-upload') %}
|
||||
{% set version_upload_url = url('devhub.submit.version', addon.slug) %}
|
||||
{% else %}
|
||||
{% set version_upload_url = '%s#version-upload' % addon.get_dev_url('versions') %}
|
||||
{% endif %}
|
||||
<a href="{{ version_upload_url }}" class="tooltip"
|
||||
title="{{ _('Upload a new version of this add-on.') }}">
|
||||
{{ _('New Version') }}</a>
|
||||
</li>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
{% extends "devhub/base.html" %}
|
||||
|
||||
|
||||
{% set title = _('Submit a New Add-on') %}
|
||||
{% if submit_page == 'version' %}
|
||||
{% set title = _('Submit a New Version') %}
|
||||
{% else %}
|
||||
{% set title = _('Submit a New Add-on') %}
|
||||
{% endif %}
|
||||
|
||||
{% block title %}
|
||||
{{ dev_page_title(title) }}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
{% block primary %}
|
||||
<h3>{{ _('Describe Add-on') }}</h3>
|
||||
<form method="post" id="submit-describe" class="item{% if not addon.is_listed %} unlisted{% endif %}">
|
||||
<form method="post" id="submit-describe" class="item">
|
||||
{{ csrf() }}
|
||||
<div class="addon-submission-field">
|
||||
<label for="id_name">{{ _("Name:") }}</label>
|
||||
|
@ -95,20 +95,20 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% set values = policy_form.data if policy_form.is_bound else policy_form.initial %}
|
||||
{% set values = form.data if form.is_bound else form.initial %}
|
||||
<div class="optional-terms">
|
||||
<div class="addon-submission-field">
|
||||
{{ policy_form.has_priv }}
|
||||
{{ policy_form.has_priv.label_tag() }}
|
||||
{{ form.has_priv }}
|
||||
{{ form.has_priv.label_tag() }}
|
||||
<span class="tip tooltip"
|
||||
title="{{ _("If your add-on transmits any data from the user's computer, "
|
||||
"a privacy policy is required that explains what data is sent "
|
||||
"and how it is used.")
|
||||
}}">?</span>
|
||||
<div class="priv {{ 'hidden' if not values.has_priv }}">
|
||||
{{ policy_form.privacy_policy.errors }}
|
||||
{{ policy_form.privacy_policy.label_tag() }}
|
||||
{{ policy_form.privacy_policy }}
|
||||
{{ form.privacy_policy.errors }}
|
||||
{{ form.privacy_policy.label_tag() }}
|
||||
{{ form.privacy_policy }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -122,7 +122,7 @@
|
|||
</div>
|
||||
<div class="submission-buttons addon-submission-field">
|
||||
<button type="submit">
|
||||
{{ _('Submit Add-on for Review') }}
|
||||
{{ _('Submit Version for Review') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{% from "devhub/includes/macros.html" import some_html_tip, select_cats %}
|
||||
{% extends "devhub/addons/submit/base.html" %}
|
||||
|
||||
{% block title %}{{ dev_page_title(_('Describe Add-on'), addon) }}{% endblock %}
|
||||
|
||||
{% block primary %}
|
||||
<h3>{{ _('Describe Version') }}</h3>
|
||||
<form method="post" id="submit-describe" class="item">
|
||||
{{ csrf() }}
|
||||
<div class="addon-submission-field">
|
||||
<label for="{{ reviewer_form.approvalnotes.auto_id }}">
|
||||
{{ _('Release Notes:') }}
|
||||
</label>
|
||||
<p>{{ _("Let your users know what's new and what's changed in this version.") }}</p>
|
||||
{{ reviewer_form.releasenotes }}
|
||||
<p>{{ _('These notes will appear on the detail page.') }}</p>
|
||||
</div>
|
||||
<div class="addon-submission-field">
|
||||
<label for="{{ reviewer_form.approvalnotes.auto_id }}">
|
||||
{{ _('Notes to Reviewer:') }}
|
||||
</label>
|
||||
<p>{{ _('Is there anything our reviewers should bear in mind when reviewing this add-on?') }}</p>
|
||||
{{ reviewer_form.approvalnotes }}
|
||||
<p>{{ _('These notes will only be visible to you and our reviewers.') }}</p>
|
||||
</div>
|
||||
<div class="submission-buttons addon-submission-field">
|
||||
<button type="submit">
|
||||
{{ _('Submit Version for Review') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock primary %}
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
{% block primary %}
|
||||
{% set version_edit_url = url('devhub.versions.edit', addon.slug, uploaded_version.id) %}
|
||||
{% if addon.is_listed %}
|
||||
{% if uploaded_version.channel == amo.RELEASE_CHANNEL_LISTED %}
|
||||
<h3>{{ _("Version Submitted for Review") }}</h3>
|
||||
<p>
|
||||
{{ _("You’re done! This version has been submitted for review. You will be "
|
||||
|
|
|
@ -7,7 +7,17 @@
|
|||
<form method="post" id="create-addon" class="item new-addon-file" enctype="multipart/form-data"
|
||||
data-addon-is-listed="{% if listed %}true{% else %}false{% endif %}">
|
||||
{{ csrf() }}
|
||||
<h3>{{ _('Upload Add-on') }}</h3>
|
||||
{% if submit_page == 'version' %}
|
||||
<h3>{{ _('Where to Host Version') }}</h3>
|
||||
<p class="addon-submit-distribute">
|
||||
{{ channel_choice_text|safe }}
|
||||
{% if waffle.switch('mixed-listed-unlisted') %}
|
||||
{% set channel_param = 'listed' if listed else 'unlisted' %}
|
||||
<a href="{{ url('devhub.submit.version.distribution', addon.slug) }}?channel={{ channel_param }}">{{ _('Change') }}</a>
|
||||
{% endif %}}
|
||||
</p>
|
||||
{% endif %}
|
||||
<h3>{{ _('Upload Version') }}</h3>
|
||||
<p>
|
||||
{% trans %}
|
||||
Use the fields below to upload your add-on package and select any platform
|
||||
|
@ -21,10 +31,18 @@
|
|||
{{ new_addon_form.upload }}
|
||||
</div>
|
||||
<input type="file" id="upload-addon"
|
||||
{% if listed %}
|
||||
data-upload-url="{{ url('devhub.upload') }}"
|
||||
{% if addon %}
|
||||
{% if listed %}
|
||||
data-upload-url="{{ url('devhub.upload_for_version', addon.slug, 'listed') }}"
|
||||
{% else %}
|
||||
data-upload-url="{{ url('devhub.upload_for_version', addon.slug, 'unlisted') }}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
data-upload-url="{{ url('devhub.upload_unlisted') }}"
|
||||
{% if listed %}
|
||||
data-upload-url="{{ url('devhub.upload') }}"
|
||||
{% else %}
|
||||
data-upload-url="{{ url('devhub.upload_unlisted') }}"
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
>
|
||||
|
||||
|
@ -38,7 +56,7 @@
|
|||
{{ new_addon_form.errors.supported_platforms }}
|
||||
{{ new_addon_form.supported_platforms }}
|
||||
</div>
|
||||
{{ new_addon_form.upload }}
|
||||
|
||||
</div>
|
||||
{% if is_admin %}
|
||||
<div class="admin-settings">
|
||||
|
@ -53,7 +71,11 @@
|
|||
{% endif %}
|
||||
<div class="submission-buttons addon-submission-field">
|
||||
<button class="addon-upload-dependant" id="submit-upload-file-finish" disabled=disabled type="submit">
|
||||
{{ _('Continue') }}
|
||||
{% if listed %}
|
||||
{{ _('Continue') }}
|
||||
{% else %}
|
||||
{{ _('Sign Add-on') }}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -45,8 +45,7 @@
|
|||
{{ status_and_tip(addon,
|
||||
_("Your add-on was disabled by a site administrator and is no "
|
||||
"longer shown in our gallery. If you have any questions, "
|
||||
"please email amo-admins@mozilla.org."),
|
||||
url=(addon.get_dev_url('versions') + '#version-upload')) }}
|
||||
"please email amo-admins@mozilla.org.")) }}
|
||||
{% endif %}
|
||||
</strong>
|
||||
</li>
|
||||
|
|
|
@ -25,7 +25,12 @@
|
|||
<p class="addon-upload">
|
||||
{% if (addon.has_complete_metadata() or addon.status != amo.STATUS_NULL) and not addon.is_disabled %}
|
||||
<strong>
|
||||
<a href="{{ addon.get_dev_url('versions') }}#version-upload"
|
||||
{% if waffle.switch('step-version-upload') %}
|
||||
{% set version_upload_url = url('devhub.submit.version', addon.slug) %}
|
||||
{% else %}
|
||||
{% set version_upload_url = '%s#version-upload' % addon.get_dev_url('versions') %}
|
||||
{% endif %}
|
||||
<a href="{{ version_upload_url }}"
|
||||
class="{{ version_upload_class }}">
|
||||
{{ _('Upload New Version') }}</a>
|
||||
</strong>
|
||||
|
|
|
@ -116,7 +116,12 @@
|
|||
{% endwith %}
|
||||
{% if not item.addon.is_persona() and item.addon.has_complete_metadata() and not item.addon.is_disabled %}
|
||||
<p class="upload-new-version">
|
||||
<a href="{{ item.addon.get_dev_url('versions') }}#version-upload">
|
||||
{% if waffle.switch('step-version-upload') %}
|
||||
{% set version_upload_url = url('devhub.submit.version', item.addon.slug) %}
|
||||
{% else %}
|
||||
{% set version_upload_url = '%s#version-upload' % item.addon.get_dev_url('versions') %}
|
||||
{% endif %}
|
||||
<a href="{{ version_upload_url }}">
|
||||
{{ _('Upload New Version') }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -219,7 +219,12 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td colspan="0">
|
||||
<a href="#" class="button version-upload">{{ _('Upload a New Version') }}</a>
|
||||
{% if waffle.switch('step-version-upload') %}
|
||||
{% set version_upload_url = url('devhub.submit.version', addon.slug) %}
|
||||
{% else %}
|
||||
{% set version_upload_url = '#' %}
|
||||
{% endif %}
|
||||
<a href="{{ version_upload_url }}" class="button version-upload">{{ _('Upload a New Version') }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% for version in versions.object_list %}
|
||||
|
@ -275,10 +280,12 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
{% if not waffle.switch('step-version-upload') %}
|
||||
{{ add_file_modal(_("Add a new Version"),
|
||||
url('devhub.versions.add', addon.slug),
|
||||
_("Add Version"),
|
||||
'version')}}
|
||||
{% endif %}
|
||||
|
||||
{% if not addon.disabled_by_user and not addon.is_disabled %}
|
||||
<div id="modal-disable" class="modal">
|
||||
|
|
|
@ -27,11 +27,11 @@ from olympia.users.models import UserProfile
|
|||
from olympia.versions.models import ApplicationsVersions, License, Version
|
||||
|
||||
|
||||
class TestNewAddonForm(TestCase):
|
||||
class TestNewVersionForm(TestCase):
|
||||
|
||||
def test_only_valid_uploads(self):
|
||||
upload = FileUpload.objects.create(valid=False)
|
||||
form = forms.NewAddonForm(
|
||||
form = forms.NewVersionForm(
|
||||
{'upload': upload.uuid, 'supported_platforms': [1]},
|
||||
request=mock.Mock())
|
||||
assert ('There was an error with your upload. Please try again.' in
|
||||
|
@ -39,38 +39,13 @@ class TestNewAddonForm(TestCase):
|
|||
|
||||
upload.validation = '{"errors": 0}'
|
||||
upload.save()
|
||||
form = forms.NewAddonForm(
|
||||
addon = Addon.objects.create()
|
||||
form = forms.NewVersionForm(
|
||||
{'upload': upload.uuid, 'supported_platforms': [1]},
|
||||
request=mock.Mock())
|
||||
addon=addon, request=mock.Mock())
|
||||
assert ('There was an error with your upload. Please try again.' not in
|
||||
form.errors.get('__all__')), form.errors
|
||||
|
||||
# Those three patches are so files.utils.parse_addon doesn't fail on a
|
||||
# non-existent file even before having a chance to call check_xpi_info.
|
||||
@mock.patch('olympia.files.utils.Extractor.parse')
|
||||
@mock.patch('olympia.files.utils.extract_xpi', lambda xpi, path: None)
|
||||
@mock.patch('olympia.files.utils.get_file', lambda xpi: None)
|
||||
# This is the one we want to test.
|
||||
@mock.patch('olympia.files.utils.check_xpi_info')
|
||||
def test_check_xpi_called(self, mock_check_xpi_info, mock_parse):
|
||||
"""Make sure the check_xpi_info helper is called.
|
||||
|
||||
There's some important checks made in check_xpi_info, if we ever
|
||||
refactor the form to not call it anymore, we need to make sure those
|
||||
checks are run at some point.
|
||||
"""
|
||||
mock_parse.return_value = None
|
||||
mock_check_xpi_info.return_value = {'name': 'foo', 'type': 2}
|
||||
upload = FileUpload.objects.create(valid=True)
|
||||
form = forms.NewAddonForm(
|
||||
{'upload': upload.uuid, 'supported_platforms': [1]},
|
||||
request=mock.Mock())
|
||||
form.clean()
|
||||
assert mock_check_xpi_info.called
|
||||
|
||||
|
||||
class TestNewVersionForm(TestCase):
|
||||
|
||||
# Those three patches are so files.utils.parse_addon doesn't fail on a
|
||||
# non-existent file even before having a chance to call check_xpi_info.
|
||||
@mock.patch('olympia.files.utils.Extractor.parse')
|
||||
|
@ -90,10 +65,8 @@ class TestNewVersionForm(TestCase):
|
|||
upload = FileUpload.objects.create(valid=True)
|
||||
addon = Addon.objects.create()
|
||||
form = forms.NewVersionForm(
|
||||
{'upload': upload.uuid, 'supported_platforms': [1],
|
||||
'nomination_type': amo.STATUS_NOMINATED},
|
||||
addon=addon,
|
||||
request=mock.Mock())
|
||||
{'upload': upload.uuid, 'supported_platforms': [1]},
|
||||
addon=addon, request=mock.Mock())
|
||||
form.clean()
|
||||
assert mock_check_xpi_info.called
|
||||
|
||||
|
@ -776,7 +749,7 @@ class TestDistributionChoiceForm(TestCase):
|
|||
"""
|
||||
with translation.override('en-US'):
|
||||
form = forms.DistributionChoiceForm()
|
||||
label = form.fields['choices'].choices[0][1]
|
||||
label = form.fields['channel'].choices[0][1]
|
||||
|
||||
expected = 'On this site.'
|
||||
label = unicode(label)
|
||||
|
@ -784,7 +757,7 @@ class TestDistributionChoiceForm(TestCase):
|
|||
|
||||
with translation.override('de'):
|
||||
form = forms.DistributionChoiceForm()
|
||||
label = form.fields['choices'].choices[0][1]
|
||||
label = form.fields['channel'].choices[0][1]
|
||||
|
||||
expected = 'Auf dieser Website.'
|
||||
label = unicode(label)
|
||||
|
|
|
@ -17,6 +17,7 @@ import mock
|
|||
import waffle
|
||||
from jingo.helpers import datetime as datetime_filter
|
||||
from pyquery import PyQuery as pq
|
||||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo, paypal, files
|
||||
from olympia.amo.tests import TestCase, version_factory
|
||||
|
@ -833,7 +834,8 @@ class TestHome(TestCase):
|
|||
# Regular users (non-devs) should not see this promo.
|
||||
assert self.get_pq()('#devhub-sidebar #editor-promo').length == 0
|
||||
|
||||
def test_my_addons(self):
|
||||
@override_switch('step-version-upload', active=False)
|
||||
def test_my_addons_inline_version_upload(self):
|
||||
statuses = [(amo.STATUS_NOMINATED, amo.STATUS_AWAITING_REVIEW),
|
||||
(amo.STATUS_PUBLIC, amo.STATUS_AWAITING_REVIEW)]
|
||||
|
||||
|
@ -871,6 +873,50 @@ class TestHome(TestCase):
|
|||
Addon.with_unlisted.all().delete()
|
||||
assert self.get_pq()('#my-addons').length == 0
|
||||
|
||||
def test_my_unlisted_addons_inline_version_upload(self):
|
||||
self.addon.update(is_listed=False)
|
||||
# Run the test again but with an unlisted addon.
|
||||
self.test_my_addons_inline_version_upload()
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
def test_my_addons(self):
|
||||
statuses = [(amo.STATUS_NOMINATED, amo.STATUS_AWAITING_REVIEW),
|
||||
(amo.STATUS_PUBLIC, amo.STATUS_AWAITING_REVIEW)]
|
||||
|
||||
for addon_status, file_status in statuses:
|
||||
latest_version = self.addon.find_latest_version()
|
||||
file = latest_version.files.all()[0]
|
||||
file.update(status=file_status)
|
||||
|
||||
self.addon.update(status=addon_status)
|
||||
|
||||
doc = self.get_pq()
|
||||
addon_item = doc('#my-addons .addon-item')
|
||||
assert addon_item.length == 1
|
||||
assert addon_item.find('.addon-name').attr('href') == (
|
||||
self.addon.get_dev_url('edit'))
|
||||
if self.addon.is_listed:
|
||||
# We don't display a link to the inexistent public page for
|
||||
# unlisted addons.
|
||||
assert addon_item.find('p').eq(3).find('a').attr('href') == (
|
||||
self.addon.current_version.get_url_path())
|
||||
if self.addon.is_listed:
|
||||
assert 'Queue Position: 1 of 1' == (
|
||||
addon_item.find('p').eq(4).text())
|
||||
assert addon_item.find('.upload-new-version a').attr('href') == (
|
||||
reverse('devhub.submit.version', args=[self.addon.slug]))
|
||||
|
||||
self.addon.status = statuses[1][0]
|
||||
self.addon.save()
|
||||
doc = self.get_pq()
|
||||
addon_item = doc('#my-addons .addon-item')
|
||||
status_str = 'Status: ' + unicode(
|
||||
self.addon.STATUS_CHOICES[self.addon.status])
|
||||
assert status_str == addon_item.find('p').eq(1).text()
|
||||
|
||||
Addon.with_unlisted.all().delete()
|
||||
assert self.get_pq()('#my-addons').length == 0
|
||||
|
||||
def test_my_unlisted_addons(self):
|
||||
self.addon.update(is_listed=False)
|
||||
self.test_my_addons() # Run the test again but with an unlisted addon.
|
||||
|
@ -2003,6 +2049,7 @@ class TestUploadErrors(UploadTest):
|
|||
self.test_dupe_xpi(channel='unlisted')
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=False)
|
||||
class AddVersionTest(UploadTest):
|
||||
|
||||
def post(self, supported_platforms=None,
|
||||
|
@ -2024,6 +2071,7 @@ class AddVersionTest(UploadTest):
|
|||
self.url = reverse('devhub.versions.add', args=[self.addon.slug])
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=False)
|
||||
class TestAddVersion(AddVersionTest):
|
||||
|
||||
def test_unique_version_num(self):
|
||||
|
@ -2195,10 +2243,11 @@ class TestAddVersion(AddVersionTest):
|
|||
assert signed_file.version.addon == self.addon
|
||||
assert signed_file.version.channel == amo.RELEASE_CHANNEL_LISTED
|
||||
# There is a log for that beta file signature (with passed validation).
|
||||
log = ActivityLog.objects.get()
|
||||
log = ActivityLog.objects.latest(field_name='id')
|
||||
assert log.action == amo.LOG.EXPERIMENT_SIGNED.id
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=False)
|
||||
class TestAddBetaVersion(AddVersionTest):
|
||||
fixtures = ['base/users', 'base/appversion', 'base/addon_3615']
|
||||
|
||||
|
@ -2281,6 +2330,7 @@ class TestAddBetaVersion(AddVersionTest):
|
|||
assert log.action == amo.LOG.BETA_SIGNED_VALIDATION_FAILED.id
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=False)
|
||||
class TestAddVersionValidation(AddVersionTest):
|
||||
|
||||
def login_as_admin(self):
|
||||
|
|
|
@ -6,14 +6,17 @@ from django.core.files import temp
|
|||
|
||||
import mock
|
||||
from pyquery import PyQuery as pq
|
||||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo
|
||||
from olympia.addons.models import Addon, AddonCategory, Category
|
||||
from olympia.amo.tests import addon_factory, formset, initial, TestCase
|
||||
from olympia.amo.tests import (
|
||||
addon_factory, formset, initial, TestCase, version_factory)
|
||||
from olympia.amo.tests.test_helpers import get_image_path
|
||||
from olympia.amo.urlresolvers import reverse
|
||||
from olympia.devhub import views
|
||||
from olympia.devhub.models import ActivityLog
|
||||
from olympia.files.tests.test_models import UploadTest as BaseUploadTest
|
||||
from olympia.files.tests.test_models import UploadTest
|
||||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import License
|
||||
|
||||
|
@ -76,10 +79,10 @@ class TestSubmitBase(TestCase):
|
|||
return Addon.with_unlisted.no_cache().get(pk=3615)
|
||||
|
||||
def get_version(self):
|
||||
return self.get_addon().versions.get()
|
||||
return self.get_addon().versions.latest()
|
||||
|
||||
|
||||
class TestSubmitStepAgreement(TestSubmitBase):
|
||||
class TestAddonSubmitAgreement(TestSubmitBase):
|
||||
def test_step1_submit(self):
|
||||
self.user.update(read_dev_agreement=None)
|
||||
response = self.client.get(reverse('devhub.submit.agreement'))
|
||||
|
@ -110,11 +113,45 @@ class TestSubmitStepAgreement(TestSubmitBase):
|
|||
self.assert3xx(response, reverse('devhub.submit.distribution'))
|
||||
|
||||
|
||||
class TestCreateAddon(BaseUploadTest, TestCase):
|
||||
class TestAddonSubmitDistribution(TestCase):
|
||||
fixtures = ['base/users']
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreateAddon, self).setUp()
|
||||
super(TestAddonSubmitDistribution, self).setUp()
|
||||
self.client.login(email='regular@mozilla.com')
|
||||
self.user = UserProfile.objects.get(email='regular@mozilla.com')
|
||||
|
||||
def test_check_agreement_okay(self):
|
||||
r = self.client.post(reverse('devhub.submit.agreement'))
|
||||
self.assert3xx(r, reverse('devhub.submit.distribution'))
|
||||
r = self.client.get(reverse('devhub.submit.distribution'))
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_redirect_back_to_agreement(self):
|
||||
# We require a cookie that gets set in step 1.
|
||||
self.user.update(read_dev_agreement=None)
|
||||
|
||||
r = self.client.get(reverse('devhub.submit.distribution'), follow=True)
|
||||
self.assert3xx(r, reverse('devhub.submit.agreement'))
|
||||
|
||||
def test_listed_redirects_to_next_step(self):
|
||||
response = self.client.post(reverse('devhub.submit.distribution'),
|
||||
{'channel': 'listed'})
|
||||
self.assert3xx(response,
|
||||
reverse('devhub.submit.upload', args=['listed']))
|
||||
|
||||
def test_unlisted_redirects_to_next_step(self):
|
||||
response = self.client.post(reverse('devhub.submit.distribution'),
|
||||
{'channel': 'unlisted'})
|
||||
self.assert3xx(response, reverse('devhub.submit.upload',
|
||||
args=['unlisted']))
|
||||
|
||||
|
||||
class TestAddonSubmitUpload(UploadTest, TestCase):
|
||||
fixtures = ['base/users']
|
||||
|
||||
def setUp(self):
|
||||
super(TestAddonSubmitUpload, self).setUp()
|
||||
self.upload = self.get_upload('extension.xpi')
|
||||
assert self.client.login(email='regular@mozilla.com')
|
||||
self.client.post(reverse('devhub.submit.agreement'))
|
||||
|
@ -259,7 +296,7 @@ class TestCreateAddon(BaseUploadTest, TestCase):
|
|||
all_ = sorted([f.filename for f in latest_version.all_files])
|
||||
assert all_ == [u'xpi_name-0.1-linux.xpi', u'xpi_name-0.1-mac.xpi']
|
||||
mock_auto_sign_file.assert_has_calls(
|
||||
[mock.call(f) for f in latest_version.all_files])
|
||||
[mock.call(f, is_beta=False) for f in latest_version.all_files])
|
||||
|
||||
def test_with_source(self):
|
||||
tdir = temp.gettempdir()
|
||||
|
@ -274,47 +311,10 @@ class TestCreateAddon(BaseUploadTest, TestCase):
|
|||
assert Addon.objects.get(pk=addon.pk).admin_review
|
||||
|
||||
|
||||
class TestSubmitStepDistribution(TestCase):
|
||||
fixtures = ['base/users']
|
||||
class TestAddonSubmitDetails(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSubmitStepDistribution, self).setUp()
|
||||
self.client.login(email='regular@mozilla.com')
|
||||
self.user = UserProfile.objects.get(email='regular@mozilla.com')
|
||||
|
||||
def test_check_agreement_okay(self):
|
||||
r = self.client.post(reverse('devhub.submit.agreement'))
|
||||
self.assert3xx(r, reverse('devhub.submit.distribution'))
|
||||
r = self.client.get(reverse('devhub.submit.distribution'))
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_redirect_back_to_agreement(self):
|
||||
# We require a cookie that gets set in step 1.
|
||||
self.user.update(read_dev_agreement=None)
|
||||
|
||||
r = self.client.get(reverse('devhub.submit.distribution'), follow=True)
|
||||
self.assert3xx(r, reverse('devhub.submit.agreement'))
|
||||
|
||||
def test_listed_redirects_to_next_step(self):
|
||||
response = self.client.post(reverse('devhub.submit.distribution'),
|
||||
{'choices': 'listed'})
|
||||
self.assert3xx(response,
|
||||
reverse('devhub.submit.upload', args=['listed']))
|
||||
|
||||
def test_unlisted_redirects_to_next_step(self):
|
||||
response = self.client.post(reverse('devhub.submit.distribution'),
|
||||
{'choices': 'unlisted'})
|
||||
self.assert3xx(response, reverse('devhub.submit.upload',
|
||||
args=['unlisted']))
|
||||
|
||||
|
||||
# Tests for Upload step in TestCreateAddon
|
||||
|
||||
|
||||
class TestSubmitStepDetails(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSubmitStepDetails, self).setUp()
|
||||
super(TestAddonSubmitDetails, self).setUp()
|
||||
self.url = reverse('devhub.submit.details', args=['a3615'])
|
||||
|
||||
AddonCategory.objects.filter(
|
||||
|
@ -339,7 +339,7 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
'support_email': 'black@hole.org'})
|
||||
cat_initial = kw.pop('cat_initial', self.cat_initial)
|
||||
cat_form = formset(cat_initial, initial_count=1)
|
||||
license_form = {'builtin': 3}
|
||||
license_form = {'license-builtin': 3}
|
||||
policy_form = {} if minimal else {
|
||||
'has_priv': True, 'privacy_policy': 'Ur data belongs to us now.'}
|
||||
reviewer_form = {} if minimal else {'approvalnotes': 'approove plz'}
|
||||
|
@ -354,11 +354,12 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
def is_success(self, data):
|
||||
assert self.get_addon().status == amo.STATUS_NULL
|
||||
response = self.client.post(self.url, data)
|
||||
assert all(self.get_addon().get_required_metadata())
|
||||
assert response.status_code == 302
|
||||
assert self.get_addon().status == amo.STATUS_NOMINATED
|
||||
return response
|
||||
|
||||
def test_submit_success_minimal(self):
|
||||
def test_submit_success_required(self):
|
||||
# Set/change the required fields only
|
||||
r = self.client.get(self.url)
|
||||
assert r.status_code == 200
|
||||
|
@ -380,6 +381,7 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
assert addon.slug == 'testname'
|
||||
assert addon.summary == 'Hello!'
|
||||
assert addon.is_experimental
|
||||
assert addon.all_categories[0].id == 22
|
||||
|
||||
# Test add-on log activity.
|
||||
log_items = ActivityLog.objects.for_addons(addon)
|
||||
|
@ -398,6 +400,7 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
assert addon.support_url == 'http://stackoverflow.com'
|
||||
assert addon.support_email == 'black@hole.org'
|
||||
assert addon.privacy_policy == 'Ur data belongs to us now.'
|
||||
assert addon.current_version.approvalnotes == 'approove plz'
|
||||
|
||||
def test_submit_name_unique(self):
|
||||
# Make sure name is unique.
|
||||
|
@ -510,7 +513,7 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
assert category_ids_new == [22]
|
||||
|
||||
def test_set_builtin_license_no_log(self):
|
||||
self.is_success(self.get_dict(builtin=3))
|
||||
self.is_success(self.get_dict(**{'license-builtin': 3}))
|
||||
addon = self.get_addon()
|
||||
assert addon.status == amo.STATUS_NOMINATED
|
||||
assert addon.current_version.license.builtin == 3
|
||||
|
@ -518,7 +521,8 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
assert not log_items.filter(action=amo.LOG.CHANGE_LICENSE.id)
|
||||
|
||||
def test_license_error(self):
|
||||
response = self.client.post(self.url, self.get_dict(builtin=4))
|
||||
response = self.client.post(
|
||||
self.url, self.get_dict(**{'license-builtin': 4}))
|
||||
assert response.status_code == 200
|
||||
self.assertFormError(response, 'license_form', 'builtin',
|
||||
'Select a valid choice. 4 is not one of '
|
||||
|
@ -550,14 +554,13 @@ class TestSubmitStepDetails(TestSubmitBase):
|
|||
version = self.get_addon().versions.latest()
|
||||
version.update(channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
response = self.client.get(self.url)
|
||||
self.assert3xx(
|
||||
response, reverse('devhub.submit.finish', args=[self.addon.slug]))
|
||||
self.assert3xx(response, self.next_step)
|
||||
|
||||
|
||||
class TestSubmitStepFinish(TestSubmitBase):
|
||||
class TestAddonSubmitFinish(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSubmitStepFinish, self).setUp()
|
||||
super(TestAddonSubmitFinish, self).setUp()
|
||||
self.url = reverse('devhub.submit.finish', args=[self.addon.slug])
|
||||
|
||||
@mock.patch.object(settings, 'SITE_URL', 'http://b.ro')
|
||||
|
@ -624,8 +627,9 @@ class TestSubmitStepFinish(TestSubmitBase):
|
|||
assert not send_welcome_email_mock.called
|
||||
|
||||
def test_finish_submitting_listed_addon(self):
|
||||
assert self.addon.current_version.supported_platforms == (
|
||||
[amo.PLATFORM_ALL])
|
||||
version = self.addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
assert version.supported_platforms == ([amo.PLATFORM_ALL])
|
||||
|
||||
r = self.client.get(self.url)
|
||||
assert r.status_code == 200
|
||||
|
@ -639,9 +643,9 @@ class TestSubmitStepFinish(TestSubmitBase):
|
|||
# Second link is to edit the version
|
||||
assert links[1].attrib['href'] == reverse(
|
||||
'devhub.versions.edit',
|
||||
args=[self.addon.slug, self.addon.current_version.id])
|
||||
args=[self.addon.slug, version.id])
|
||||
assert links[1].text == (
|
||||
'Edit version %s' % self.addon.current_version.version)
|
||||
'Edit version %s' % version.version)
|
||||
# Third back to my submissions.
|
||||
assert links[2].attrib['href'] == reverse('devhub.addons')
|
||||
|
||||
|
@ -737,10 +741,10 @@ class TestSubmitStepFinish(TestSubmitBase):
|
|||
self.assert3xx(r, reverse('devhub.submit.details', args=['a3615']))
|
||||
|
||||
|
||||
class TestResumeStep(TestSubmitBase):
|
||||
class TestAddonSubmitResume(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestResumeStep, self).setUp()
|
||||
super(TestAddonSubmitResume, self).setUp()
|
||||
self.url = reverse('devhub.submit.resume', args=['a3615'])
|
||||
|
||||
def test_addon_no_versions_redirects_to_versions(self):
|
||||
|
@ -763,3 +767,442 @@ class TestResumeStep(TestSubmitBase):
|
|||
r = self.client.get(reverse('devhub.addons.edit', args=['a3615']),
|
||||
follow=True)
|
||||
self.assert3xx(r, reverse('devhub.submit.details', args=['a3615']))
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
@override_switch('mixed-listed-unlisted', active=True)
|
||||
class TestVersionSubmitDistribution(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitDistribution, self).setUp()
|
||||
self.url = reverse('devhub.submit.version.distribution',
|
||||
args=[self.addon.slug])
|
||||
|
||||
def test_listed_redirects_to_next_step(self):
|
||||
response = self.client.post(self.url, {'channel': 'listed'})
|
||||
self.assert3xx(
|
||||
response,
|
||||
reverse('devhub.submit.version.upload', args=[
|
||||
self.addon.slug, 'listed']))
|
||||
|
||||
def test_unlisted_redirects_to_next_step(self):
|
||||
response = self.client.post(self.url, {'channel': 'unlisted'})
|
||||
self.assert3xx(
|
||||
response,
|
||||
reverse('devhub.submit.version.upload', args=[
|
||||
self.addon.slug, 'unlisted']))
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
@override_switch('mixed-listed-unlisted', active=True)
|
||||
class TestVersionSubmitAutoChannel(TestSubmitBase):
|
||||
""" Just check we chose the right upload channel. The upload tests
|
||||
themselves are in other tests. """
|
||||
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitAutoChannel, self).setUp()
|
||||
self.url = reverse('devhub.submit.version', args=[self.addon.slug])
|
||||
|
||||
@mock.patch('olympia.devhub.views._submit_upload',
|
||||
side_effect=views._submit_upload)
|
||||
def test_listed_last_uses_listed_upload(self, _submit_upload_mock):
|
||||
version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
self.client.post(self.url)
|
||||
args, _ = _submit_upload_mock.call_args
|
||||
assert args[1:] == (
|
||||
self.addon, amo.RELEASE_CHANNEL_LISTED,
|
||||
'devhub.submit.version.details', 'devhub.submit.version.finish')
|
||||
|
||||
@mock.patch('olympia.devhub.views._submit_upload',
|
||||
side_effect=views._submit_upload)
|
||||
def test_unlisted_last_uses_unlisted_upload(self, _submit_upload_mock):
|
||||
version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
self.client.post(self.url)
|
||||
args, _ = _submit_upload_mock.call_args
|
||||
assert args[1:] == (
|
||||
self.addon, amo.RELEASE_CHANNEL_UNLISTED,
|
||||
'devhub.submit.version.details', 'devhub.submit.version.finish')
|
||||
|
||||
def test_no_versions_redirects_to_distribution(self):
|
||||
[v.delete() for v in self.addon.versions.all()]
|
||||
response = self.client.post(self.url)
|
||||
self.assert3xx(
|
||||
response,
|
||||
reverse('devhub.submit.version.distribution',
|
||||
args=[self.addon.slug]))
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
@override_switch('mixed-listed-unlisted', active=False)
|
||||
class TestVersionSubmitAutoChannelNoMixed(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitAutoChannelNoMixed, self).setUp()
|
||||
self.url = reverse('devhub.submit.version', args=[self.addon.slug])
|
||||
|
||||
@mock.patch('olympia.devhub.views._submit_upload',
|
||||
side_effect=views._submit_upload)
|
||||
def test_redirect_listed_based_on_is_listed(self, _submit_upload_mock):
|
||||
self.addon.update(is_listed=True)
|
||||
self.client.post(self.url)
|
||||
args, _ = _submit_upload_mock.call_args
|
||||
assert args[1:] == (
|
||||
self.addon, amo.RELEASE_CHANNEL_LISTED,
|
||||
'devhub.submit.version.details', 'devhub.submit.version.finish')
|
||||
|
||||
@mock.patch('olympia.devhub.views._submit_upload',
|
||||
side_effect=views._submit_upload)
|
||||
def test_redirect_unlisted_based_on_is_listed(self, _submit_upload_mock):
|
||||
self.addon.update(is_listed=False)
|
||||
self.client.post(self.url)
|
||||
args, _ = _submit_upload_mock.call_args
|
||||
assert args[1:] == (
|
||||
self.addon, amo.RELEASE_CHANNEL_UNLISTED,
|
||||
'devhub.submit.version.details', 'devhub.submit.version.finish')
|
||||
|
||||
|
||||
class VersionSubmitUploadMixin(object):
|
||||
channel = None
|
||||
fixtures = ['base/users', 'base/addon_3615']
|
||||
|
||||
def setUp(self):
|
||||
super(VersionSubmitUploadMixin, self).setUp()
|
||||
self.upload = self.get_upload('extension.xpi')
|
||||
self.addon = Addon.objects.get(id=3615)
|
||||
self.version = self.addon.current_version
|
||||
self.addon.update(guid='guid@xpi')
|
||||
assert self.client.login(email='del@icio.us')
|
||||
self.addon.update(
|
||||
is_listed=(self.channel == amo.RELEASE_CHANNEL_LISTED))
|
||||
self.addon.versions.update(channel=self.channel)
|
||||
channel = ('listed' if self.channel == amo.RELEASE_CHANNEL_LISTED else
|
||||
'unlisted')
|
||||
self.url = reverse('devhub.submit.version.upload',
|
||||
args=[self.addon.slug, channel])
|
||||
assert self.addon.has_complete_metadata()
|
||||
|
||||
def post(self, supported_platforms=None,
|
||||
override_validation=False, expected_status=302, source=None,
|
||||
beta=False):
|
||||
if supported_platforms is None:
|
||||
supported_platforms = [amo.PLATFORM_MAC]
|
||||
d = dict(upload=self.upload.uuid.hex, source=source,
|
||||
supported_platforms=[p.id for p in supported_platforms],
|
||||
admin_override_validation=override_validation, beta=beta)
|
||||
r = self.client.post(self.url, d)
|
||||
assert r.status_code == expected_status
|
||||
return r
|
||||
|
||||
def get_next_url(self, version):
|
||||
raise NotImplementedError
|
||||
|
||||
def test_with_source(self):
|
||||
tdir = temp.gettempdir()
|
||||
source = temp.NamedTemporaryFile(suffix=".zip", dir=tdir)
|
||||
source.write('a' * (2 ** 21))
|
||||
source.seek(0)
|
||||
response = self.post(source=source)
|
||||
version = self.addon.find_latest_version(channel=self.channel)
|
||||
assert version.source
|
||||
self.assert3xx(response, self.get_next_url(version))
|
||||
assert self.addon.reload().admin_review
|
||||
|
||||
def test_with_bad_source_format(self):
|
||||
tdir = temp.gettempdir()
|
||||
source = temp.NamedTemporaryFile(suffix=".exe", dir=tdir)
|
||||
source.write('a' * (2 ** 21))
|
||||
source.seek(0)
|
||||
response = self.post(source=source, expected_status=200)
|
||||
assert response.context['new_addon_form'].errors.as_text().startswith(
|
||||
'* source\n * Unsupported file type, please upload an archive ')
|
||||
|
||||
def test_missing_platforms(self):
|
||||
r = self.client.post(self.url, dict(upload=self.upload.uuid.hex))
|
||||
assert r.status_code == 200
|
||||
assert r.context['new_addon_form'].errors.as_text() == (
|
||||
'* supported_platforms\n * Need at least one platform.')
|
||||
|
||||
def test_one_xpi_for_multiple_platforms(self):
|
||||
response = self.post(supported_platforms=[amo.PLATFORM_MAC,
|
||||
amo.PLATFORM_LINUX])
|
||||
version = self.addon.find_latest_version(channel=self.channel)
|
||||
self.assert3xx(response, self.get_next_url(version))
|
||||
all_ = sorted([f.filename for f in version.all_files])
|
||||
assert all_ == [u'delicious_bookmarks-0.1-linux.xpi',
|
||||
u'delicious_bookmarks-0.1-mac.xpi']
|
||||
|
||||
def test_unique_version_num(self):
|
||||
self.version.update(version='0.1')
|
||||
r = self.post(expected_status=200)
|
||||
assert pq(r.content)('ul.errorlist').text() == (
|
||||
'Version 0.1 already exists, or was uploaded before.')
|
||||
|
||||
def test_same_version_if_previous_is_rejected(self):
|
||||
# We can't re-use the same version number, even if the previous
|
||||
# versions have been disabled/rejected.
|
||||
self.version.update(version='0.1')
|
||||
self.version.files.update(status=amo.STATUS_DISABLED)
|
||||
r = self.post(expected_status=200)
|
||||
assert pq(r.content)('ul.errorlist').text() == (
|
||||
'Version 0.1 already exists, or was uploaded before.')
|
||||
|
||||
def test_same_version_if_previous_is_deleted(self):
|
||||
# We can't re-use the same version number if the previous
|
||||
# versions has been deleted either.
|
||||
self.version.update(version='0.1')
|
||||
self.version.delete()
|
||||
r = self.post(expected_status=200)
|
||||
assert pq(r.content)('ul.errorlist').text() == (
|
||||
'Version 0.1 already exists, or was uploaded before.')
|
||||
|
||||
@override_switch('mixed-listed-unlisted', active=True)
|
||||
def test_distribution_link(self):
|
||||
response = self.client.get(self.url)
|
||||
channel_text = ('listed' if self.channel == amo.RELEASE_CHANNEL_LISTED
|
||||
else 'unlisted')
|
||||
distribution_url = reverse('devhub.submit.version.distribution',
|
||||
args=[self.addon.slug])
|
||||
doc = pq(response.content)
|
||||
assert doc('.addon-submit-distribute a').attr('href') == (
|
||||
distribution_url + '?channel=' + channel_text)
|
||||
|
||||
@override_switch('mixed-listed-unlisted', active=False)
|
||||
def test_distribution_link_hidden(self):
|
||||
response = self.client.get(self.url)
|
||||
doc = pq(response.content)
|
||||
assert doc('.addon-submit-distribute a').length == 0
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
class TestVersionSubmitUploadListed(VersionSubmitUploadMixin, UploadTest):
|
||||
channel = amo.RELEASE_CHANNEL_LISTED
|
||||
|
||||
def get_next_url(self, version):
|
||||
return reverse('devhub.submit.version.details', args=[
|
||||
self.addon.slug, version.pk])
|
||||
|
||||
def test_success(self):
|
||||
response = self.post()
|
||||
version = self.addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
assert version.channel == amo.RELEASE_CHANNEL_LISTED
|
||||
assert version.all_files[0].status == amo.STATUS_AWAITING_REVIEW
|
||||
self.assert3xx(response, self.get_next_url(version))
|
||||
log_items = ActivityLog.objects.for_addons(self.addon)
|
||||
assert log_items.filter(action=amo.LOG.ADD_VERSION.id)
|
||||
|
||||
@mock.patch('olympia.devhub.views.sign_file')
|
||||
def test_experiments_are_auto_signed(self, mock_sign_file):
|
||||
"""Experiment extensions (bug 1220097) are auto-signed."""
|
||||
# We're going to sign even if it has signing related errors/warnings.
|
||||
self.upload = self.get_upload(
|
||||
'telemetry_experiment.xpi',
|
||||
validation=json.dumps({
|
||||
"notices": 2, "errors": 0, "messages": [],
|
||||
"metadata": {}, "warnings": 1,
|
||||
"signing_summary": {"trivial": 1, "low": 0,
|
||||
"medium": 0, "high": 1},
|
||||
"passed_auto_validation": 0}))
|
||||
self.addon.update(guid='experiment@xpi', is_listed=True,
|
||||
status=amo.STATUS_PUBLIC)
|
||||
self.post()
|
||||
# Make sure the file created and signed is for this addon.
|
||||
assert mock_sign_file.call_count == 1
|
||||
mock_sign_file_call = mock_sign_file.call_args[0]
|
||||
signed_file = mock_sign_file_call[0]
|
||||
assert signed_file.version.addon == self.addon
|
||||
assert signed_file.version.channel == amo.RELEASE_CHANNEL_LISTED
|
||||
# There is a log for that file (with passed validation).
|
||||
log = ActivityLog.objects.latest(field_name='id')
|
||||
assert log.action == amo.LOG.EXPERIMENT_SIGNED.id
|
||||
|
||||
def test_force_beta(self):
|
||||
self.post(beta=True)
|
||||
# Need latest() rather than find_latest_version as Beta isn't returned.
|
||||
version = self.addon.versions.latest()
|
||||
assert version.all_files[0].status == amo.STATUS_BETA
|
||||
|
||||
def test_incomplete_addon_now_nominated(self):
|
||||
"""Uploading a new version for an incomplete addon should set it to
|
||||
nominated."""
|
||||
self.addon.current_version.files.update(status=amo.STATUS_DISABLED)
|
||||
self.addon.update_status()
|
||||
# Deleting all the versions should make it null.
|
||||
assert self.addon.status == amo.STATUS_NULL
|
||||
self.post()
|
||||
self.addon.reload()
|
||||
assert self.addon.status == amo.STATUS_NOMINATED
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
class TestVersionSubmitUploadUnlisted(VersionSubmitUploadMixin, UploadTest):
|
||||
channel = amo.RELEASE_CHANNEL_UNLISTED
|
||||
|
||||
def get_next_url(self, version):
|
||||
return reverse('devhub.submit.version.finish', args=[
|
||||
self.addon.slug, version.pk])
|
||||
|
||||
@mock.patch('olympia.editors.helpers.sign_file')
|
||||
def test_success(self, mock_sign_file):
|
||||
"""Sign automatically."""
|
||||
# No validation errors or warning.
|
||||
self.upload = self.get_upload(
|
||||
'extension.xpi',
|
||||
validation=json.dumps(dict(errors=0, warnings=0, notices=2,
|
||||
metadata={}, messages=[],
|
||||
signing_summary={
|
||||
'trivial': 1, 'low': 0, 'medium': 0,
|
||||
'high': 0},
|
||||
passed_auto_validation=True
|
||||
)))
|
||||
response = self.post()
|
||||
version = self.addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
assert version.channel == amo.RELEASE_CHANNEL_UNLISTED
|
||||
assert version.all_files[0].status == amo.STATUS_PUBLIC
|
||||
self.assert3xx(response, self.get_next_url(version))
|
||||
assert mock_sign_file.called
|
||||
|
||||
@mock.patch('olympia.editors.helpers.sign_file')
|
||||
def test_success_fail_validation(self, mock_sign_file):
|
||||
self.upload = self.get_upload(
|
||||
'extension.xpi',
|
||||
validation=json.dumps(dict(errors=0, warnings=0, notices=2,
|
||||
metadata={}, messages=[],
|
||||
signing_summary={
|
||||
'trivial': 0, 'low': 1, 'medium': 0,
|
||||
'high': 0},
|
||||
passed_auto_validation=False
|
||||
)))
|
||||
response = self.post()
|
||||
version = self.addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
assert version.channel == amo.RELEASE_CHANNEL_UNLISTED
|
||||
assert version.all_files[0].status == amo.STATUS_PUBLIC
|
||||
self.assert3xx(response, self.get_next_url(version))
|
||||
assert mock_sign_file.called
|
||||
|
||||
@mock.patch('olympia.devhub.views.auto_sign_file')
|
||||
def test_one_xpi_for_multiple_platforms(self, mock_auto_sign_file):
|
||||
super(TestVersionSubmitUploadUnlisted,
|
||||
self).test_one_xpi_for_multiple_platforms()
|
||||
version = self.addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
mock_auto_sign_file.assert_has_calls(
|
||||
[mock.call(f, is_beta=False) for f in version.all_files])
|
||||
|
||||
def test_no_force_beta_for_unlisted(self):
|
||||
"""No beta version for unlisted addons."""
|
||||
self.post(beta=True)
|
||||
# Need latest() rather than find_latest_version as Beta isn't returned.
|
||||
version = self.addon.versions.latest()
|
||||
assert version.all_files[0].status != amo.STATUS_BETA
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
class TestVersionSubmitDetails(TestSubmitBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitDetails, self).setUp()
|
||||
addon = self.get_addon()
|
||||
self.version = version_factory(
|
||||
addon=addon,
|
||||
channel=amo.RELEASE_CHANNEL_LISTED,
|
||||
license_id=addon.versions.latest().license_id)
|
||||
self.url = reverse('devhub.submit.version.details',
|
||||
args=[addon.slug, self.version.pk])
|
||||
|
||||
def test_submit_empty_is_okay(self):
|
||||
assert all(self.get_addon().get_required_metadata())
|
||||
r = self.client.get(self.url)
|
||||
assert r.status_code == 200
|
||||
|
||||
response = self.client.post(self.url, {})
|
||||
self.assert3xx(
|
||||
response, reverse('devhub.submit.version.finish',
|
||||
args=[self.addon.slug, self.version.pk]))
|
||||
|
||||
assert not self.version.approvalnotes
|
||||
assert not self.version.releasenotes
|
||||
|
||||
def test_submit_success(self):
|
||||
assert all(self.get_addon().get_required_metadata())
|
||||
r = self.client.get(self.url)
|
||||
assert r.status_code == 200
|
||||
|
||||
# Post and be redirected - trying to sneak in a field that shouldn't
|
||||
# be modified when this is not the first listed version.
|
||||
data = {'approvalnotes': 'approove plz',
|
||||
'releasenotes': 'loadsa stuff', 'name': 'foo'}
|
||||
response = self.client.post(self.url, data)
|
||||
self.assert3xx(
|
||||
response, reverse('devhub.submit.version.finish',
|
||||
args=[self.addon.slug, self.version.pk]))
|
||||
|
||||
# This field should not have been modified.
|
||||
assert self.get_addon().name != 'foo'
|
||||
|
||||
self.version.reload()
|
||||
assert self.version.approvalnotes == 'approove plz'
|
||||
assert self.version.releasenotes == 'loadsa stuff'
|
||||
|
||||
def test_submit_details_unlisted_should_redirect(self):
|
||||
self.version.update(channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
assert all(self.get_addon().get_required_metadata())
|
||||
response = self.client.get(self.url)
|
||||
self.assert3xx(
|
||||
response, reverse('devhub.submit.version.finish',
|
||||
args=[self.addon.slug, self.version.pk]))
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
class TestVersionSubmitDetailsFirstListed(TestAddonSubmitDetails):
|
||||
""" Testing the case of a listed version being submitted on an add-on that
|
||||
previously only had unlisted versions - so is missing metadata."""
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitDetailsFirstListed, self).setUp()
|
||||
self.addon.versions.update(channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||
self.version = version_factory(addon=self.addon,
|
||||
channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
self.url = reverse('devhub.submit.version.details',
|
||||
args=['a3615', self.version.pk])
|
||||
self.next_step = reverse('devhub.submit.version.finish',
|
||||
args=['a3615', self.version.pk])
|
||||
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
class TestVersionSubmitFinish(TestAddonSubmitFinish):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVersionSubmitFinish, self).setUp()
|
||||
addon = self.get_addon()
|
||||
self.version = version_factory(
|
||||
addon=addon,
|
||||
channel=amo.RELEASE_CHANNEL_LISTED,
|
||||
license_id=addon.versions.latest().license_id,
|
||||
file_kw={'status': amo.STATUS_AWAITING_REVIEW})
|
||||
self.url = reverse('devhub.submit.version.finish',
|
||||
args=[addon.slug, self.version.pk])
|
||||
|
||||
@mock.patch('olympia.devhub.tasks.send_welcome_email.delay')
|
||||
def test_no_welcome_email(self, send_welcome_email_mock):
|
||||
"""No emails for version finish."""
|
||||
self.client.get(self.url)
|
||||
assert not send_welcome_email_mock.called
|
||||
|
||||
def test_addon_no_versions_redirects_to_versions(self):
|
||||
# No versions makes getting to this step difficult!
|
||||
pass
|
||||
|
||||
# No emails for any of these cases so ignore them.
|
||||
def test_welcome_email_for_newbies(self):
|
||||
pass
|
||||
|
||||
def test_welcome_email_first_listed_addon(self):
|
||||
pass
|
||||
|
||||
def test_welcome_email_if_previous_addon_is_incomplete(self):
|
||||
pass
|
||||
|
||||
def test_no_welcome_email_if_unlisted(self):
|
||||
pass
|
||||
|
|
|
@ -74,7 +74,8 @@ class TestVersion(TestCase):
|
|||
reverse('devhub.file_validation',
|
||||
args=[self.addon.slug, self.version.all_files[0].id]))
|
||||
|
||||
def test_upload_link_label_in_edit_nav(self):
|
||||
@override_switch('step-version-upload', active=False)
|
||||
def test_upload_link_label_in_edit_nav_inline_upload(self):
|
||||
url = reverse('devhub.versions.edit',
|
||||
args=(self.addon.slug, self.version.pk))
|
||||
r = self.client.get(url)
|
||||
|
@ -83,6 +84,16 @@ class TestVersion(TestCase):
|
|||
assert link.attr('href') == '%s#version-upload' % (
|
||||
reverse('devhub.addons.versions', args=[self.addon.slug]))
|
||||
|
||||
@override_switch('step-version-upload', active=True)
|
||||
def test_upload_link_label_in_edit_nav(self):
|
||||
url = reverse('devhub.versions.edit',
|
||||
args=(self.addon.slug, self.version.pk))
|
||||
r = self.client.get(url)
|
||||
link = pq(r.content)('.addon-status>.addon-upload>strong>a')
|
||||
assert link.text() == 'Upload New Version'
|
||||
assert link.attr('href') == (
|
||||
reverse('devhub.submit.version', args=[self.addon.slug]))
|
||||
|
||||
def test_delete_message(self):
|
||||
"""Make sure we warn our users of the pain they will feel."""
|
||||
r = self.client.get(self.url)
|
||||
|
|
|
@ -11,15 +11,6 @@ from . import views
|
|||
PACKAGE_NAME = '(?P<package_name>[_\w]+)'
|
||||
|
||||
|
||||
# These will all start with /addon/<addon_id>/submit/
|
||||
submit_patterns = patterns(
|
||||
'',
|
||||
url('^$', lambda r, addon_id: redirect('devhub.submit.finish', addon_id)),
|
||||
url('^details$', views.submit_details, name='devhub.submit.details'),
|
||||
url('^finish$', views.submit_finish, name='devhub.submit.finish'),
|
||||
)
|
||||
|
||||
|
||||
# These will all start with /theme/<slug>/
|
||||
theme_detail_patterns = patterns(
|
||||
'',
|
||||
|
@ -82,6 +73,23 @@ detail_patterns = patterns(
|
|||
name='devhub.versions.add_file'),
|
||||
url('^versions/(?P<version>[^/]+)$', views.version_bounce),
|
||||
|
||||
# New version submission
|
||||
url('^versions/submit/$',
|
||||
views.submit_version,
|
||||
name='devhub.submit.version'),
|
||||
url('^versions/submit/distribution$',
|
||||
views.submit_version_distribution,
|
||||
name='devhub.submit.version.distribution'),
|
||||
url('^versions/submit/upload-(?P<channel>listed|unlisted)$',
|
||||
views.submit_version_upload,
|
||||
name='devhub.submit.version.upload'),
|
||||
url('^versions/submit/(?P<version_id>\d+)/details$',
|
||||
views.submit_version_details,
|
||||
name='devhub.submit.version.details'),
|
||||
url('^versions/submit/(?P<version_id>\d+)/finish$',
|
||||
views.submit_version_finish,
|
||||
name='devhub.submit.version.finish'),
|
||||
|
||||
url('^file/(?P<file_id>[^/]+)/validation$', views.file_validation,
|
||||
name='devhub.file_validation'),
|
||||
url('^file/(?P<file_id>[^/]+)/validation\.json$',
|
||||
|
@ -99,8 +107,14 @@ detail_patterns = patterns(
|
|||
views.json_bulk_compat_result,
|
||||
name='devhub.json_bulk_compat_result'),
|
||||
|
||||
url('^submit/', include(submit_patterns)),
|
||||
url('^submit/$',
|
||||
lambda r, addon_id: redirect('devhub.submit.finish', addon_id)),
|
||||
url('^submit/details$',
|
||||
views.submit_addon_details, name='devhub.submit.details'),
|
||||
url('^submit/finish$', views.submit_addon_finish,
|
||||
name='devhub.submit.finish'),
|
||||
url('^submit/resume$', views.submit_resume, name='devhub.submit.resume'),
|
||||
|
||||
url('^request-review$',
|
||||
views.request_review, name='devhub.request-review'),
|
||||
url('^rmlocale$', views.remove_locale, name='devhub.addons.remove-locale'),
|
||||
|
@ -143,7 +157,7 @@ urlpatterns = decorate(write, patterns(
|
|||
# Add-on submission
|
||||
url('^addon/submit/(?:1)?$',
|
||||
lambda r: redirect('devhub.submit.agreement', permanent=True)),
|
||||
url('^addon/submit/agreement$', views.submit,
|
||||
url('^addon/submit/agreement$', views.submit_addon,
|
||||
name='devhub.submit.agreement'),
|
||||
url('^addon/submit/distribution$', views.submit_addon_distribution,
|
||||
name='devhub.submit.distribution'),
|
||||
|
|
|
@ -18,8 +18,10 @@ from django.views.decorators.cache import never_cache
|
|||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
import commonware.log
|
||||
import waffle
|
||||
from django_statsd.clients import statsd
|
||||
from PIL import Image
|
||||
from waffle.decorators import waffle_switch
|
||||
|
||||
from olympia import amo
|
||||
from olympia.amo import utils as amo_utils
|
||||
|
@ -1262,6 +1264,7 @@ def auto_sign_version(version, **kwargs):
|
|||
@json_view
|
||||
@dev_required
|
||||
@post_required
|
||||
@waffle_switch('!step-version-upload')
|
||||
def version_add(request, addon_id, addon):
|
||||
form = forms.NewVersionForm(
|
||||
request.POST,
|
||||
|
@ -1387,109 +1390,208 @@ def version_stats(request, addon_id, addon):
|
|||
|
||||
|
||||
@login_required
|
||||
def submit(request):
|
||||
def submit_addon(request):
|
||||
return render_agreement(request, 'devhub/addons/submit/start.html',
|
||||
'devhub.submit.distribution')
|
||||
|
||||
|
||||
@login_required
|
||||
@transaction.atomic
|
||||
def submit_addon_distribution(request):
|
||||
def _submit_distribution(request, addon, next_view):
|
||||
if request.user.read_dev_agreement is None:
|
||||
return redirect('devhub.submit.agreement')
|
||||
form = forms.DistributionChoiceForm(request.POST)
|
||||
# Accept GET for the first load so we can preselect the channel.
|
||||
form = forms.DistributionChoiceForm(
|
||||
request.POST if request.method == 'POST' else request.GET)
|
||||
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
data = form.cleaned_data
|
||||
return redirect('devhub.submit.upload', data['choices'])
|
||||
args = [addon.slug] if addon else []
|
||||
args.append(data['channel'])
|
||||
return redirect(next_view, *args)
|
||||
return render(request, 'devhub/addons/submit/distribute.html',
|
||||
{'distribution_form': form})
|
||||
{'distribution_form': form,
|
||||
'submit_page': 'version' if addon else 'addon'})
|
||||
|
||||
|
||||
@login_required
|
||||
def submit_addon_distribution(request):
|
||||
return _submit_distribution(request, None, 'devhub.submit.upload')
|
||||
|
||||
|
||||
@dev_required
|
||||
@waffle_switch('step-version-upload')
|
||||
@waffle_switch('mixed-listed-unlisted')
|
||||
def submit_version_distribution(request, addon_id, addon):
|
||||
return _submit_distribution(request, addon, 'devhub.submit.version.upload')
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def submit_addon_upload(request, channel):
|
||||
form = forms.NewAddonForm(
|
||||
def _submit_upload(request, addon, channel, next_listed, next_unlisted):
|
||||
form = forms.NewVersionForm(
|
||||
request.POST or None,
|
||||
request.FILES or None,
|
||||
addon=addon,
|
||||
request=request
|
||||
)
|
||||
is_listed = channel == 'listed'
|
||||
if request.method == 'POST':
|
||||
if form.is_valid():
|
||||
data = form.cleaned_data
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
data = form.cleaned_data
|
||||
is_beta = (data['beta'] and addon and
|
||||
channel == amo.RELEASE_CHANNEL_LISTED)
|
||||
|
||||
p = data.get('supported_platforms', [])
|
||||
|
||||
addon = Addon.from_upload(data['upload'], p, source=data['source'],
|
||||
is_listed=is_listed)
|
||||
if addon:
|
||||
version = Version.from_upload(
|
||||
upload=form.cleaned_data['upload'],
|
||||
addon=addon,
|
||||
platforms=data.get('supported_platforms', []),
|
||||
channel=channel,
|
||||
source=data['source'],
|
||||
is_beta=is_beta)
|
||||
url_args = [addon.slug, version.id]
|
||||
else:
|
||||
addon = Addon.from_upload(
|
||||
upload=data['upload'],
|
||||
platforms=data.get('supported_platforms', []),
|
||||
source=data['source'],
|
||||
is_listed=channel == amo.RELEASE_CHANNEL_LISTED)
|
||||
version = addon.find_latest_version(channel=channel)
|
||||
AddonUser(addon=addon, user=request.user).save()
|
||||
check_validation_override(request, form, addon,
|
||||
addon.current_version)
|
||||
if not addon.is_listed: # Not listed? Automatically choose queue.
|
||||
addon.update(status=amo.STATUS_NOMINATED)
|
||||
# Sign all the files submitted, one for each platform.
|
||||
auto_sign_version(addon.versions.get())
|
||||
return redirect('devhub.submit.finish', addon.slug)
|
||||
return redirect('devhub.submit.details', addon.slug)
|
||||
url_args = [addon.slug]
|
||||
|
||||
check_validation_override(request, form, addon, version)
|
||||
addon_update = {}
|
||||
if data['source']:
|
||||
addon_update['admin_review'] = True
|
||||
if addon.status == amo.STATUS_NULL and addon.has_complete_metadata():
|
||||
addon_update['status'] = amo.STATUS_NOMINATED
|
||||
if addon_update:
|
||||
addon.update(**addon_update)
|
||||
# auto-sign versions (the method checks eligibility)
|
||||
auto_sign_version(version, is_beta=is_beta)
|
||||
next_url = (next_listed if channel == amo.RELEASE_CHANNEL_LISTED
|
||||
else next_unlisted)
|
||||
return redirect(next_url, *url_args)
|
||||
is_admin = acl.action_allowed(request, 'ReviewerAdminTools', 'View')
|
||||
|
||||
if addon:
|
||||
channel_choice_text = (forms.DistributionChoiceForm().LISTED_LABEL
|
||||
if channel == amo.RELEASE_CHANNEL_LISTED else
|
||||
forms.DistributionChoiceForm().UNLISTED_LABEL)
|
||||
else:
|
||||
channel_choice_text = '' # We only need this for Version upload.
|
||||
return render(request, 'devhub/addons/submit/upload.html',
|
||||
{'new_addon_form': form, 'is_admin': is_admin,
|
||||
'listed': is_listed})
|
||||
{'new_addon_form': form,
|
||||
'is_admin': is_admin,
|
||||
'addon': addon,
|
||||
'submit_page': 'version' if addon else 'addon',
|
||||
'listed': channel == amo.RELEASE_CHANNEL_LISTED,
|
||||
'channel_choice_text': channel_choice_text})
|
||||
|
||||
|
||||
@dev_required(submitting=True)
|
||||
def submit_details(request, addon_id, addon):
|
||||
forms_, context = [], {}
|
||||
@login_required
|
||||
def submit_addon_upload(request, channel):
|
||||
channel_id = amo.CHANNEL_CHOICES_LOOKUP[channel]
|
||||
return _submit_upload(request, None, channel_id,
|
||||
'devhub.submit.details', 'devhub.submit.finish')
|
||||
|
||||
|
||||
@dev_required
|
||||
@waffle_switch('step-version-upload')
|
||||
def submit_version_upload(request, addon_id, addon, channel):
|
||||
if waffle.switch_is_active('mixed-listed-unlisted'):
|
||||
channel_id = amo.CHANNEL_CHOICES_LOOKUP[channel]
|
||||
else:
|
||||
# Don't allow channel choice until rest of AMO supports it.
|
||||
channel_id = (amo.RELEASE_CHANNEL_LISTED if addon.is_listed else
|
||||
amo.RELEASE_CHANNEL_UNLISTED)
|
||||
return _submit_upload(request, addon, channel_id,
|
||||
'devhub.submit.version.details',
|
||||
'devhub.submit.version.finish')
|
||||
|
||||
|
||||
@dev_required
|
||||
@waffle_switch('step-version-upload')
|
||||
def submit_version(request, addon_id, addon):
|
||||
if waffle.switch_is_active('mixed-listed-unlisted'):
|
||||
# choose the channel we need from the last upload
|
||||
last_version = addon.find_latest_version_including_rejected()
|
||||
if not last_version:
|
||||
return redirect('devhub.submit.version.distribution', addon.slug)
|
||||
channel = last_version.channel
|
||||
else:
|
||||
# Don't allow channel choice until rest of AMO supports it.
|
||||
channel = (amo.RELEASE_CHANNEL_LISTED if addon.is_listed else
|
||||
amo.RELEASE_CHANNEL_UNLISTED)
|
||||
return _submit_upload(request, addon, channel,
|
||||
'devhub.submit.version.details',
|
||||
'devhub.submit.version.finish')
|
||||
|
||||
|
||||
def _submit_details(request, addon, version):
|
||||
forms_list, context = [], {}
|
||||
if version and version.channel == amo.RELEASE_CHANNEL_UNLISTED:
|
||||
# Not a listed version ? Then nothing to do here.
|
||||
return redirect('devhub.submit.version.finish', addon.slug, version.pk)
|
||||
# Figure out the latest version early in order to pass the same instance to
|
||||
# each form that needs it (otherwise they might overwrite each other).
|
||||
latest_version = addon.find_latest_version(
|
||||
latest_version = version or addon.find_latest_version(
|
||||
channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
if not latest_version:
|
||||
# No listed version ? Then nothing to do in the listed submission flow.
|
||||
return redirect('devhub.submit.finish', addon.slug)
|
||||
post_data = request.POST if request.method == 'POST' else None
|
||||
show_all_fields = not version or not addon.has_complete_metadata()
|
||||
|
||||
describe_form = forms.DescribeForm(
|
||||
post_data, instance=addon, request=request)
|
||||
cat_form = addon_forms.CategoryFormSet(
|
||||
request.POST or None, addon=addon, request=request)
|
||||
license_form = forms.LicenseForm(post_data, version=latest_version)
|
||||
policy_form = forms.PolicyForm(post_data, addon=addon)
|
||||
reviewer_form = forms.ReviewerNotesForm(
|
||||
if show_all_fields:
|
||||
describe_form = forms.DescribeForm(
|
||||
post_data, instance=addon, request=request)
|
||||
cat_form = addon_forms.CategoryFormSet(
|
||||
post_data, addon=addon, request=request)
|
||||
license_form = forms.LicenseForm(
|
||||
post_data, version=latest_version, prefix='license')
|
||||
context.update(license_form.get_context())
|
||||
context.update(form=describe_form, cat_form=cat_form)
|
||||
forms_list.extend([describe_form, cat_form, context['license_form']])
|
||||
reviewer_form = forms.VersionForm(
|
||||
post_data, instance=latest_version)
|
||||
context.update(reviewer_form=reviewer_form)
|
||||
forms_list.append(reviewer_form)
|
||||
|
||||
context.update(license_form.get_context())
|
||||
context.update(form=describe_form, cat_form=cat_form,
|
||||
policy_form=policy_form, reviewer_form=reviewer_form)
|
||||
forms_.extend([describe_form, cat_form, context['license_form'],
|
||||
policy_form, reviewer_form])
|
||||
if request.method == 'POST' and all(
|
||||
form.is_valid() for form in forms_list):
|
||||
if show_all_fields:
|
||||
addon = describe_form.save()
|
||||
cat_form.save()
|
||||
license_form.save(log=False)
|
||||
reviewer_form.save()
|
||||
addon.update(status=amo.STATUS_NOMINATED)
|
||||
signals.submission_done.send(sender=addon)
|
||||
else:
|
||||
reviewer_form.save()
|
||||
|
||||
if request.method == 'POST' and all([form.is_valid() for form in forms_]):
|
||||
addon = describe_form.save(addon)
|
||||
cat_form.save()
|
||||
license_form.save(log=False)
|
||||
policy_form.save()
|
||||
reviewer_form.save()
|
||||
addon.update(status=amo.STATUS_NOMINATED)
|
||||
|
||||
signals.submission_done.send(sender=addon)
|
||||
|
||||
return redirect('devhub.submit.finish', addon.slug)
|
||||
context.update(addon=addon)
|
||||
return render(request, 'devhub/addons/submit/describe.html', context)
|
||||
if not version:
|
||||
return redirect('devhub.submit.finish', addon.slug)
|
||||
else:
|
||||
return redirect('devhub.submit.version.finish',
|
||||
addon.slug, version.id)
|
||||
context.update(addon=addon, submit_page='version' if version else 'addon')
|
||||
template = 'devhub/addons/submit/%s' % (
|
||||
'describe.html' if show_all_fields else 'describe_minimal.html')
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@dev_required(submitting=True)
|
||||
def submit_finish(request, addon_id, addon):
|
||||
# Bounce to the details step if incomplete
|
||||
if not addon.has_complete_metadata():
|
||||
return redirect('devhub.submit.details', addon.slug)
|
||||
# Bounce to the versions page if they don't have any versions.
|
||||
if not addon.versions.exists():
|
||||
return redirect(addon.get_dev_url('versions'))
|
||||
uploaded_version = addon.versions.latest()
|
||||
def submit_addon_details(request, addon_id, addon):
|
||||
return _submit_details(request, addon, None)
|
||||
|
||||
|
||||
@dev_required(submitting=True)
|
||||
@waffle_switch('step-version-upload')
|
||||
def submit_version_details(request, addon_id, addon, version_id):
|
||||
version = get_object_or_404(Version, id=version_id)
|
||||
return _submit_details(request, addon, version)
|
||||
|
||||
|
||||
def _submit_finish(request, addon, version):
|
||||
uploaded_version = version or addon.versions.latest()
|
||||
supported_platforms = uploaded_version.supported_platforms
|
||||
is_platform_specific = supported_platforms != [amo.PLATFORM_ALL]
|
||||
|
||||
|
@ -1499,7 +1601,8 @@ def submit_finish(request, addon_id, addon):
|
|||
# This should never happen.
|
||||
author = None
|
||||
|
||||
if (author and uploaded_version.channel == amo.RELEASE_CHANNEL_LISTED and
|
||||
if (not version and author and
|
||||
uploaded_version.channel == amo.RELEASE_CHANNEL_LISTED and
|
||||
not Version.objects.exclude(pk=uploaded_version.pk)
|
||||
.filter(addon__authors=author,
|
||||
channel=amo.RELEASE_CHANNEL_LISTED)
|
||||
|
@ -1519,10 +1622,30 @@ def submit_finish(request, addon_id, addon):
|
|||
tasks.send_welcome_email.delay(addon.id, [author.email], context)
|
||||
|
||||
return render(request, 'devhub/addons/submit/done.html',
|
||||
{'addon': addon, 'uploaded_version': uploaded_version,
|
||||
{'addon': addon,
|
||||
'uploaded_version': uploaded_version,
|
||||
'submit_page': 'version' if version else 'addon',
|
||||
'is_platform_specific': is_platform_specific})
|
||||
|
||||
|
||||
@dev_required(submitting=True)
|
||||
def submit_addon_finish(request, addon_id, addon):
|
||||
# Bounce to the details step if incomplete
|
||||
if not addon.has_complete_metadata():
|
||||
return redirect('devhub.submit.details', addon.slug)
|
||||
# Bounce to the versions page if they don't have any versions.
|
||||
if not addon.versions.exists():
|
||||
return redirect(addon.get_dev_url('versions'))
|
||||
return _submit_finish(request, addon, None)
|
||||
|
||||
|
||||
@dev_required
|
||||
@waffle_switch('step-version-upload')
|
||||
def submit_version_finish(request, addon_id, addon, version_id):
|
||||
version = get_object_or_404(Version, id=version_id)
|
||||
return _submit_finish(request, addon, version)
|
||||
|
||||
|
||||
@dev_required(submitting=True)
|
||||
def submit_resume(request, addon_id, addon):
|
||||
# Redirect to end and @submit_step will send us back if incomplete.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO waffle_switch (name, active, note, created, modified)
|
||||
VALUES ('mixed-listed-unlisted', 0, 'Allow mixed listed/unlisted versions on a single add-on.', NOW(), NOW());
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO waffle_switch (name, active, note, created, modified)
|
||||
VALUES ('step-version-upload', 0, 'Use step by step (listed/unlisted) version upload instead of inline.', NOW(), NOW());
|
||||
|
|
@ -145,7 +145,7 @@ class Version(OnChangeMixin, ModelBase):
|
|||
)
|
||||
log.info(
|
||||
'New version: %r (%s) from %r' % (version, version.id, upload))
|
||||
|
||||
amo.log(amo.LOG.ADD_VERSION, version, addon)
|
||||
# Update the add-on e10s compatibility since we're creating a new
|
||||
# version that may change that.
|
||||
e10s_compatibility = data.get('e10s_compatibility')
|
||||
|
|
|
@ -129,10 +129,19 @@ span.remove {
|
|||
cursor: help;
|
||||
}
|
||||
|
||||
.addon-submit-distribute * label span.helptext {
|
||||
.addon-submit-distribute * label span.helptext{
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p.addon-submit-distribute span.helptext{
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
}
|
||||
|
||||
p.addon-submit-distribute {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.tip:hover,
|
||||
a.remove:hover {
|
||||
background-color: #2A4364 !important;
|
||||
|
|
Загрузка…
Ссылка в новой задаче