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

Commit

Permalink
Only start claims when address verified
Browse files Browse the repository at this point in the history
If unverified, allow to use the package page to start verification.
  • Loading branch information
chadwhitacre committed Aug 31, 2017
1 parent f8e7886 commit 84945c1
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 42 deletions.
33 changes: 22 additions & 11 deletions js/gratipay/packages.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
Gratipay.packages = {};

Gratipay.packages.initBulk = function() {
$('button.apply').on('click', Gratipay.packages.postBulk);
$('.important-button button.apply').on('click', Gratipay.packages.postBulk);
};

Gratipay.packages.initSingle = function() {
Gratipay.Select('.gratipay-select');
$('button.apply').on('click', Gratipay.packages.postOne);
Gratipay.Select('.gratipay-select', Gratipay.packages.selectOne);
$('.important-button button').on('click', Gratipay.packages.postOne);
Gratipay.packages.selectOne($('.gratipay-select li.selected'));
};

Gratipay.packages.selectOne = function($li) {
var action = $li.data('action');
$('.important-button span').hide();
$('.important-button span.' + action).show();
};

Gratipay.packages.postBulk = function(e) {
e.preventDefault();
Expand All @@ -20,20 +26,25 @@ Gratipay.packages.postBulk = function(e) {
package_ids_by_email[pkg.email].push(pkg.packageId);
});
for (email in package_ids_by_email)
Gratipay.packages.post(email, package_ids_by_email[email], true);
Gratipay.packages.post(email, package_ids_by_email[email], 'yes');
};

Gratipay.packages.postOne = function(e) {
e.preventDefault();
var email = $('input[name=email]:checked').val();
var package_id = $('input[name=package_id]').val();
Gratipay.packages.post(email, [package_id]);
var $input = $('input[name=email]:checked');
var email = $input.val();
var package_ids;
var show_address_in_message = 'no';
if ($input.closest('li').data('action') === 'apply') {
package_ids = [$('input[name=package_id]').val()];
show_address_in_message = 'yes';
}
Gratipay.packages.post(email, package_ids, show_address_in_message);
}


Gratipay.packages.post = function(email, package_ids, show_email) {
Gratipay.packages.post = function(email, package_ids, show_address_in_message) {
var action = 'start-verification';
var $button = $('button.apply')
var $button = $('.important-button button')

$button.prop('disabled', true);
function reenable() { $button.prop('disabled', false); }
Expand All @@ -43,7 +54,7 @@ Gratipay.packages.post = function(email, package_ids, show_email) {
data: { action: action
, address: email
, package_id: package_ids
, show_address_in_message: true
, show_address_in_message: show_address_in_message
},
traditional: true,
dataType: 'json',
Expand Down
9 changes: 6 additions & 3 deletions js/gratipay/select.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Gratipay.Select = function(selector) {
Gratipay.Select = function(selector, onselect) {
var $ul = $('ul', selector);
var $labels = $('label', $ul);
var onselect = onselect || function() {};

// state for vertical position
var topFactor = 0; // float between 0 and $labels.length-1
Expand Down Expand Up @@ -51,9 +52,11 @@ Gratipay.Select = function(selector) {

function close($label) {
if ($label) {
if ($label.closest('li').hasClass('disabled')) return;
var $li = $label.closest('li');
if ($li.hasClass('disabled')) return;
$('.selected', $ul).removeClass('selected')
$label.closest('li').addClass('selected').removeClass('hover');
$li.addClass('selected').removeClass('hover');
onselect($li);
}
$ul.css({'top': 0}).removeClass('open');
$ul.unbind('mousewheel');
Expand Down
6 changes: 6 additions & 0 deletions scss/pages/package.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
color: $medium-gray;
}

.important-button {
span {
display: none;
}
}

.status-icon {
font-size: 12px;
line-height: 12px;
Expand Down
70 changes: 53 additions & 17 deletions tests/ttw/test_package_claiming.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,25 @@

class Test(BrowserHarness):

def check(self, choice=0):
self.make_participant('alice', claimed_time='now')
def setUp(self):
BrowserHarness.setUp(self)
self.alice = self.make_participant( 'alice'
, claimed_time='now'
, email_address='alice@example.com'
)
self.add_and_verify_email(self.alice, 'bob@example.com')
self.sign_in('alice')

def choose(self, choice=0):
self.css('#content li.selected label').click() # activate select
want = self.css('#content label')[choice]
want.click()
return want.text

def check(self, choice=0):
self.visit('/on/npm/foo')
self.css('#content label')[0].click() # activate select
self.css('#content label')[choice].click()
self.css('#content button')[0].click()
self.choose(choice)
self.css('#content .important-button button').click()
address = ('alice' if choice == 0 else 'bob') + '@example.com'
assert self.wait_for_success() == 'Check {} for a verification link.'.format(address)
return self.db.one('select address from claims c '
Expand All @@ -39,18 +51,42 @@ def test_can_send_to_second_email(self):
self.make_package(emails=['alice@example.com', 'bob@example.com'])
assert self.check(choice=1) == 'bob@example.com'

def test_disabled_items_are_disabled(self):
self.make_package(emails=['alice@example.com', 'bob@example.com'])
alice = self.make_participant('alice', claimed_time='now')
self.add_and_verify_email(alice, 'alice@example.com', 'bob@example.com')
self.sign_in('alice')
def test_button_varies_with_email_state(self):
self.make_package(emails=['alice@example.com', 'bob@example.com', 'cat@example.com',
'doug@example.com', 'edna@example.com'])
self.make_participant('doug', claimed_time='now', email_address='doug@example.com')
self.alice.start_email_verification('cat@example.com')
self.visit('/on/npm/foo')

self.choose(0) == 'alice@example.com'
self.css('li.selected').text.endswith('Your primary email address')
self.css('button').text == 'Apply to accept payments'

self.choose(1) == 'bob@example.com'
self.css('li.selected').text.endswith('Linked to your account')
self.css('button').text == 'Apply to accept payments'

self.choose(2) == 'cat@example.com'
self.css('li.selected').text.endswith('Verification pending')
self.css('button').text == 'Resend verification'

self.choose(3) == 'edna@example.com'
self.css('li.selected').text.endswith('Unverified')
self.css('button').text == 'Verify email address'

# doug is last, can't even be selected
self.choose(4) == 'doug@example.com'
self.css('li.selected label') == 'edna@example.com'
self.css('#content li')[4].text.endswith('Linked to a different account')

def test_sending_to_unverified_doesnt_start_a_claim(self):
self.make_package(emails=['alice@example.com', 'cat@example.com'])
self.visit('/on/npm/foo')
self.css('#content label')[0].click() # activate select
self.css('#content label')[1].click() # click second item
self.css('#content li')[0].has_class('selected') # first item is still selected
self.css('#content ul')[0].has_class('open') # still open
self.css('#content button').has_class('disabled')
assert self.db.all('select * from claims') == []
self.choose(1)
self.css('#content .important-button button').click()
assert self.wait_for_success() == 'Check your inbox for a verification link.'
assert self.db.one('select address from claims c '
'join email_addresses e on c.nonce = e.nonce') is None


def test_claimed_packages_can_be_given_to(self):
Expand Down Expand Up @@ -79,7 +115,7 @@ def test_visiting_verify_link_shows_helpful_information(self):
self.make_package()
self.check()

link = pickle.loads(self.db.one('select context from email_messages'))['link']
link = pickle.loads(self.db.all('select context from email_messages')[-1])['link']
link = link[len(self.base_url):] # strip because visit will add it back

self.visit(link)
Expand Down
28 changes: 18 additions & 10 deletions www/on/npm/%package.spt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ banner = package.name

if user.participant:
emails = package.classify_emails_for_participant(user.participant)
has_email_options = bool([x for x in emails if x[1] != OTHER])
[---] text/html
{% extends "templates/base.html" %}

Expand Down Expand Up @@ -77,7 +76,11 @@ if user.participant:
<ul>
{% for i, (email, group) in enumerate(emails, start=1) %}
<li class="{% if group == OTHER %}disabled{% endif %}
{% if i == 1 %}selected{% endif %}">
{% if i == 1 %}selected{% endif %}"
data-action="{{ 'apply' if group in (PRIMARY, VERIFIED) else
'resend' if group == UNVERIFIED else
'verify' if group == UNLINKED else
'none' }}">
<input type="radio" name="email" value="{{ email }}" id="email-{{i}}"
{% if i == 1 %}checked{% endif %}>
<span class="icon">&#xe007;</span>
Expand All @@ -91,19 +94,22 @@ if user.participant:
{{ icons.STATUS_ICONS['failure']|safe }}</span>
{{ _('Linked to a different account') }}
{% else %}
{{ _('Ready to use') }}
{% if group == PRIMARY %}
&middot; <span class="status-icon feature">
<span class="status-icon feature">
{{ icons.STATUS_ICONS['feature']|safe }}</span>
{{ _('Your primary email address') }}
{% elif group == VERIFIED %}
&middot; <span class="status-icon success">
<span class="status-icon success">
{{ icons.STATUS_ICONS['success']|safe }}</span>
{{ _('Linked to your account') }}
{% elif group == UNVERIFIED %}
&middot; <span class="status-icon warning">
<span class="status-icon warning">
{{ icons.STATUS_ICONS['warning']|safe }}</span>
{{ _('Half-linked to your account') }}
{{ _('Verification pending') }}
{% elif group == UNLINKED %}
<span class="status-icon warning">
{{ icons.STATUS_ICONS['warning']|safe }}</span>
{{ _('Unverified') }}
{% endif %}
{% endif %}
</div>
Expand All @@ -116,9 +122,11 @@ if user.participant:
</div>

<div class="important-button">
<button type="submit" class="apply selected large"
{% if not has_email_options %} disabled{% endif %}>
{{ _('Apply to accept payments') }}
<button type="submit" class="selected large disabled">
<span class="apply">{{ _('Apply to accept payments') }}</span>
<span class="resend">{{ _('Resend verification') }}</span>
<span class="verify">{{ _('Verify email address') }}</span>
<span class="dead-end">{{ _('Dead-end, sorry') }}</span>
</button>
</div>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion www/~/%username/emails/modify.json.spt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ participant = get_participant(state, restrict=True)

action = request.body['action']
address = request.body['address']
show_address_in_message = bool(request.body.get('show_address_in_message', ''))
show_address_in_message = request.body.get('show_address_in_message', '') == 'yes'

# Basic checks. The real validation will happen when we send the email.
if (len(address) > 254) or not email_re.match(address):
Expand Down

0 comments on commit 84945c1

Please sign in to comment.