add in the feature flag (bug 627077 and 635351)

This commit is contained in:
Andy McKay 2011-02-21 22:37:34 -08:00
Родитель e01ca949df
Коммит 71c2ce878e
12 изменённых файлов: 323 добавлений и 44 удалений

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

@ -1,3 +1,5 @@
from django.conf import settings
import jinja2
from jingo import register, env
@ -44,7 +46,8 @@ def flag(context, addon):
@register.function
def support_addon(addon):
t = env.get_template('addons/support_addon.html')
return jinja2.Markup(t.render(addon=addon, amo=amo))
return jinja2.Markup(t.render(addon=addon, amo=amo,
use_embedded=settings.PAYPAL_USE_EMBEDDED))
@register.inclusion_tag('addons/performance_note.html')

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

@ -119,6 +119,17 @@
'$', '<input type="text" name="onetime-amount" value=""/>')|safe }}
</label>
</li>
{% if not settings.PAYPAL_USE_EMBEDDED %}
<li>
<input type="radio" name="type" value="monthly"
id="contrib-monthly"/>
<label for="contrib-monthly">
{# L10n: {0} is a currency symbol (e.g., $),
{1} is an amount input field #}
{{ _('A regular monthly contribution of {0} {1}')|f(
'$', '</label><input type="text" name="monthly-amount" value=""/>')|safe }}
</li>
{% endif %}
</ul>
<h4 class="comment">

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

@ -23,5 +23,5 @@
{% else %}
<h4>{{ _('Payment completed') }}</h4>
{% endif %}
<p><a href="{{ url('addons.detail', addon.slug) }}">{{ _('Return to the addon.') }}</a></p>
<p><a id="paypal-result" href="{{ url('addons.detail', addon.slug) }}">{{ _('Return to the addon.') }}</a></p>
{% endblock %}

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

@ -21,7 +21,11 @@
{% endtrans %}
{% endif %}
{% else %}
{% if use_embedded %}
<a href="{{ url('addons.detail', addon.slug) }}#contribute-confirm" class="no-suggested-amount">{{ _('Support this add-on') }}</a>
{% else %}
<a href="{{ base }}">{{ _('Support this add-on') }}</a>
{% endif %}
{% endif %}
</div>
{% endif %}

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

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from email import utils
from decimal import Decimal
import re
from email import utils
import json
import re
import urlparse
from django import test
from django.conf import settings
@ -143,10 +144,11 @@ class TestContributeInstalled(test_utils.TestCase):
eq_(title[:37], 'Thank you for installing Gmail S/MIME')
class TestContribute(test_utils.TestCase):
class TestContributeEmbedded(test_utils.TestCase):
fixtures = ['base/apps', 'base/addon_3615', 'base/addon_592']
def setUp(self):
settings.PAYPAL_USE_EMBEDDED = True
self.addon = Addon.objects.get(pk=592)
self.detail_url = reverse('addons.detail', args=[self.addon.slug])
@ -238,6 +240,150 @@ class TestContribute(test_utils.TestCase):
assert not json.loads(res.content)['paykey']
class TestContribute(test_utils.TestCase):
fixtures = ['base/apps', 'base/addon_3615', 'base/addon_592']
def setUp(self):
settings.PAYPAL_USE_EMBEDDED = False
def test_invalid_is_404(self):
"""we get a 404 in case of invalid addon id"""
response = self.client.get(reverse('addons.contribute', args=[1]))
eq_(response.status_code, 404)
def test_redirect_params_no_type(self):
"""Test that we have the required ppal params when no type is given"""
response = self.client.get(reverse('addons.contribute',
args=['a592']), follow=True)
redirect_url = response.redirect_chain[0][0]
required_params = ['bn', 'business', 'charset', 'cmd', 'item_name',
'no_shipping', 'notify_url',
'return', 'item_number']
for param in required_params:
assert(redirect_url.find(param + '=') > -1), \
"param [%s] not found" % param
def test_redirect_params_common(self):
"""Test for the common values that do not change based on type,
Check that they have expected values"""
response = self.client.get(reverse('addons.contribute',
args=['a592']), follow=True)
redirect_url = response.redirect_chain[0][0]
assert(re.search('business=([^&]+)', redirect_url))
common_params = {'bn': r'-AddonID592',
'business': r'gmailsmime%40seantek.com',
'charset': r'utf-8',
'cmd': r'_donations',
'item_name': r'Contribution\+for\+Gmail\+S%2FMIME',
'no_shipping': r'1',
'notify_url': r'%2Fservices%2Fpaypal',
'return': r'x',
'item_number': r'[a-f\d]{32}'}
message = 'param [%s] unexpected value: given [%s], ' \
+ 'expected pattern [%s]'
for param, value_pattern in common_params.items():
match = re.search(r'%s=([^&]+)' % param, redirect_url)
assert(match and re.search(value_pattern, match.group(1))), \
message % (param, match.group(1), value_pattern)
def test_redirect_params_type_suggested(self):
"""Test that we have the required ppal param when type
suggested is given"""
request_params = '?type=suggested'
response = self.client.get(reverse('addons.contribute',
args=['a592']) + request_params,
follow=True)
redirect_url = response.redirect_chain[0][0]
required_params = ['amount', 'bn', 'business', 'charset',
'cmd', 'item_name', 'no_shipping', 'notify_url',
'return', 'item_number']
for param in required_params:
assert(redirect_url.find(param + '=') > -1), \
"param [%s] not found" % param
def test_redirect_params_type_onetime(self):
"""Test that we have the required ppal param when
type onetime is given"""
request_params = '?type=onetime&onetime-amount=42'
response = self.client.get(reverse('addons.contribute',
args=['a592']) + request_params,
follow=True)
redirect_url = response.redirect_chain[0][0]
required_params = ['amount', 'bn', 'business', 'charset', 'cmd',
'item_name', 'no_shipping', 'notify_url',
'return', 'item_number']
for param in required_params:
assert(redirect_url.find(param + '=') > -1), \
"param [%s] not found" % param
assert(redirect_url.find('amount=42') > -1)
def test_ppal_return_url_not_relative(self):
response = self.client.get(reverse('addons.contribute',
args=['a592']), follow=True)
redirect_url = response.redirect_chain[0][0]
assert(re.search('\?|&return=https?%3A%2F%2F', redirect_url)), \
("return URL param did not start w/ "
"http%3A%2F%2F (http://) [%s]" % redirect_url)
def test_redirect_params_type_monthly(self):
"""Test that we have the required ppal param when
type monthly is given"""
request_params = '?type=monthly&monthly-amount=42'
response = self.client.get(reverse('addons.contribute',
args=['a592']) + request_params,
follow=True)
redirect_url = response.redirect_chain[0][0]
required_params = ['no_note', 'a3', 't3', 'p3', 'bn', 'business',
'charset', 'cmd', 'item_name', 'no_shipping',
'notify_url', 'return', 'item_number']
for param in required_params:
assert(redirect_url.find(param + '=') > -1), \
"param [%s] not found" % param
assert(redirect_url.find('cmd=_xclick-subscriptions') > -1), \
'param a3 was not 42'
assert(redirect_url.find('p3=12') > -1), 'param p3 was not 12'
assert(redirect_url.find('t3=M') > -1), 'param t3 was not M'
assert(redirect_url.find('a3=42') > -1), 'param a3 was not 42'
assert(redirect_url.find('no_note=1') > -1), 'param no_note was not 1'
def test_paypal_bounce(self):
"""Paypal is retarded and posts to this page."""
args = dict(args=['a3615'])
r = self.client.post(reverse('addons.thanks', **args))
self.assertRedirects(r, reverse('addons.detail', **args))
def test_unicode_comment(self):
r = self.client.get(reverse('addons.contribute', args=['a592']),
{'comment': u'版本历史记录'})
eq_(r.status_code, 302)
assert r['Location'].startswith(settings.PAYPAL_CGI_URL)
def test_organization(self):
c = Charity.objects.create(name='moz', url='moz.com', paypal='mozcom')
addon = Addon.objects.get(id=592)
addon.update(charity=c)
r = self.client.get(reverse('addons.contribute', args=['a592']))
eq_(r.status_code, 302)
qs = dict(urlparse.parse_qsl(r['Location']))
eq_(qs['item_name'], 'Contribution for moz')
eq_(qs['business'], 'mozcom')
contrib = Contribution.objects.get(addon=addon)
eq_(addon.charity_id, contrib.charity_id)
def test_no_org(self):
addon = Addon.objects.get(id=592)
r = self.client.get(reverse('addons.contribute', args=['a592']))
eq_(r.status_code, 302)
contrib = Contribution.objects.get(addon=addon)
eq_(contrib.charity_id, None)
class TestDeveloperPages(test_utils.TestCase):
fixtures = ['base/apps', 'base/addon_3615', 'base/addon_592',
'base/users', 'addons/eula+contrib-addon',

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

@ -1,4 +1,5 @@
from django.conf.urls.defaults import patterns, url, include
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import redirect
from . import views
@ -20,9 +21,14 @@ detail_patterns = patterns('',
{'page': 'roadblock'}, name='addons.roadblock'),
url('^contribute/installed/', views.developers,
{'page': 'installed'}, name='addons.installed'),
url('^contribute/thanks',
csrf_exempt(lambda r, addon_id: redirect('addons.detail', addon_id)),
name='addons.thanks'),
url('^contribute/$', views.contribute, name='addons.contribute'),
url('^contribute/(?P<status>cancel|complete)$',
views.paypal_result, name='addons.paypal'),
url('^contribute/(?P<status>cancel|complete)$', views.paypal_result,
name='addons.paypal'),
('^about$', lambda r, addon_id: redirect('addons.installed',
addon_id, permanent=True)),

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

@ -412,8 +412,55 @@ def developers(request, addon, page):
'page': page, 'version': version})
@addon_view
def contribute(request, addon):
def old_contribute(request, addon):
contrib_type = request.GET.get('type', '')
is_suggested = contrib_type == 'suggested'
source = request.GET.get('source', '')
comment = request.GET.get('comment', '')
amount = {
'suggested': addon.suggested_amount,
'onetime': request.GET.get('onetime-amount', ''),
'monthly': request.GET.get('monthly-amount', '')}.get(contrib_type, '')
contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()
contrib = Contribution(addon_id=addon.id,
charity_id=addon.charity_id,
amount=amount,
source=source,
source_locale=request.LANG,
annoying=addon.annoying,
uuid=str(contribution_uuid),
is_suggested=is_suggested,
suggested_amount=addon.suggested_amount,
comment=comment)
contrib.save()
return_url = "%s?%s" % (reverse('addons.thanks', args=[addon.slug]),
urllib.urlencode({'uuid': contribution_uuid}))
# L10n: {0} is an add-on name.
if addon.charity:
name, paypal = addon.charity.name, addon.charity.paypal
else:
name, paypal = addon.name, addon.paypal_id
contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))
redirect_url_params = contribute_url_params(
paypal,
addon.id,
contrib_for,
absolutify(return_url),
amount,
contribution_uuid,
contrib_type == 'monthly',
comment)
return http.HttpResponseRedirect(settings.PAYPAL_CGI_URL
+ '?'
+ urllib.urlencode(redirect_url_params))
def embedded_contribute(request, addon):
contrib_type = request.GET.get('type', 'suggested')
is_suggested = contrib_type == 'suggested'
source = request.GET.get('source', '')
@ -479,6 +526,56 @@ def contribute(request, addon):
return http.HttpResponseRedirect(url)
@addon_view
def contribute(request, addon):
if settings.PAYPAL_USE_EMBEDDED:
return embedded_contribute(request, addon)
else:
return old_contribute(request, addon)
def contribute_url_params(business, addon_id, item_name, return_url,
amount='', item_number='',
monthly=False, comment=''):
lang = translation.get_language()
try:
paypal_lang = settings.PAYPAL_COUNTRYMAP[lang]
except KeyError:
lang = lang.split('-')[0]
paypal_lang = settings.PAYPAL_COUNTRYMAP.get(lang, 'US')
# Get all the data elements that will be URL params
# on the Paypal redirect URL.
data = {'business': business,
'item_name': item_name,
'item_number': item_number,
'bn': settings.PAYPAL_BN + '-AddonID' + str(addon_id),
'no_shipping': '1',
'return': return_url,
'charset': 'utf-8',
'lc': paypal_lang,
'notify_url': "%s%s" % (settings.SERVICES_URL,
reverse('amo.paypal'))}
if not monthly:
data['cmd'] = '_donations'
if amount:
data['amount'] = amount
else:
data.update({
'cmd': '_xclick-subscriptions',
'p3': '12', # duration: for 12 months
't3': 'M', # time unit, 'M' for month
'a3': amount, # recurring contribution amount
'no_note': '1'}) # required: no "note" text field for user
if comment:
data['custom'] = comment
return data
@addon_view
def paypal_result(request, addon, status):
uuid = request.GET.get('uuid')

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

@ -136,6 +136,7 @@ class TestPaypal(test_utils.TestCase):
self.url = reverse('amo.paypal')
self.item = 1234567890
self.client = Client()
settings.PAYPAL_USE_EMBEDDED = True
def urlopener(self, status):
m = Mock()

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

@ -56,28 +56,32 @@ var contributions = {
$(this).find('#contrib-too-much').show();
return false;
}
if (parseFloat(amt) >= 0.01) {
if (parseFloat(amt) < 0.01) {
$(this).find('#contrib-too-little').show();
return false;
}
}
var $self = $(this);
$.ajax({type: 'GET',
url: $(this).attr('action') + '?result_type=json',
data: $(this).serialize(),
success: function(json) {
if (json.paykey) {
$.getScript($('body').attr('data-paypal-url'), function() {
dgFlow = new PAYPAL.apps.DGFlow();
dgFlow.startFlow(json.url);
$self.find('span.cancel a').click();
})
} else {
$self.find('#paypal-error').show();
if ($('body').attr('data-paypal-url')) {
var $self = $(this);
$.ajax({type: 'GET',
url: $(this).attr('action') + '?result_type=json',
data: $(this).serialize(),
success: function(json) {
if (json.paykey) {
$.getScript($('body').attr('data-paypal-url'), function() {
dgFlow = new PAYPAL.apps.DGFlow();
dgFlow.startFlow(json.url);
$self.find('span.cancel a').click();
});
} else {
$self.find('#paypal-error').show();
}
}
}
});
return false;
});
return false;
} else {
return true;
}
});
// enable overlay; make sure we have the jqm package available.

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

@ -2,25 +2,30 @@ $(document).ready(function() {
$("#contribute-why").popup("#contribute-more-info", {
pointTo: "#contribute-more-info"
});
$('div.contribute a.suggested-amount').bind('click', function(event) {
$.ajax({type: 'GET',
url: $(this).attr('href') + '?result_type=json',
success: function(json) {
$.getScript($('body').attr('data-paypal-url'), function() {
dgFlow = new PAYPAL.apps.DGFlow();
dgFlow.startFlow(json.url);
});
}
if ($('body').attr('data-paypal-url')) {
$('div.contribute a.suggested-amount').bind('click', function(event) {
$.ajax({type: 'GET',
url: $(this).attr('href') + '?result_type=json',
success: function(json) {
$.getScript($('body').attr('data-paypal-url'), function() {
dgFlow = new PAYPAL.apps.DGFlow();
dgFlow.startFlow(json.url);
});
}
});
return false;
});
return false;
});
}
});
top_dgFlow = top.dgFlow || (top.opener && top.opener.top.dgFlow);
if (top_dgFlow != null) {
top_dgFlow.closeFlow();
if (top != null) {
top.close();
if ($('body').attr('data-paypal-url')) {
if ($('#paypal-result').length) {
top_dgFlow = top.dgFlow || (top.opener && top.opener.top.dgFlow);
if (top_dgFlow !== null) {
top_dgFlow.closeFlow();
if (top !== null) {
top.close();
}
}
}
}

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

@ -590,6 +590,8 @@ PAYPAL_API_VERSION = '50'
PAYPAL_APP_ID = ''
PAYPAL_BN = ''
PAYPAL_CGI_URL = 'https://www.paypal.com/cgi-bin/webscr'
PAYPAL_USE_EMBEDDED = True
PAYPAL_PAY_URL = 'https://paypal.com/adaptivepayments/pay'
PAYPAL_FLOW_URL = 'https://paypal.com/webapps/adaptivepayment/flow/pay'
PAYPAL_JS_URL = 'https://www.paypalobjects.com/js/external/dg.js'

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

@ -34,7 +34,7 @@
data-anonymous="{{ (not request.user.is_authenticated())|json }}"
data-readonly="{{ settings.READ_ONLY|json }}"
data-media-url="{{ MEDIA_URL }}"
data-paypal-url="{{ settings.PAYPAL_JS_URL }}"
{% if settings.PAYPAL_USE_EMBEDDED %}data-paypal-url="{{ settings.PAYPAL_JS_URL }}"{% endif %}
{% block bodyattrs %}{% endblock %}>
{% if ADMIN_MESSAGE or settings.READ_ONLY%}