From cb000ed04ff250efe6c4484eb41a3a5fd506558d Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Tue, 24 Jan 2017 18:37:16 -0500 Subject: [PATCH 1/7] Start fleshing out send confirmation flow --- tests/py/test_www_npm_package.py | 14 +++++++++++--- www/on/npm/%package/index.html.spt | 24 ++++++++++++++++++------ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/tests/py/test_www_npm_package.py b/tests/py/test_www_npm_package.py index 8f9c5bec45..51fa907a16 100644 --- a/tests/py/test_www_npm_package.py +++ b/tests/py/test_www_npm_package.py @@ -4,10 +4,18 @@ 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 'with one of these email addresses' in body diff --git a/www/on/npm/%package/index.html.spt b/www/on/npm/%package/index.html.spt index e9d0693bff..465464958c 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -27,15 +27,27 @@ url = 'https://npmjs.com/package/' + package.name {% endblock %} {% block content %} -{% if user.ANON %}

{{ _( '{npm_package} has not been claimed on Gratipay.' , npm_package=('' + 'npm/' + package_name + '')|safe ) }}

-

{{ _('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" %} -

{{ _("What is Gratipay?") }}

-

{{ _("Gratipay helps companies and others pay for open source.") }} -{{ _("Learn more") }}

+

{{ _('What is Gratipay?') }}

+

{{ _('Gratipay helps companies and others pay for open source.') }} + {{ _("Learn more") }}

+{% else %} +

{{ _('Is this yours? You can claim it on Gratipay with one of these email addresses:') + }}

+
+ + + +
{% endif %} {% endblock %} From aa9b3e17a5a740bee29156ad45929d12e7fce88f Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 25 Jan 2017 08:43:17 -0500 Subject: [PATCH 2/7] Failing ttw test for sending a confirmation link --- tests/ttw/test_package_claiming.py | 17 +++++++++++++++++ www/on/npm/%package/index.html.spt | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/ttw/test_package_claiming.py diff --git a/tests/ttw/test_package_claiming.py b/tests/ttw/test_package_claiming.py new file mode 100644 index 0000000000..4d6a9b7c86 --- /dev/null +++ b/tests/ttw/test_package_claiming.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, unicode_literals + +from gratipay.testing import BrowserHarness + + +class Tests(BrowserHarness): + + def test_sending_a_confirmation_email_works(self): + self.make_package() + self.make_participant('alice', claimed_time='now') + self.sign_in('alice') + self.visit('/on/npm/foo/') + self.css('input[type=radio]').click() + self.css('button').click() + assert self.has_element('.notification.notification-success', 1) + assert self.has_text('A confirmation email has been sent') diff --git a/www/on/npm/%package/index.html.spt b/www/on/npm/%package/index.html.spt index 465464958c..5fa163ba7f 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -43,7 +43,7 @@ url = 'https://npmjs.com/package/' + package.name
    - {% for email in emails %} + {% for email in package.emails %}
  • {{ email }}
  • {% endfor %}
From 83a9ab14e1c7b2c5788a4f10996eda7977f19a3a Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 3 Feb 2017 16:00:12 -0500 Subject: [PATCH 3/7] Prune a console.log while we're paying attention --- js/gratipay/emails.js | 1 - 1 file changed, 1 deletion(-) 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); From d7560d904c3181393a60a3d6c446af9e7be96121 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Tue, 7 Feb 2017 20:59:23 -0500 Subject: [PATCH 4/7] Rough in sending a confirmation email --- gratipay/models/package/__init__.py | 7 ++++++ js/gratipay/packages.js | 35 ++++++++++++++++++++++++++ tests/ttw/test_package_claiming.py | 12 ++++----- www/on/npm/%package/index.html.spt | 26 ++++++++++++------- www/~/%username/emails/modify.json.spt | 6 +++++ 5 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 js/gratipay/packages.js 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/packages.js b/js/gratipay/packages.js new file mode 100644 index 0000000000..9edffa7fca --- /dev/null +++ b/js/gratipay/packages.js @@ -0,0 +1,35 @@ +Gratipay.packages = {}; + +Gratipay.packages.post = function(e) { + e.preventDefault(); + var $this = $(this); + var action = 'add-email-and-claim-package'; + var $inputs = $('input, button'); + var $selection = $('input[name=email]:checked'); + var address = $selection.val(); + var package_id = $selection.parent().parent().data('package-id'); + + $inputs.prop('disabled', true); + + $.ajax({ + url: '/~' + Gratipay.username + '/emails/modify.json', + type: 'POST', + data: {action: action, address: address, package_id: package_id}, + dataType: 'json', + success: function (msg) { + if (msg) { + Gratipay.notification(msg, 'success'); + } + if (action == 'add-email') { + $('input.add-email').val(''); + setTimeout(function(){ window.location.reload(); }, 3000); + return; + } + $inputs.prop('disabled', false); + }, + error: [ + function () { $inputs.prop('disabled', false); }, + Gratipay.error + ], + }); +}; diff --git a/tests/ttw/test_package_claiming.py b/tests/ttw/test_package_claiming.py index 4d6a9b7c86..60656c2d13 100644 --- a/tests/ttw/test_package_claiming.py +++ b/tests/ttw/test_package_claiming.py @@ -6,12 +6,12 @@ class Tests(BrowserHarness): - def test_sending_a_confirmation_email_works(self): + def test_sending_a_confirmation_email_appears_to_work(self): self.make_package() - self.make_participant('alice', claimed_time='now') - self.sign_in('alice') + self.make_participant('bob', claimed_time='now') + self.sign_in('bob') self.visit('/on/npm/foo/') - self.css('input[type=radio]').click() - self.css('button').click() + self.css('input[type=radio]')[0].click() + self.css('button')[0].click() assert self.has_element('.notification.notification-success', 1) - assert self.has_text('A confirmation email has been sent') + assert self.has_text('Check alice@example.com for a confirmation email.') diff --git a/www/on/npm/%package/index.html.spt b/www/on/npm/%package/index.html.spt index 5fa163ba7f..b97a787f9b 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -26,6 +26,15 @@ url = 'https://npmjs.com/package/' + package.name {% endblock %} +{% block scripts %} + +{{ super() }} +{% endblock %} + {% block content %}

{{ _( '{npm_package} has not been claimed on Gratipay.' , npm_package=('' + 'npm/' + package_name + '')|safe @@ -40,14 +49,13 @@ url = 'https://npmjs.com/package/' + package.name {% else %}

{{ _('Is this yours? You can claim it on Gratipay with one of these email addresses:') }}

- - -
    - {% for email in package.emails %} -
  • {{ email }}
  • - {% endfor %} -
- - + +
    + {% for i, email in enumerate(package.emails) %} +
  • +
  • + {% endfor %} +
+ {% endif %} {% endblock %} diff --git a/www/~/%username/emails/modify.json.spt b/www/~/%username/emails/modify.json.spt index 2451879bb5..9e094cdd75 100644 --- a/www/~/%username/emails/modify.json.spt +++ b/www/~/%username/emails/modify.json.spt @@ -6,6 +6,7 @@ import re from aspen import Response from gratipay.exceptions import ProblemChangingEmail from gratipay.utils import get_participant +from gratipay.models.package import Package email_re = re.compile(r'^[^@]+@[^@]+\.[^@]+$') @@ -36,6 +37,11 @@ elif action == 'set-primary': participant.update_email(address) elif action == 'remove': participant.remove_email(address) +elif action == 'add-email-and-claim-package': + package_id = request.body['package_id'] + package = Package.from_id(package_id) + package.send_confirmation_email(address) + msg = _("Check {email_address} for a confirmation email.", email_address=address) else: raise Response(400, 'unknown action "%s"' % action) From 12659d141f19063f4a3745010eeec81a2f43ef0a Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 8 Feb 2017 17:52:43 -0500 Subject: [PATCH 5/7] =?UTF-8?q?Round=20out=20confirmation=20form=20for=20z?= =?UTF-8?q?ero=20and=20=E2=88=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/gratipay/packages.js | 14 ++++---------- scss/components/listing.scss | 6 ++++++ scss/pages/search.scss | 6 ------ tests/py/test_www_npm_package.py | 17 ++++++++++++++++- tests/ttw/test_package_claiming.py | 21 ++++++++++++++++----- www/on/npm/%package/index.html.spt | 20 ++++++++++++++------ www/~/%username/emails/modify.json.spt | 2 +- 7 files changed, 57 insertions(+), 29 deletions(-) diff --git a/js/gratipay/packages.js b/js/gratipay/packages.js index 9edffa7fca..675a0c4614 100644 --- a/js/gratipay/packages.js +++ b/js/gratipay/packages.js @@ -4,27 +4,21 @@ Gratipay.packages.post = function(e) { e.preventDefault(); var $this = $(this); var action = 'add-email-and-claim-package'; - var $inputs = $('input, button'); - var $selection = $('input[name=email]:checked'); - var address = $selection.val(); - var package_id = $selection.parent().parent().data('package-id'); + 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: address, package_id: package_id}, + data: {action: action, address: email, package_id: package_id}, dataType: 'json', success: function (msg) { if (msg) { Gratipay.notification(msg, 'success'); } - if (action == 'add-email') { - $('input.add-email').val(''); - setTimeout(function(){ window.location.reload(); }, 3000); - return; - } $inputs.prop('disabled', false); }, 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/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 51fa907a16..2dd30ad31d 100644 --- a/tests/py/test_www_npm_package.py +++ b/tests/py/test_www_npm_package.py @@ -18,4 +18,19 @@ 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 'with one of these email addresses' 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 index 60656c2d13..f3eb02e969 100644 --- a/tests/ttw/test_package_claiming.py +++ b/tests/ttw/test_package_claiming.py @@ -4,14 +4,25 @@ from gratipay.testing import BrowserHarness -class Tests(BrowserHarness): +class TestSendConfirmationLink(BrowserHarness): - def test_sending_a_confirmation_email_appears_to_work(self): - self.make_package() + 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]')[0].click() + 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 email.') + 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/on/npm/%package/index.html.spt b/www/on/npm/%package/index.html.spt index b97a787f9b..909fb16051 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -12,6 +12,7 @@ banner = package_name page_id = "on-npm-foo" suppress_sidebar = True url = 'https://npmjs.com/package/' + package.name +nemails = len(package.emails) [---] {% extends "templates/base.html" %} @@ -47,15 +48,22 @@ url = 'https://npmjs.com/package/' + package.name

{{ _('Gratipay helps companies and others pay for open source.') }} {{ _("Learn more") }}

{% else %} -

{{ _('Is this yours? You can claim it on Gratipay with one of these email addresses:') - }}

- -
    +

    {{ _( '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 nemails == 0 %} +

    {{ _("Sorry, we didn't find any email addresses on file.") }}

    + {% else %} + +
      {% for i, email in enumerate(package.emails) %} -
    • +
    • {% endfor %}
    - + + {% endif %} {% endif %} {% endblock %} diff --git a/www/~/%username/emails/modify.json.spt b/www/~/%username/emails/modify.json.spt index 9e094cdd75..14ce8ac2e4 100644 --- a/www/~/%username/emails/modify.json.spt +++ b/www/~/%username/emails/modify.json.spt @@ -41,7 +41,7 @@ elif action == 'add-email-and-claim-package': package_id = request.body['package_id'] package = Package.from_id(package_id) package.send_confirmation_email(address) - msg = _("Check {email_address} for a confirmation email.", email_address=address) + msg = _("Check {email} for a confirmation link.", email=address) else: raise Response(400, 'unknown action "%s"' % action) From 3586969370852adab37abfef8fb70db3f2ebfc66 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 9 Feb 2017 08:34:35 -0500 Subject: [PATCH 6/7] Use constant --- tests/py/test_www_npm_package.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/py/test_www_npm_package.py b/tests/py/test_www_npm_package.py index 2dd30ad31d..f5fa219bb7 100644 --- a/tests/py/test_www_npm_package.py +++ b/tests/py/test_www_npm_package.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals +from gratipay.models.package import NPM from gratipay.testing import Harness @@ -22,7 +23,7 @@ def test_auth_gets_send_confirmation_page_from_unclaimed(self): 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_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 @@ -30,7 +31,7 @@ def test_auth_gets_multiple_options_if_present(self): assert 'alice@example.net' in body def test_auth_gets_something_if_no_emails(self): - self.make_package('npm', 'bar', 'Bar', []) + 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 From 455f06c67e56027839582c62ceeca8bab12b5351 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 9 Feb 2017 08:53:00 -0500 Subject: [PATCH 7/7] Lightly style the email list --- scss/pages/package.scss | 8 ++++++++ www/assets/gratipay.css.spt | 1 + www/on/npm/%package/index.html.spt | 7 +++---- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 scss/pages/package.scss 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/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 909fb16051..327377bb7b 100644 --- a/www/on/npm/%package/index.html.spt +++ b/www/on/npm/%package/index.html.spt @@ -9,10 +9,9 @@ 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 -nemails = len(package.emails) [---] {% extends "templates/base.html" %} @@ -52,11 +51,11 @@ nemails = len(package.emails) , a=('')|safe , _a=''|safe ) }} - {% if nemails == 0 %} + {% if len(package.emails) == 0 %}

    {{ _("Sorry, we didn't find any email addresses on file.") }}

    {% else %} -
      +
        {% for i, email in enumerate(package.emails) %}