Bug 595319, 595320, 595325, devhub payments flow
This commit is contained in:
Родитель
e9b72942b2
Коммит
f41fb7961b
|
@ -149,12 +149,15 @@ class ContribForm(happyforms.ModelForm):
|
|||
('org', _lazy('An organization of my choice')))
|
||||
|
||||
recipient = forms.ChoiceField(choices=RECIPIENTS,
|
||||
widget=forms.RadioSelect())
|
||||
widget=forms.RadioSelect(attrs={'class': 'recipient'}))
|
||||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('paypal_id', 'suggested_amount', 'annoying')
|
||||
widgets = {'annoying': forms.RadioSelect()}
|
||||
widgets = {
|
||||
'annoying': forms.RadioSelect(),
|
||||
'suggested_amount': forms.TextInput(attrs={'class': 'short'}),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def initial(addon):
|
||||
|
@ -166,8 +169,11 @@ class ContribForm(happyforms.ModelForm):
|
|||
'annoying': addon.annoying or amo.CONTRIB_PASSIVE}
|
||||
|
||||
def clean(self):
|
||||
if self.cleaned_data['recipient'] == 'dev':
|
||||
check_paypal_id(self.cleaned_data['paypal_id'])
|
||||
try:
|
||||
if not self.errors and self.cleaned_data['recipient'] == 'dev':
|
||||
check_paypal_id(self.cleaned_data['paypal_id'])
|
||||
except forms.ValidationError, e:
|
||||
self.errors['paypal_id'] = self.error_class(e.messages)
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
{{ dev_breadcrumbs(addon) }}
|
||||
<h2>{{ title }}</h2>
|
||||
</header>
|
||||
<section class="primary" role="main">
|
||||
{% if addon.takes_contributions %}
|
||||
<section class="primary payments" role="main" id="edit-addon">
|
||||
{% set contrib = addon.takes_contributions %}
|
||||
{% if contrib %}
|
||||
<div id="status-bar">
|
||||
<p>{{ _('You are currently requesting <b>contributions</b> from users')|safe }}</p>
|
||||
<form method="post" action="{{ url('devhub.addons.payments.disable', addon.id) }}">
|
||||
|
@ -17,26 +18,67 @@
|
|||
<button type="submit">{{ _('Disable Contributions') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="intro">
|
||||
<h3>{{ _('Contributions') }}</h3>
|
||||
<p>{{ _('Voluntary contributions provide a way for users to support your add-on financially. With contributions, you can:') }}</p>
|
||||
<ul>
|
||||
<li>{{ _('Ask for donations in most places your add-on appears') }}</li>
|
||||
<li>{{ _('Allow users to make payments with a credit card or PayPal account') }}</li>
|
||||
<li>{{ _('Receive contributions in your PayPal account or send them to an organization of your choice') }}</li>
|
||||
</ul>
|
||||
<div class="button-wrapper">
|
||||
<a href="#setup" id="do-setup" class="button prominent">{{ _('Set Up Contributions') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="intro">
|
||||
<h3>{{ _('Contributions') }}</h3>
|
||||
<p>{{ _('Voluntary contributions provide a way for users to support your add-on financially. With contributions, you can:') }}</p>
|
||||
<ul>
|
||||
<li>{{ _('Ask for donations in most places your add-on appears') }}</li>
|
||||
<li>{{ _('Allow users to make payments with a credit card or PayPal account') }}</li>
|
||||
<li>{{ _('Receive contributions in your PayPal account or send them to a charity') }}</li>
|
||||
</ul>
|
||||
<a href="#setup" class="button">{{ _('Set Up Contributions') }}</a>
|
||||
</div>
|
||||
|
||||
<div id="setup">
|
||||
<h3>{{ _('Set up Contributions') }}</h3>
|
||||
<p>{{ _('Fill in the fields below to begin asking for voluntary contributions from users.') }}</p>
|
||||
<div id="setup" class="{{ 'hidden' if not contrib }}">
|
||||
<h3>{{ _('Contributions') if contrib else _('Set up Contributions') }}</h3>
|
||||
<p class="{{ 'hidden' if contrib }}">{{ _('Fill in the fields below to begin asking for voluntary contributions from users.') }}</p>
|
||||
<form method="post" action="">
|
||||
{{ csrf() }}
|
||||
<table>{{ contrib_form|safe }}</table>
|
||||
<table>{{ charity_form|safe }}</table>
|
||||
<button type="submit">GO GO GO</button>
|
||||
{% set values = contrib_form.data if contrib_form.is_bound else contrib_form.initial %}
|
||||
<div>
|
||||
{{ contrib_form.non_field_errors()|safe }}
|
||||
{{ contrib_form.recipient.errors|safe }}
|
||||
<b>{{ _('Who will receive contributions to this add-on?') }}</b>
|
||||
{{ contrib_form.recipient|safe }}
|
||||
<div id="org-org" class="brform paypal {{ 'hidden' if (values.recipient != 'org') }}">
|
||||
{{ charity_form.non_field_errors()|safe }}
|
||||
{{ charity_form.name.errors|safe }}
|
||||
<label for="id_charity-name">{{ _('What is the name of the organization?') }}</label>
|
||||
{{ charity_form.name|safe }}
|
||||
{{ charity_form.url.errors|safe }}
|
||||
<label for="id_charity-url">{{ _('What is the URL of the organization?') }}</label>
|
||||
{{ charity_form.url|safe }}
|
||||
{{ charity_form.paypal.errors|safe }}
|
||||
<label for="id_charity-paypal">{{ _('What is the PayPal ID of the charity?') }}</label>
|
||||
{{ charity_form.paypal|safe }}
|
||||
</div>
|
||||
</div>
|
||||
<div id="org-dev" class="brform paypal {{ 'hidden' if (values.recipient != 'dev') }}">
|
||||
{{ contrib_form.paypal_id.errors|safe }}
|
||||
<label for="id_paypal_id">{{ _('What is your PayPal ID?') }}</label>
|
||||
<div>{{ contrib_form.paypal_id|safe }} <a class="extra" href="{{ settings.PAYPAL_CGI_URL + '?cmd=_registration-run' }}">{{ _('Sign up for Paypal') }}</a></div>
|
||||
</div>
|
||||
<div class="brform">
|
||||
{{ contrib_form.suggested_amount.errors|safe }}
|
||||
<label for="id_suggested_amount">{{ _('What is your suggested contribution?') }}</label>
|
||||
<div class="extra">{{ contrib_form.suggested_amount.help_text }}</div>
|
||||
<div>{{ contrib_form.suggested_amount|safe }} USD</div>
|
||||
</div>
|
||||
<div class="nag">
|
||||
{{ contrib_form.annoying.errors|safe }}
|
||||
<b>{{ _('When should users be asked for contributions?') }}</b>
|
||||
{{ contrib_form.annoying|safe }}
|
||||
</div>
|
||||
<button type="submit">{{ _('Save Changes') if contrib else _('Activate Contributions') }}</button>
|
||||
<span class="{{ 'hidden' if contrib }}">
|
||||
{% trans %}
|
||||
or <a id="setup-cancel" href="#">Cancel</a>
|
||||
{% endtrans %}
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -457,7 +457,7 @@ class TestEditPayments(test_utils.TestCase):
|
|||
d = dict(recipient='dev', suggested_amount=2,
|
||||
annoying=amo.CONTRIB_PASSIVE)
|
||||
r = self.client.post(self.url, d)
|
||||
self.assertFormError(r, 'contrib_form', None,
|
||||
self.assertFormError(r, 'contrib_form', 'paypal_id',
|
||||
'PayPal id required to accept contributions.')
|
||||
|
||||
def test_bad_paypal_id_dev(self):
|
||||
|
@ -465,7 +465,7 @@ class TestEditPayments(test_utils.TestCase):
|
|||
d = dict(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
|
||||
annoying=amo.CONTRIB_AFTER)
|
||||
r = self.client.post(self.url, d)
|
||||
self.assertFormError(r, 'contrib_form', None, 'error')
|
||||
self.assertFormError(r, 'contrib_form', 'paypal_id', 'error')
|
||||
|
||||
def test_bad_paypal_id_charity(self):
|
||||
self.paypal_mock.return_value = False, 'error'
|
||||
|
@ -482,7 +482,7 @@ class TestEditPayments(test_utils.TestCase):
|
|||
d = dict(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
|
||||
annoying=amo.CONTRIB_AFTER)
|
||||
r = self.client.post(self.url, d)
|
||||
self.assertFormError(r, 'contrib_form', None,
|
||||
self.assertFormError(r, 'contrib_form', 'paypal_id',
|
||||
'Could not validate PayPal id.')
|
||||
|
||||
def test_charity_details_reqd(self):
|
||||
|
|
|
@ -9,7 +9,9 @@ import commonware.log
|
|||
import jingo
|
||||
import path
|
||||
from tower import ugettext_lazy as _lazy
|
||||
from tower import ugettext as _
|
||||
|
||||
from amo import messages
|
||||
import amo.utils
|
||||
from amo.decorators import json_view, login_required, post_required
|
||||
from access import acl
|
||||
|
@ -177,7 +179,10 @@ def payments(request, addon_id, addon):
|
|||
addon.charity = charity_form.save()
|
||||
if valid:
|
||||
addon.save()
|
||||
messages.success(request, _('Changes successfully saved.'))
|
||||
return redirect('devhub.addons.payments', addon_id)
|
||||
if charity_form.errors or contrib_form.errors:
|
||||
messages.error(request, _('There were errors in your submission.'))
|
||||
return jingo.render(request, 'devhub/addons/payments.html',
|
||||
dict(addon=addon, charity_form=charity_form,
|
||||
contrib_form=contrib_form))
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
/*clearfix*/
|
||||
.brform:after,
|
||||
#status-bar:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
/* @group Edit addon */
|
||||
#edit-addon .label {
|
||||
margin-right:5px;
|
||||
|
@ -397,6 +406,77 @@ input.valid {
|
|||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Payments */
|
||||
.payments .intro {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #C9E8F3;
|
||||
padding: 1em;
|
||||
width: 300px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
display: inline-block;
|
||||
}
|
||||
.payments .intro ul {
|
||||
list-style-type: disc;
|
||||
padding: 0 0 .5em 1em;
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 1px dotted #ADD0DC;
|
||||
}
|
||||
.payments .intro ul,
|
||||
.payments .intro p {
|
||||
font-size: .9em;
|
||||
}
|
||||
.payments .intro .button-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
#edit-addon .brform label,
|
||||
#edit-addon .b {
|
||||
font-weight: 500;
|
||||
}
|
||||
.brform > label,
|
||||
.brform > input,
|
||||
.brform > div,
|
||||
.brform > ul {
|
||||
float: left;
|
||||
clear: left;
|
||||
}
|
||||
.payments form > div {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.paypal {
|
||||
margin-left: 2em;
|
||||
}
|
||||
.extra {
|
||||
font-size: .9em;
|
||||
}
|
||||
a.extra {
|
||||
margin-left: .5em;
|
||||
}
|
||||
#status-bar form {
|
||||
float: right;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: .9em;
|
||||
font-weight: 500;
|
||||
}
|
||||
#status-bar p {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
#status-bar {
|
||||
padding: .5em 1em;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2em;
|
||||
background: #fff;
|
||||
border: 1px solid #C9E8F3;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Recent Activity */
|
||||
.secondary-feed {
|
||||
padding: 1em 0 0 1em;
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 150 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 188 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 162 KiB |
|
@ -46,10 +46,87 @@ $("#user-form-template .email-autocomplete")
|
|||
|
||||
$(document).ready(function() {
|
||||
|
||||
//Ownership
|
||||
if ($("#author_list").length) {
|
||||
initAuthorFields();
|
||||
}
|
||||
|
||||
//Payments
|
||||
if ($('.payments').length) {
|
||||
initPayments();
|
||||
}
|
||||
});
|
||||
|
||||
function initPayments() {
|
||||
var previews = [
|
||||
"img/zamboni/contributions/passive.png",
|
||||
"img/zamboni/contributions/after.png",
|
||||
"img/zamboni/contributions/roadblock.png",
|
||||
],
|
||||
media_url = $("body").attr("data-media-url"),
|
||||
to = false,
|
||||
img = $("<img id='contribution-preview'/>");
|
||||
moz = $("input[value=moz]");
|
||||
img.hide().appendTo($("body"));
|
||||
moz.parent().after(
|
||||
$("<a class='extra' target='_blank' href='http://www.mozilla.org/foundation/donate.html'>"+gettext('Learn more')+"</a>"));
|
||||
$(".nag li label").each(function (i,v) {
|
||||
var pl = new Image();
|
||||
pl.src = media_url + previews[i];
|
||||
$(this).after(format(" <a class='extra' href='{0}{1}'>{2}</a>", [media_url, previews[i], gettext('Example')]));
|
||||
});
|
||||
$(".nag").delegate("a.extra", "mouseover", function(e) {
|
||||
var tgt = $(this);
|
||||
img.attr("src", tgt.attr("href")).css({
|
||||
position: 'absolute',
|
||||
'pointer-events': 'none',
|
||||
top: tgt.offset().top-350,
|
||||
left: ($(document).width()-755)/2
|
||||
});
|
||||
clearTimeout(to);
|
||||
to = setTimeout(function() {
|
||||
img.fadeIn(100);
|
||||
}, 300);
|
||||
}).delegate("a.extra", "mouseout", function(e) {
|
||||
clearTimeout(to);
|
||||
img.fadeOut(100);
|
||||
})
|
||||
.delegate("a.extra", "click", function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
$("#do-setup").click(function (e) {
|
||||
e.preventDefault();
|
||||
$("#setup").removeClass("hidden").show();
|
||||
$(".intro").hide();
|
||||
});
|
||||
$("#setup-cancel").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(".intro").show();
|
||||
$("#setup").hide();
|
||||
});
|
||||
$(".recipient").change(function (e) {
|
||||
var v = $(this).val();
|
||||
$(".paypal").hide(200);
|
||||
$(format("#org-{0}", [v])).removeClass("hidden").show(200);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function initAuthorFields() {
|
||||
var request = false,
|
||||
timeout = false,
|
||||
manager = $("#id_form-TOTAL_FORMS"),
|
||||
empty_form = template($("#user-form-template").html().replace(/__prefix__/g, "{0}")),
|
||||
author_list = $("#author_list");
|
||||
author_list.sortable({
|
||||
items: ".author",
|
||||
handle: ".handle",
|
||||
containment: author_list,
|
||||
tolerance: "pointer",
|
||||
update: renumberAuthors
|
||||
});
|
||||
addAuthorRow();
|
||||
|
||||
$("#id_has_eula").change(function (e) {
|
||||
if ($(this).attr("checked")) {
|
||||
$(".eula").show().removeClass("hidden");
|
||||
|
@ -72,22 +149,6 @@ $(document).ready(function() {
|
|||
$(".license-other").hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function initAuthorFields() {
|
||||
var request = false,
|
||||
timeout = false,
|
||||
manager = $("#id_form-TOTAL_FORMS"),
|
||||
empty_form = template($("#user-form-template").html().replace(/__prefix__/g, "{0}")),
|
||||
author_list = $("#author_list");
|
||||
author_list.sortable({
|
||||
items: ".author",
|
||||
handle: ".handle",
|
||||
containment: author_list,
|
||||
tolerance: "pointer",
|
||||
update: renumberAuthors
|
||||
});
|
||||
addAuthorRow();
|
||||
|
||||
$(".author .errorlist").each(function() {
|
||||
$(this).parent()
|
||||
|
|
Загрузка…
Ссылка в новой задаче