From 6646f977238e3c008e55783631cf7499b2150905 Mon Sep 17 00:00:00 2001 From: evcheng1 Date: Mon, 19 Apr 2021 16:33:00 -0400 Subject: [PATCH 1/3] added pause donations option in goals --- emails/donations_paused.spt | 16 ++++++++++++++ liberapay/models/participant.py | 14 +++++++++++- tests/py/test_goal.py | 34 ++++++++++++++++++++++++++++- www/%username/edit/goal.spt | 19 +++++++++++----- www/%username/giving/index.html.spt | 4 ++++ 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 emails/donations_paused.spt diff --git a/emails/donations_paused.spt b/emails/donations_paused.spt new file mode 100644 index 0000000000..cc3e18b60f --- /dev/null +++ b/emails/donations_paused.spt @@ -0,0 +1,16 @@ +% if donations_paused +{{ _("Liberapay donation paused") }} +% else +{{ _("Liberapay donation resumed") }} +% endif + +[---] text/html +% if donations_paused +

{{ _("Due to changes recently made by {0}, your donations for {0} have been temporarily paused.", recipient) }}

+% else +

{{ _("Due to changes recently made by {0}, your donations for {0} are set to resume.", recipient) }}

+% endif + +

{{ _("View your updated donations page at the following link:") }}

+ +

{{ _("View Donations Page") }}

diff --git a/liberapay/models/participant.py b/liberapay/models/participant.py index 220e82d506..edca671a75 100644 --- a/liberapay/models/participant.py +++ b/liberapay/models/participant.py @@ -2235,16 +2235,19 @@ def update_goal(self, goal, cursor=None): raise UnexpectedCurrency(goal, self.main_currency) with self.db.get_cursor(cursor) as c: json = None if goal is None else str(goal) + donations_paused = goal == -2 and self.goal != -1 + donations_resumed = goal != -1 and self.goal == -2 self.add_event(c, 'set_goal', json) c.run("UPDATE participants SET goal=%s WHERE id=%s", (goal, self.id)) self.set_attributes(goal=goal) - if not self.accepts_tips: + if not self.accepts_tips or donations_resumed: tippers = c.all(""" SELECT p FROM current_tips t JOIN participants p ON p.id = t.tipper WHERE t.tippee = %s """, (self.id,)) + if not self.accepts_tips: for tipper in tippers: tipper.update_giving(cursor=c) r = c.one(""" @@ -2255,6 +2258,15 @@ def update_goal(self, goal, cursor=None): RETURNING receiving, npatrons """, (self.id,)) self.set_attributes(**r._asdict()) + if donations_paused or donations_resumed: + for tipper in tippers: + tipper.send_email( + 'donations_paused', + tipper.get_email(tipper.get_email_address()), + recipient=self.username, + donations_paused=donations_paused, + donations_url=tipper.url('giving/') + ) def update_status(self, status, cursor=None): with self.db.get_cursor(cursor) as c: diff --git a/tests/py/test_goal.py b/tests/py/test_goal.py index 2f5f886fd1..edf8b2f61f 100644 --- a/tests/py/test_goal.py +++ b/tests/py/test_goal.py @@ -1,5 +1,5 @@ from liberapay.testing import EUR, Harness - +from liberapay.testing.emails import EmailHarness class Tests(Harness): @@ -70,3 +70,35 @@ def test_team_member_can_change_team_goal(self): ) assert r.code == 302 assert team.refetch().goal == EUR('99.99') + +class TestPauseDonations(EmailHarness): + def setUp(self): + EmailHarness.setUp(self) + self.alice = self.make_participant('alice', email='alice@example.com') + + def change_goal(self, goal, goal_custom="", auth_as="alice", expect_success=False): + r = self.client.PxST( + "/alice/edit/goal", + {'goal': goal, 'goal_custom': goal_custom}, + auth_as=self.alice if auth_as == 'alice' else auth_as + ) + if expect_success and r.code >= 400: + raise r + return r + + def test_pause(self): + bob = self.make_participant('bob', email='bob@example.com') + bob.set_tip_to(self.alice, EUR('0.99'), renewal_mode=2) + self.change_goal("-2", expect_success=True) + + assert self.mailer.call_count == 1 + last_email = self.get_last_email() + assert last_email['to'][0] == 'bob ' + assert "paused" in last_email['text'] + + self.change_goal("null", "", expect_success=True) + + assert self.mailer.call_count == 2 + last_email = self.get_last_email() + assert last_email['to'][0] == 'bob ' + assert "resume" in last_email['text'] diff --git a/www/%username/edit/goal.spt b/www/%username/edit/goal.spt index 4f19edb4e9..e36d56b5bf 100644 --- a/www/%username/edit/goal.spt +++ b/www/%username/edit/goal.spt @@ -11,22 +11,25 @@ if request.method == "POST": goal = request.body["goal_custom"] goal_currency = request.body.get_currency('currency', participant.main_currency) goal = locale.parse_money_amount(goal, goal_currency) - if not (goal > 0 or participant.is_person and goal.amount in (0, -1)): + if not (goal > 0 or participant.is_person and goal.amount in (0, -1, -2)): raise response.invalid_input(goal, 'goal_custom', 'body') else: - goal = Money(request.body.get_int("goal", minimum=-1), participant.main_currency).round_down() - if not (goal > 0 or participant.is_person and goal.amount in (0, -1)): + goal = Money(request.body.get_int("goal", minimum=-2), participant.main_currency).round_down() + if not (goal > 0 or participant.is_person and goal.amount in (0, -1, -2)): raise response.invalid_input(goal, 'goal', 'body') - participant.update_goal(goal) + if goal != participant.goal: + participant.update_goal(goal) form_post_success(state) if participant.kind == 'individual': GRATEFUL = _("I'm grateful for gifts, but don't have a specific funding goal.") + GRATEFUL_PAUSED_GIFTS = _("I'm grateful for gifts, but they are currently paused.") PATRON = _("I'm here as a patron.") PATRON_NO_GIFTS = _("I'm here as a patron, and politely decline to receive gifts.") GOAL_RAW = _("My goal is to receive {0}") else: GRATEFUL = _("We're grateful for gifts, but don't have a specific funding goal.") + GRATEFUL_PAUSED_GIFTS = _("We're grateful for gifts, but they are currently paused.") PATRON = _("We're here as a patron.") PATRON_NO_GIFTS = _("We're here as a patron, and politely decline to receive gifts.") GOAL_RAW = _("Our goal is to receive {0}") @@ -69,6 +72,12 @@ subhead = _("Goal")
% if participant.kind != 'group' +
+
diff --git a/www/%username/giving/index.html.spt b/www/%username/giving/index.html.spt index 05ca8f0cc4..62d5c32cf3 100644 --- a/www/%username/giving/index.html.spt +++ b/www/%username/giving/index.html.spt @@ -358,8 +358,8 @@ next_payday = compute_next_payday_date() "Inactive because the account of the recipient is closed." ) }}

% elif tippee.goal == -2 -

{{ glyphicon('warning-sign') }} {{ _( - "Inactive because the recipient's donations are temporarily paused." +

{{ glyphicon('pause') }} {{ _( + "This donation has been paused by the recipient." ) }}

% elif not tippee.accepts_tips

{{ glyphicon('warning-sign') }} {{ _( From e467f17fe261f26d3debf11dd1d217798d744c5e Mon Sep 17 00:00:00 2001 From: evcheng1 Date: Sat, 24 Apr 2021 03:35:30 -0400 Subject: [PATCH 3/3] added seperate donations_paused and donations_resumed events in the update_goal method --- liberapay/models/participant.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/liberapay/models/participant.py b/liberapay/models/participant.py index edca671a75..803c847ea8 100644 --- a/liberapay/models/participant.py +++ b/liberapay/models/participant.py @@ -2260,8 +2260,9 @@ def update_goal(self, goal, cursor=None): self.set_attributes(**r._asdict()) if donations_paused or donations_resumed: for tipper in tippers: + event = 'donations_paused' if donations_paused else 'donations_resumed' tipper.send_email( - 'donations_paused', + event, tipper.get_email(tipper.get_email_address()), recipient=self.username, donations_paused=donations_paused,