From a487ef5beee4511838b8134ccc522a002a210272 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Tue, 21 Feb 2017 10:38:46 -0500 Subject: [PATCH] Shelve --- emails/claim-package.spt | 17 +++++++++ gratipay/models/package/__init__.py | 7 ---- gratipay/models/participant/email.py | 13 +++++-- .../models/participant/package_claiming.py | 37 +++++++++++++++++++ gratipay/testing/harness.py | 12 +++--- tests/py/test_packages.py | 10 +++++ www/~/%username/emails/modify.json.spt | 5 +-- 7 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 emails/claim-package.spt create mode 100644 gratipay/models/participant/package_claiming.py diff --git a/emails/claim-package.spt b/emails/claim-package.spt new file mode 100644 index 0000000000..cbbd9fdb57 --- /dev/null +++ b/emails/claim-package.spt @@ -0,0 +1,17 @@ +{{ _("Claim {0} on Gratipay?", project) }} + +[---] text/html +{{ _("We've received a request to connect {0} to the {1} account on Gratipay. Sound familiar?", + ('%s'|safe) % email, + ('{0}'|safe).format(username)) }} +
+
+{{ _("Yes, proceed!") }} + +[---] text/plain +{{ _("We've received a request to connect {0} to the {1} account on Gratipay. Sound familiar?", + email, username) }} + +{{ _("Follow this link to finish connecting your email:") }} + +{{ link }} diff --git a/gratipay/models/package/__init__.py b/gratipay/models/package/__init__.py index 571e2cb971..24af56d724 100644 --- a/gratipay/models/package/__init__.py +++ b/gratipay/models/package/__init__.py @@ -40,10 +40,3 @@ 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/gratipay/models/participant/email.py b/gratipay/models/participant/email.py index 38ca03bc32..d3995e1a9a 100644 --- a/gratipay/models/participant/email.py +++ b/gratipay/models/participant/email.py @@ -41,15 +41,14 @@ class Email(object): """ - def add_email(self, email): + def add_email(self, email, package=None): """Add an email address for a participant. This is called when adding a new email address, and when resending the verification email for an unverified email address. :param unicode email: the email address to add - - :returns: ``None`` + :param Package package: a package the participant is claiming :raises EmailAlreadyVerified: if the email is already verified for this participant @@ -57,6 +56,10 @@ def add_email(self, email): :raises TooManyEmailAddresses: if the participant already has 10 emails :raises Throttled: if the participant adds too many emails too quickly + :returns: the number of emails sent + + If ``package`` is provided, then + """ # Check that this address isn't already verified @@ -67,6 +70,10 @@ def add_email(self, email): WHERE e.address = %(email)s AND e.verified IS true """, locals()) + + if package: + return self.initiate_package_claim(package, email) + if owner: if owner == self.username: raise EmailAlreadyVerified(email) diff --git a/gratipay/models/participant/package_claiming.py b/gratipay/models/participant/package_claiming.py new file mode 100644 index 0000000000..86a081d430 --- /dev/null +++ b/gratipay/models/participant/package_claiming.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, unicode_literals + + +class PackageClaiming(object): + + """Gratipay participants may claim packages on the Node package manager + (npm), bringing them into Gratipay as projects similar to any other. The + claiming process is handled via email: ``initiate_package_claim`` sends an + email to an address registered with npm, and a link back from the email + lands in ``claim_package`` to finalize the claim. + + Packages can also be unclaimed, and reclaimed. + + """ + + def initiate_package_claim(self, package, email): + """Initiate a claim on the given package. + + :param Package package: a ``Package`` instance + + :returns: ``None`` + + """ + assert email in package.emails # sanity check + + r = self.send_email('claim_package', + email=email, + link=link.format(**locals()), + include_unsubscribe=False) + assert r == 1 # Make sure the verification email was sent + if self.email_address: + self.send_email('verification_notice', + new_email=email, + include_unsubscribe=False) + return 2 + return 1 diff --git a/gratipay/testing/harness.py b/gratipay/testing/harness.py index a27d539cbc..84de748347 100644 --- a/gratipay/testing/harness.py +++ b/gratipay/testing/harness.py @@ -17,6 +17,7 @@ from gratipay.exceptions import NoSelfTipping, NoTippee, BadAmount from gratipay.models.account_elsewhere import AccountElsewhere from gratipay.models.exchange_route import ExchangeRoute +from gratipay.models.package import NPM, Package from gratipay.models.participant import Participant, MAX_TIP, MIN_TIP from gratipay.security import user from gratipay.testing.vcr import use_cassette @@ -195,14 +196,15 @@ def make_team(self, *a, **kw): return team - def make_package(self, package_manager='npm', name='foo', description='Foo', + def make_package(self, package_manager=NPM, name='foo', description='Foo', emails=['alice@example.com']): """Factory for packages. """ - return self.db.one( 'INSERT INTO packages (package_manager, name, description, emails) ' - 'VALUES (%s, %s, %s, %s) RETURNING *' - , (package_manager, name, description, emails) - ) + self.db.run( 'INSERT INTO packages (package_manager, name, description, emails) ' + 'VALUES (%s, %s, %s, %s) RETURNING *' + , (package_manager, name, description, emails) + ) + return Package.from_names(NPM, name) def make_participant(self, username, **kw): diff --git a/tests/py/test_packages.py b/tests/py/test_packages.py index b97e888377..c0b3611c97 100644 --- a/tests/py/test_packages.py +++ b/tests/py/test_packages.py @@ -3,6 +3,7 @@ from gratipay.models.package import NPM, Package from gratipay.testing import Harness +from gratipay.testing.emails import EmailHarness class TestPackage(Harness): @@ -14,3 +15,12 @@ def test_can_be_instantiated_from_id(self): def test_can_be_instantiated_from_names(self): self.make_package() assert Package.from_names(NPM, 'foo').name == 'foo' + + +class TestClaiming(EmailHarness): + + def test_participant_can_initiate_package_claim(self): + alice = self.make_participant('alice', claimed_time='now') + p = self.make_package() + alice.initiate_package_claim(p) + assert self.get_last_email() diff --git a/www/~/%username/emails/modify.json.spt b/www/~/%username/emails/modify.json.spt index fed9b49635..5205dab9a2 100644 --- a/www/~/%username/emails/modify.json.spt +++ b/www/~/%username/emails/modify.json.spt @@ -39,8 +39,7 @@ if action in ('add-email', 'resend'): raise Response(400, _( "You have already added and verified {email_address}." , email_address=address )) - else: - msg = _("A verification email has been sent to {email_address}.", email_address=address) + msg = _("Check your inbox for a verification link.") elif action == 'set-primary': participant.update_email(address) elif action == 'remove': @@ -49,7 +48,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} for a confirmation link.", email=address) + msg = _("Check your inbox for a confirmation link.") else: raise Response(400, 'unknown action "%s"' % action)