Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Shelve
Browse files Browse the repository at this point in the history
  • Loading branch information
chadwhitacre committed Feb 21, 2017
1 parent fd21b9a commit a9f9dfe
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 20 deletions.
17 changes: 17 additions & 0 deletions 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?",
('<b>%s</b>'|safe) % email,
('<b><a href="https://gratipay.com/~{0}/">{0}</a></b>'|safe).format(username)) }}
<br>
<br>
<a href="{{ link }}" style="{{ button_style }}">{{ _("Yes, proceed!") }}</a>

[---] 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 }}
7 changes: 0 additions & 7 deletions gratipay/models/package/__init__.py
Expand Up @@ -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
13 changes: 11 additions & 2 deletions gratipay/models/participant/email.py
Expand Up @@ -37,13 +37,20 @@ class Email(object):
"""

def add_email(self, email, resend_threshold='3 minutes'):
def add_email(self, email, package=None, resend_threshold='3 minutes'):
"""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.
:returns: the number of emails sent.
:param unicode email: the email address to add
:param Package package: a package the participant is claiming
:param unicode resend_threshold: the time interval to wait before
sending another verification message
:returns: the number of emails sent
If ``package`` is provided, then
"""

Expand All @@ -57,6 +64,8 @@ def add_email(self, email, resend_threshold='3 minutes'):
""", locals())
if owner:
if owner == self.username:
if package:
return self.initiate_package_claim(package, email)
return 0
else:
raise EmailAlreadyTaken(email)
Expand Down
37 changes: 37 additions & 0 deletions 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
12 changes: 7 additions & 5 deletions gratipay/testing/harness.py
Expand Up @@ -19,6 +19,7 @@
from gratipay.models.account_elsewhere import AccountElsewhere
from gratipay.models.exchange_route import ExchangeRoute
from gratipay.models.participant import Participant
from gratipay.models.package import NPM, Package
from gratipay.security import user
from gratipay.testing.vcr import use_cassette
from psycopg2 import IntegrityError, InternalError
Expand Down Expand Up @@ -181,14 +182,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):
Expand Down
4 changes: 2 additions & 2 deletions tests/py/test_email.py
Expand Up @@ -237,12 +237,12 @@ def test_cannot_resend_verification_too_frequently(self):
self.alice.add_email('alice@gratipay.coop')
time.sleep(0.05)
with self.assertRaises(ResendingTooFast):
self.alice.add_email('alice@gratipay.coop', '0.1 seconds')
self.alice.add_email('alice@gratipay.coop', resend_threshold='0.1 seconds')

def test_can_resend_verification_after_a_while(self):
self.alice.add_email('alice@gratipay.coop')
time.sleep(0.15)
self.alice.add_email('alice@gratipay.coop', '0.1 seconds')
self.alice.add_email('alice@gratipay.coop', resend_threshold='0.1 seconds')

def test_html_escaping(self):
self.alice.add_email("foo'bar@example.com")
Expand Down
10 changes: 10 additions & 0 deletions tests/py/test_packages.py
Expand Up @@ -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):
Expand All @@ -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()
6 changes: 3 additions & 3 deletions tests/py/test_take_over.py
Expand Up @@ -190,10 +190,10 @@ def test_email_addresses_merging(self):
alice.verify_email('alice@example.org', alice.get_email('alice@example.org').nonce)
bob_github = self.make_elsewhere('github', 2, 'bob')
bob = bob_github.opt_in('bob')[0].participant
bob.add_email('alice@example.com', '0 seconds')
bob.add_email('alice@example.com', resend_threshold='0 seconds')
bob.verify_email('alice@example.com', bob.get_email('alice@example.com').nonce)
bob.add_email('alice@example.net', '0 seconds')
bob.add_email('bob@example.net', '0 seconds')
bob.add_email('alice@example.net', resend_threshold='0 seconds')
bob.add_email('bob@example.net', resend_threshold='0 seconds')
alice.take_over(bob_github, have_confirmation=True)

alice_emails = {e.address: e for e in alice.get_emails()}
Expand Down
2 changes: 1 addition & 1 deletion www/~/%username/emails/modify.json.spt
Expand Up @@ -28,7 +28,7 @@ if not participant.email_lang:

msg = None
if action in ('add-email', 'resend'):
r = participant.add_email(address, website.env.resend_verification_threshold)
r = participant.add_email(address, resend_threshold=website.env.resend_verification_threshold)
if r:
msg = _("A verification email has been sent to {0}.", address)
else:
Expand Down

0 comments on commit a9f9dfe

Please sign in to comment.