Skip to content

Commit

Permalink
Merge pull request #2088 from liberapay/anonymity
Browse files Browse the repository at this point in the history
  • Loading branch information
Changaco authored Nov 25, 2021
2 parents 7628dd8 + ab2e8b8 commit 948ad79
Show file tree
Hide file tree
Showing 29 changed files with 825 additions and 70 deletions.
4 changes: 2 additions & 2 deletions liberapay/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ def __missing__(self, currency):
)

PRIVACY_FIELDS = OrderedDict([
('hide_giving', (_("Hide total giving from others."), False)),
('hide_receiving', (_("Hide total receiving from others."), False)),
('hide_giving', (_("Do not publish the amounts of money I send."), False)),
('hide_receiving', (_("Do not publish the amounts of money I receive."), False)),
('hide_from_search', (_("Hide this profile from search results on Liberapay."), True)),
('profile_noindex', (_("Tell web search engines not to index this profile."), True)),
('hide_from_lists', (_("Prevent this profile from being listed on Liberapay."), True)),
Expand Down
76 changes: 59 additions & 17 deletions liberapay/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,9 +982,9 @@ def clear_tips_giving(self, cursor):
tippees = cursor.all("""
INSERT INTO tips
( ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, renewal_mode )
, paid_in_advance, is_funded, renewal_mode, visibility )
SELECT ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, 0
, paid_in_advance, is_funded, 0, visibility
FROM current_tips
WHERE tipper = %s
AND renewal_mode > 0
Expand Down Expand Up @@ -1915,6 +1915,38 @@ def send_newsletters(cls):
sleep(1)


# Recipient settings
# ==================

@cached_property
def recipient_settings(self):
return 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),
))

def update_recipient_settings(self, **kw):
cols, vals = zip(*kw.items())
updates = ','.join('{0}=excluded.{0}'.format(col) for col in cols)
cols = ', '.join(cols)
placeholders = ', '.join(['%s']*len(vals))
with self.db.get_cursor() as cursor:
settings = cursor.one("""
INSERT INTO recipient_settings
(participant, {0})
VALUES (%s, {1})
ON CONFLICT (participant) DO UPDATE
SET {2}
RETURNING *
""".format(cols, placeholders, updates), (self.id,) + vals)
self.add_event(cursor, 'recipient_settings', kw)
self.recipient_settings = settings


# Random Stuff
# ============

Expand Down Expand Up @@ -2551,7 +2583,7 @@ def update_receiving(self, cursor=None):


def set_tip_to(self, tippee, periodic_amount, period='weekly', renewal_mode=None,
update_self=True, update_tippee=True):
visibility=None, update_self=True, update_tippee=True):
"""Given a Participant or username, and amount as str, returns a dict.
We INSERT instead of UPDATE, so that we have history to explore. The
Expand Down Expand Up @@ -2599,20 +2631,27 @@ def set_tip_to(self, tippee, periodic_amount, period='weekly', renewal_mode=None
INSERT INTO tips
( ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance
, renewal_mode )
, renewal_mode
, visibility )
VALUES ( COALESCE((SELECT ctime FROM current_tip), CURRENT_TIMESTAMP)
, %(tipper)s, %(tippee)s, %(amount)s, %(period)s, %(periodic_amount)s
, (SELECT convert(paid_in_advance, %(currency)s) FROM current_tip)
, coalesce(
%(renewal_mode)s,
(SELECT renewal_mode FROM current_tip WHERE renewal_mode > 0),
1
)
, coalesce(
%(visibility)s,
(SELECT visibility FROM current_tip),
1
) )
RETURNING tips
""", dict(
tipper=self.id, tippee=tippee.id, amount=amount, currency=amount.currency,
period=period, periodic_amount=periodic_amount, renewal_mode=renewal_mode,
visibility=visibility,
))
t.tipper_p = self
t.tippee_p = tippee
Expand Down Expand Up @@ -2641,16 +2680,17 @@ def _zero_tip(tippee, currency=None):
return Tip(
amount=zero, is_funded=False, tippee=tippee.id, tippee_p=tippee,
period='weekly', periodic_amount=zero, renewal_mode=0,
visibility=0,
)


def stop_tip_to(self, tippee, update_schedule=True):
t = self.db.one("""
INSERT INTO tips
( ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, renewal_mode )
, paid_in_advance, is_funded, renewal_mode, visibility )
SELECT ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, 0
, paid_in_advance, is_funded, 0, visibility
FROM current_tips
WHERE tipper = %(tipper)s
AND tippee = %(tippee)s
Expand All @@ -2675,14 +2715,14 @@ def hide_tip_to(self, tippee_id, hide=True):
return self.db.one("""
INSERT INTO tips
( ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, renewal_mode, hidden )
, paid_in_advance, is_funded, renewal_mode, visibility )
SELECT ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, renewal_mode, %(hide)s
, paid_in_advance, is_funded, renewal_mode, -visibility
FROM current_tips
WHERE tipper = %(tipper)s
AND tippee = %(tippee)s
AND renewal_mode = 0
AND hidden IS NOT %(hide)s
AND (visibility < 0) IS NOT %(hide)s
RETURNING tips
""", dict(tipper=self.id, tippee=tippee_id, hide=hide))

Expand Down Expand Up @@ -3261,6 +3301,7 @@ def get_giving_details(self):
, t.is_funded
, t.paid_in_advance
, t.renewal_mode
, t.visibility
, p.payment_providers
, ( t.paid_in_advance IS NULL OR
t.paid_in_advance < (t.amount * 3)
Expand All @@ -3269,7 +3310,7 @@ def get_giving_details(self):
JOIN participants p ON p.id = t.tippee
WHERE t.tipper = %s
AND p.status <> 'stub'
AND t.hidden IS NOT true
AND t.visibility > 0
ORDER BY tippee, t.mtime DESC
""", (self.id,))
Expand All @@ -3283,13 +3324,14 @@ def get_giving_details(self):
, t.ctime
, t.mtime
, t.renewal_mode
, t.visibility
, (e, p)::elsewhere_with_participant AS e_account
FROM current_tips t
JOIN participants p ON p.id = t.tippee
JOIN elsewhere e ON e.participant = t.tippee
WHERE t.tipper = %s
AND p.status = 'stub'
AND t.hidden IS NOT true
AND t.visibility > 0
ORDER BY tippee, t.mtime DESC
""", (self.id,))
Expand Down Expand Up @@ -3603,11 +3645,11 @@ def take_over(self, account, have_confirmation=False):
-- maximum allowed.
INSERT INTO tips
( ctime, tipper, tippee, amount, period
, periodic_amount, is_funded, renewal_mode, hidden
, periodic_amount, is_funded, renewal_mode, visibility
, paid_in_advance )
SELECT DISTINCT ON (tipper)
ctime, tipper, %(live)s AS tippee, amount, period
, periodic_amount, is_funded, renewal_mode, hidden
, periodic_amount, is_funded, renewal_mode, visibility
, ( SELECT sum(t2.paid_in_advance, t.amount::currency)
FROM temp_tips t2
WHERE t2.tipper = t.tipper
Expand All @@ -3624,9 +3666,9 @@ def take_over(self, account, have_confirmation=False):
ZERO_OUT_OLD_TIPS_RECEIVING = """
INSERT INTO tips
( ctime, tipper, tippee, amount, period, periodic_amount
, paid_in_advance, is_funded, renewal_mode, hidden )
, paid_in_advance, is_funded, renewal_mode, visibility )
SELECT ctime, tipper, tippee, amount, period, periodic_amount
, NULL, false, 0, true
, NULL, false, 0, -visibility
FROM temp_tips
WHERE tippee = %(dead)s
AND ( coalesce_currency_amount(paid_in_advance, amount::currency) > 0 OR
Expand Down Expand Up @@ -3788,7 +3830,7 @@ def to_dict(self, details=False):

# Key: receiving
# Values:
# null - user is receiving anonymously
# null - user does not publish how much they receive
# 3.00 - user receives this amount in tips
if not self.hide_receiving:
receiving = self.receiving
Expand All @@ -3798,7 +3840,7 @@ def to_dict(self, details=False):

# Key: giving
# Values:
# null - user is giving anonymously
# null - user does not publish how much they give
# 3.00 - user gives this amount in tips
if not self.hide_giving:
giving = self.giving
Expand Down
24 changes: 13 additions & 11 deletions liberapay/payin/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

ProtoTransfer = namedtuple(
'ProtoTransfer',
'amount recipient destination context unit_amount period team',
'amount recipient destination context unit_amount period team visibility',
)


Expand Down Expand Up @@ -63,7 +63,7 @@ def prepare_payin(db, payer, amount, route, proto_transfers, off_session=False):
for t in proto_transfers:
payin_transfers.append(prepare_payin_transfer(
cursor, payin, t.recipient, t.destination, t.context, t.amount,
t.unit_amount, t.period, t.team,
t.visibility, t.unit_amount, t.period, t.team,
))

return payin, payin_transfers
Expand Down Expand Up @@ -240,8 +240,8 @@ def adjust_payin_transfers(db, payin, net_amount):
unit_amount = (d.amount / n_periods).round(allow_zero=False)
prepare_payin_transfer(
db, payin, d.recipient, d.destination, 'team-donation',
d.amount, unit_amount, tip.period,
team=team.id
d.amount, tip.visibility, unit_amount, tip.period,
team=team.id,
)
else:
pt = transfers[0]
Expand Down Expand Up @@ -303,7 +303,7 @@ def resolve_tip(
)
return [ProtoTransfer(
payment_amount, tippee, destination, 'personal-donation',
tip.periodic_amount, tip.period, None,
tip.periodic_amount, tip.period, None, tip.visibility,
)]


Expand Down Expand Up @@ -450,6 +450,7 @@ def resolve_team_donation(
(t.resolved_amount / n_periods).round(allow_zero=False),
tip.period,
team.id,
tip.visibility,
)
for t in selected_takes if t.resolved_amount != 0
]
Expand All @@ -460,7 +461,7 @@ def resolve_team_donation(
account = payment_accounts[member.id]
return [ProtoTransfer(
payment_amount, member, account, 'team-donation',
tip.periodic_amount, tip.period, team.id,
tip.periodic_amount, tip.period, team.id, tip.visibility,
)]


Expand Down Expand Up @@ -571,8 +572,8 @@ def compute_priority(item):


def prepare_payin_transfer(
db, payin, recipient, destination, context, amount,
unit_amount=None, period=None, team=None
db, payin, recipient, destination, context, amount, visibility,
unit_amount=None, period=None, team=None,
):
"""Prepare the allocation of funds from a payin.
Expand All @@ -581,6 +582,7 @@ def prepare_payin_transfer(
recipient (Participant): the user who will receive the money
destination (Record): a row from the `payment_accounts` table
amount (Money): the amount of money that will be received
visibility (int): a copy of `tip.visibility`
unit_amount (Money): the `periodic_amount` of a recurrent donation
period (str): the period of a recurrent payment
team (int): the ID of the project this payment is tied to
Expand All @@ -601,14 +603,14 @@ def prepare_payin_transfer(
return db.one("""
INSERT INTO payin_transfers
(payin, payer, recipient, destination, context, amount,
unit_amount, n_units, period, team,
unit_amount, n_units, period, team, visibility,
status, ctime)
VALUES (%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s,
%s, %s, %s, %s, %s,
'pre', clock_timestamp())
RETURNING *
""", (payin.id, payin.payer, recipient.id, destination.pk, context, amount,
unit_amount, n_units, period, team))
unit_amount, n_units, period, team, visibility))


def update_payin_transfer(
Expand Down
8 changes: 6 additions & 2 deletions liberapay/payin/stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,9 +708,13 @@ def generate_transfer_description(pt):
WHERE id = %s
""", (pt.team or pt.recipient,))
if pt.team:
return f"anonymous donation for team {name} via Liberapay"
name = f"team {name}"
if pt.visibility == 3:
return f"public donation for {name} via Liberapay"
if pt.visibility == 2:
return f"private donation for {name} via Liberapay"
else:
return f"anonymous donation for {name} via Liberapay"
return f"secret donation for {name} via Liberapay"


def record_refunds(db, payin, charge):
Expand Down
1 change: 1 addition & 0 deletions liberapay/utils/fake_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def fake_tip(db, tipper, tippee):
amount=Money(amount, 'EUR'),
period=period,
periodic_amount=Money(periodic_amount, 'EUR'),
visibility=1,
)


Expand Down
4 changes: 2 additions & 2 deletions liberapay/utils/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ def iter_payin_events(db, participant, period_start, period_end, minimize=False)
outgoing_transfers = db.all("""
SELECT tr.id, tr.ctime, tr.payin, tr.recipient, tr.context, tr.status, tr.error
, tr.amount, tr.fee, tr.unit_amount, tr.n_units, tr.period
, tr.reversed_amount
, tr.reversed_amount, tr.visibility
, p.username AS recipient_username, p2.username AS team_name
, r.network AS payin_method
FROM payin_transfers tr
Expand All @@ -487,7 +487,7 @@ def iter_payin_events(db, participant, period_start, period_end, minimize=False)
incoming_transfers = db.all("""
SELECT tr.id, tr.ctime, tr.payin, tr.payer, tr.context, tr.status, tr.error
, tr.amount, tr.fee, tr.unit_amount, tr.n_units, tr.period
, tr.reversed_amount
, tr.reversed_amount, tr.visibility
, p.username AS payer_username, p2.username AS team_name
, r.network AS payin_method
FROM payin_transfers tr
Expand Down
43 changes: 43 additions & 0 deletions sql/branch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
BEGIN;
ALTER TABLE tips ADD COLUMN visibility int CHECK (visibility >= -3 AND visibility <> 0 AND visibility <= 3);
-- 1 means secret, 2 means private, 3 means public, negative numbers mean hidden
ALTER TABLE payin_transfers ADD COLUMN visibility int DEFAULT 1 CHECK (visibility >= 1 AND visibility <= 3);
-- same meanings as for tips, but negative numbers aren't allowed

UPDATE tips SET visibility = -1 WHERE hidden AND visibility IS NULL;

CREATE OR REPLACE VIEW current_tips AS
SELECT DISTINCT ON (tipper, tippee) *
FROM tips
ORDER BY tipper, tippee, mtime DESC;

CREATE TABLE recipient_settings
( participant bigint PRIMARY KEY REFERENCES participants
, patron_visibilities int NOT NULL CHECK (patron_visibilities > 0)
-- Three bits: 1 is for "secret", 2 is for "private", 4 is for "public".
);
END;

SELECT 'after deployment';

BEGIN;
UPDATE tips SET visibility = -1 WHERE hidden AND visibility = 1;
DROP FUNCTION compute_arrears(current_tips);
DROP CAST (current_tips AS tips);
DROP VIEW current_tips;
ALTER TABLE tips
DROP COLUMN hidden,
ALTER COLUMN visibility DROP DEFAULT,
ALTER COLUMN visibility SET NOT NULL;
CREATE OR REPLACE VIEW current_tips AS
SELECT DISTINCT ON (tipper, tippee) *
FROM tips
ORDER BY tipper, tippee, mtime DESC;
CREATE CAST (current_tips AS tips) WITH INOUT;
CREATE FUNCTION compute_arrears(tip current_tips) RETURNS currency_amount AS $$
SELECT compute_arrears(tip::tips);
$$ LANGUAGE sql;
ALTER TABLE payin_transfers
ALTER COLUMN visibility DROP DEFAULT,
ALTER COLUMN visibility SET NOT NULL;
END;
Loading

0 comments on commit 948ad79

Please sign in to comment.