Skip to content

Commit

Permalink
implement limiting where donations can come from
Browse files Browse the repository at this point in the history
closes #1102
  • Loading branch information
Changaco committed Dec 22, 2023
1 parent 2486e6b commit 3008f25
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 4 deletions.
15 changes: 15 additions & 0 deletions liberapay/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,21 @@ def msg(self, _):
)


class ProhibitedSourceCountry(LazyResponseXXX):
code = 403

def __init__(self, recipient, country):
super().__init__()
self.recipient = recipient
self.country = country

Check warning on line 550 in liberapay/exceptions.py

View check run for this annotation

Codecov / codecov/patch

liberapay/exceptions.py#L548-L550

Added lines #L548 - L550 were not covered by tests

def msg(self, _, locale):
return _(

Check warning on line 553 in liberapay/exceptions.py

View check run for this annotation

Codecov / codecov/patch

liberapay/exceptions.py#L553

Added line #L553 was not covered by tests
"{username} does not accept donations from {country}.",
self.recipient.username, locale.Country(self.country)
)


class TooManyCurrencyChanges(LazyResponseXXX):
code = 429
def msg(self, _):
Expand Down
2 changes: 1 addition & 1 deletion liberapay/i18n/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,8 @@ def make_sorted_dict(keys, d, d2={}, clean=_return_):
SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR
TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW
""".split()

COUNTRIES = make_sorted_dict(COUNTRY_CODES, LOCALE_EN.territories)
del COUNTRY_CODES


def make_currencies_map():
Expand Down
13 changes: 11 additions & 2 deletions liberapay/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -1731,17 +1731,26 @@ def send_newsletters(cls):

@cached_property
def recipient_settings(self):
return self.db.one("""
r = self.db.one("""
SELECT *
FROM recipient_settings
WHERE participant = %s
""", (self.id,), default=Object(
participant=self.id,
patron_visibilities=(7 if self.status == 'stub' else 0),
patron_countries=None,
))
if r.patron_countries:
if r.patron_countries.startswith('-'):
r.patron_countries = set(i18n.COUNTRIES) - set(r.patron_countries[1:].split(','))

Check warning on line 1745 in liberapay/models/participant.py

View check run for this annotation

Codecov / codecov/patch

liberapay/models/participant.py#L1744-L1745

Added lines #L1744 - L1745 were not covered by tests
else:
r.patron_countries = set(r.patron_countries.split(','))

Check warning on line 1747 in liberapay/models/participant.py

View check run for this annotation

Codecov / codecov/patch

liberapay/models/participant.py#L1747

Added line #L1747 was not covered by tests
return r

def update_recipient_settings(self, **kw):
cols, vals = zip(*kw.items())
new_recipient_settings = dict(self.recipient_settings.__dict__, **kw)
new_recipient_settings.pop('participant')
cols, vals = zip(*new_recipient_settings.items())
updates = ','.join('{0}=excluded.{0}'.format(col) for col in cols)
cols = ', '.join(cols)
placeholders = ', '.join(['%s']*len(vals))
Expand Down
8 changes: 7 additions & 1 deletion liberapay/payin/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from ..constants import SEPA
from ..exceptions import (
AccountSuspended, BadDonationCurrency, MissingPaymentAccount,
RecipientAccountSuspended, NoSelfTipping, UserDoesntAcceptTips,
NoSelfTipping, ProhibitedSourceCountry, RecipientAccountSuspended,
UserDoesntAcceptTips,
)
from ..i18n.currencies import Money, MoneyBasket
from ..utils import group_by
Expand Down Expand Up @@ -58,6 +59,11 @@ def prepare_payin(db, payer, amount, route, proto_transfers, off_session=False):
if payer.is_suspended or not payer.get_email_address():
raise AccountSuspended()

for pt in proto_transfers:
if (allowed_countries := pt.recipient.recipient_settings.patron_countries):
if route.country and route.country not in allowed_countries:
raise ProhibitedSourceCountry(route, pt.recipient)

Check warning on line 65 in liberapay/payin/common.py

View check run for this annotation

Codecov / codecov/patch

liberapay/payin/common.py#L64-L65

Added lines #L64 - L65 were not covered by tests

with db.get_cursor() as cursor:
payin = cursor.one("""
INSERT INTO payins
Expand Down
1 change: 1 addition & 0 deletions sql/branch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE recipient_settings ADD COLUMN patron_countries text;
8 changes: 8 additions & 0 deletions style/base/columns.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
column-count: 2;
column-gap: 2ex;
}
.columns-sm-3 {
column-count: 3;
column-gap: 1.5ex;
}
}
@media (min-width: $screen-md-min) {
.columns-md-3 {
Expand All @@ -13,4 +17,8 @@
column-count: 4;
column-gap: 2ex;
}
.columns-md-5 {
column-count: 5;
column-gap: 1.5ex;
}
}
8 changes: 8 additions & 0 deletions style/base/lists.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
.checklist {
list-style: none;
padding-left: 0;
& > li {
padding-left: 0;
}
}

.right-pointing-arrows {
list-style-type: '';
padding-left: 2ex;
Expand Down
2 changes: 2 additions & 0 deletions templates/macros/nav.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
('/username', _("Name")),
('/avatar', _("Avatar")),
('/currencies', _("Currencies")),
('/countries', _("Countries")),
('/goal', _("Goal")),
('/statement', _("Descriptions")),
('/elsewhere', _("Linked Accounts")),
Expand Down Expand Up @@ -136,6 +137,7 @@
('/username', _("Name")),
('/avatar', _("Avatar")),
('/currencies', _("Currencies")),
('/countries', _("Countries")),
('/goal', _("Goal")),
('/statement', _("Descriptions")),
('/elsewhere', _("Linked Accounts")),
Expand Down
11 changes: 11 additions & 0 deletions templates/macros/your-tip.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
% if request.qs.get('currency') in accepted_currencies
% set new_currency = request.qs['currency']
% endif
% if not tippee_is_stub
% set patron_countries = tippee.recipient_settings.patron_countries
% set source_country = request.source_country or 'FI'
% if True or patron_countries and source_country and source_country not in patron_countries
<p class="alert alert-warning">{{ _(
"It looks like you are in {country}. {username} does not accept "
"donations coming from that country.",
country=locale.Country(source_country), username=tippee_name,
) }}</p>
% endif
% endif
% set currency_mismatch = tip_currency not in accepted_currencies
% if tip.renewal_mode > 0 and not pledging
% if currency_mismatch
Expand Down
57 changes: 57 additions & 0 deletions www/%username/edit/countries.spt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from liberapay.utils import form_post_success, get_participant

[---]
participant = get_participant(state, restrict=True, allow_member=True)

if request.method == 'POST':
accepted_countries, rejected_countries = [], []
for country_code in locale.countries:
if request.body.get('accepted_countries:' + country_code) == 'yes':
accepted_countries.append(country_code)
else:
rejected_countries.append(country_code)
if not accepted_countries:
raise response.error(400, _("You have to check at least one box."))
if not rejected_countries:
new_patron_countries = None
elif len(accepted_countries) > len(rejected_countries):
new_patron_countries = '-' + ','.join(rejected_countries)
else:
new_patron_countries = ','.join(accepted_countries)
participant.update_recipient_settings(patron_countries=new_patron_countries)
form_post_success(state)

accepted_countries = participant.recipient_settings.patron_countries
accept_all = accepted_countries is None

title = participant.username
subhead = _("Territories")

[---] text/html
% from "templates/macros/icons.html" import glyphicon

% extends "templates/layouts/profile-edit.html"

% block form

<form action="" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />

<p>{{ _("Which countries should your donors be allowed to send you money from?") }}</p>

<p class="text-info">{{ glyphicon('info-sign') }} {{ _(
"We recommend limiting the origins of donations only if you are required to by law."
) }}</p>

<ul class="columns-sm-3 columns-md-5 checklist">
% for country_code, country_name in locale.countries.items()
<li><label><input type="checkbox" name="accepted_countries:{{ country_code }}" value="yes"
{{ 'checked' if accept_all or country_code in accepted_countries else '' }} /> {{ country_name }}</label></li>
% endfor
</ul>

<br>
<button class="save btn btn-lg btn-success">{{ _("Save") }}</button>
</form>

% endblock

0 comments on commit 3008f25

Please sign in to comment.