Skip to content

Commit

Permalink
Bug 595319, 595320, 595325, devhub payments flow
Browse files Browse the repository at this point in the history
  • Loading branch information
potch committed Oct 20, 2010
1 parent e9b7294 commit f41fb79
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 41 deletions.
14 changes: 10 additions & 4 deletions apps/devhub/forms.py
Expand Up @@ -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):
Expand All @@ -166,8 +169,11 @@ def initial(addon):
'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


Expand Down
78 changes: 60 additions & 18 deletions apps/devhub/templates/devhub/addons/payments.html
Expand Up @@ -8,35 +8,77 @@
{{ 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) }}">
{{ csrf() }}
<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>
Expand Down
6 changes: 3 additions & 3 deletions apps/devhub/tests/test_views.py
Expand Up @@ -457,15 +457,15 @@ def test_dev_paypal_reqd(self):
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):
self.paypal_mock.return_value = False, 'error'
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'
Expand All @@ -482,7 +482,7 @@ def test_paypal_timeout(self):
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):
Expand Down
5 changes: 5 additions & 0 deletions apps/devhub/views.py
Expand Up @@ -9,7 +9,9 @@
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
Expand Down Expand Up @@ -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))
Expand Down
80 changes: 80 additions & 0 deletions media/css/zamboni/developers.css
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Binary file added media/img/zamboni/contributions/after.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/img/zamboni/contributions/passive.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/img/zamboni/contributions/roadblock.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 77 additions & 16 deletions media/js/zamboni/devhub.js
Expand Up @@ -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(" &nbsp;<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");
Expand All @@ -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()
Expand Down

0 comments on commit f41fb79

Please sign in to comment.