diff --git a/gratipay/models/package/__init__.py b/gratipay/models/package/__init__.py index 24af56d724..571e2cb971 100644 --- a/gratipay/models/package/__init__.py +++ b/gratipay/models/package/__init__.py @@ -40,3 +40,10 @@ def from_names(cls, package_manager, name): """ return cls.db.one("SELECT packages.*::packages FROM packages " "WHERE package_manager=%s and name=%s", (package_manager, name)) + + + # Emails + # ====== + + def send_confirmation_email(self, address): + pass diff --git a/js/gratipay/emails.js b/js/gratipay/emails.js index c135826f20..99d617ce3a 100644 --- a/js/gratipay/emails.js +++ b/js/gratipay/emails.js @@ -5,7 +5,6 @@ Gratipay.emails.post = function(e) { var $this = $(this); var action = this.className; var $inputs = $('.emails button, .emails input'); - console.log($this); var address = $this.parent().data('email') || $('input.add-email').val(); $inputs.prop('disabled', true); diff --git a/js/gratipay/packages.js b/js/gratipay/packages.js new file mode 100644 index 0000000000..675a0c4614 --- /dev/null +++ b/js/gratipay/packages.js @@ -0,0 +1,29 @@ +Gratipay.packages = {}; + +Gratipay.packages.post = function(e) { + e.preventDefault(); + var $this = $(this); + var action = 'add-email-and-claim-package'; + var package_id = $('input[name=package_id]').val(); + var email = $('input[name=email]:checked').val(); + + var $inputs = $('input, button'); + $inputs.prop('disabled', true); + + $.ajax({ + url: '/~' + Gratipay.username + '/emails/modify.json', + type: 'POST', + data: {action: action, address: email, package_id: package_id}, + dataType: 'json', + success: function (msg) { + if (msg) { + Gratipay.notification(msg, 'success'); + } + $inputs.prop('disabled', false); + }, + error: [ + function () { $inputs.prop('disabled', false); }, + Gratipay.error + ], + }); +}; diff --git a/scss/components/listing.scss b/scss/components/listing.scss index f40bb2f8c2..148ea54cf9 100644 --- a/scss/components/listing.scss +++ b/scss/components/listing.scss @@ -1,3 +1,9 @@ +.sorry { + text-align: center; + font: normal 12px/15px $Ideal; + color: $medium-gray; +} + table.listing { width: 100%; diff --git a/scss/pages/package.scss b/scss/pages/package.scss new file mode 100644 index 0000000000..d63f050ac8 --- /dev/null +++ b/scss/pages/package.scss @@ -0,0 +1,8 @@ +#package { + .emails { + margin: 1em 0; + li { + list-style: none; + } + } +} diff --git a/scss/pages/search.scss b/scss/pages/search.scss index 9275a7eff8..850723ae7f 100644 --- a/scss/pages/search.scss +++ b/scss/pages/search.scss @@ -16,12 +16,6 @@ } } - .sorry { - text-align: center; - font: normal 12px/15px $Ideal; - color: $medium-gray; - } - h2 { margin-top: 4em; &:first-of-type { diff --git a/tests/py/test_www_npm_package.py b/tests/py/test_www_npm_package.py index 8f9c5bec45..f5fa219bb7 100644 --- a/tests/py/test_www_npm_package.py +++ b/tests/py/test_www_npm_package.py @@ -1,13 +1,37 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals +from gratipay.models.package import NPM from gratipay.testing import Harness -class TestAnon(Harness): +class TestClaimingWorkflow(Harness): def setUp(self): self.make_package() - def test_gets_signin_page(self): - assert 'npm/foo has not been claimed' in self.client.GET('/on/npm/foo/').body + def test_anon_gets_signin_page_from_unclaimed(self): + body = self.client.GET('/on/npm/foo/').body + assert 'npm/foo has not been claimed' in body + assert 'with a couple clicks' in body + + def test_auth_gets_send_confirmation_page_from_unclaimed(self): + self.make_participant('bob', claimed_time='now') + body = self.client.GET('/on/npm/foo/', auth_as='bob').body + assert 'npm/foo has not been claimed' in body + assert 'using any email address' in body + assert 'alice@example.com' in body + + def test_auth_gets_multiple_options_if_present(self): + self.make_package(NPM, 'bar', 'Bar', ['alice@example.com', 'alice@example.net']) + self.make_participant('bob', claimed_time='now') + body = self.client.GET('/on/npm/bar/', auth_as='bob').body + assert 'using any email address' in body + assert 'alice@example.com' in body + assert 'alice@example.net' in body + + def test_auth_gets_something_if_no_emails(self): + self.make_package(NPM, 'bar', 'Bar', []) + self.make_participant('bob', claimed_time='now') + body = self.client.GET('/on/npm/bar/', auth_as='bob').body + assert "didn't find any email addresses" in body diff --git a/tests/ttw/test_package_claiming.py b/tests/ttw/test_package_claiming.py new file mode 100644 index 0000000000..f3eb02e969 --- /dev/null +++ b/tests/ttw/test_package_claiming.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, unicode_literals + +from gratipay.testing import BrowserHarness + + +class TestSendConfirmationLink(BrowserHarness): + + def check(self, choice=0): + self.make_participant('bob', claimed_time='now') + self.sign_in('bob') + self.visit('/on/npm/foo/') + self.css('input[type=radio]')[choice].click() + self.css('button')[0].click() + assert self.has_element('.notification.notification-success', 1) + assert self.has_text('Check alice@example.com for a confirmation link.') + + def test_appears_to_work(self): + self.make_package() + self.check() + + def test_works_when_there_are_multiple_addresses(self): + self.make_package(emails=['alice@example.com', 'bob@example.com']) + self.check() + + def test_can_send_to_second_email(self): + self.make_package(emails=['bob@example.com', 'alice@example.com']) + self.check(choice=1) diff --git a/www/assets/gratipay.css.spt b/www/assets/gratipay.css.spt index 71765ac61d..cfe1d38b6b 100644 --- a/www/assets/gratipay.css.spt +++ b/www/assets/gratipay.css.spt @@ -64,6 +64,7 @@ @import "scss/pages/history"; @import "scss/pages/identities"; @import "scss/pages/team"; +@import "scss/pages/package"; @import "scss/pages/profile-edit"; @import "scss/pages/giving"; @import "scss/pages/settings"; diff --git a/www/on/npm/%package/index.html.spt b/www/on/npm/%package/index.html.spt index e9d0693bff..327377bb7b 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -9,7 +9,7 @@ package = Package.from_names('npm', package_name) if package is None: raise Response(404) banner = package_name -page_id = "on-npm-foo" +page_id = "package" suppress_sidebar = True url = 'https://npmjs.com/package/' + package.name [---] @@ -26,16 +26,43 @@ url = 'https://npmjs.com/package/' + package.name {% endblock %} +{% block scripts %} + +{{ super() }} +{% endblock %} + {% block content %} -{% if user.ANON %}
{{ _('Is this yours? You can claim it on Gratipay with a couple clicks:') }}
-{% include "templates/sign-in-using.html" %} +{% if user.ANON %} +{{ _('Is this yours? You can claim it on Gratipay with a couple clicks:') }}
+ {% include "templates/sign-in-using.html" %} -{{ _("Gratipay helps companies and others pay for open source.") }} -{{ _("Learn more") }}
+{{ _('Gratipay helps companies and others pay for open source.') }} + {{ _("Learn more") }}
+{% else %} +{{ _( 'Is this yours? You can claim it on Gratipay using any email address {a}on file{_a} in the maintainers field in the npm registry.' + , a=('')|safe + , _a=''|safe + ) }} + {% if len(package.emails) == 0 %} +
{{ _("Sorry, we didn't find any email addresses on file.") }}
+ {% else %} + +