Allow creators to delete events and restrict private invitations to friends

This commit is contained in:
2026-09-15 20:13:29 +02:00
parent bb28e17220
commit d1bb184b17
7 changed files with 163 additions and 18 deletions
+41 -11
View File
@@ -1027,11 +1027,7 @@ def can_edit_concert(user, concert) -> bool:
def can_delete_concert(user, concert) -> bool:
if not user:
return False
if concert["is_past"]:
return user["is_admin"]
return user["is_admin"] or user["id"] == concert["created_by"]
return bool(user and (user["is_admin"] or user["id"] == concert["created_by"]))
def can_manage_event_access(user, concert) -> bool:
@@ -1167,20 +1163,42 @@ def can_view_event(user, concert) -> bool:
return cursor.fetchone() is not None
def get_invitable_users(exclude_user_id: int):
def get_invitable_users(user_id: int):
"""Only confirmed, unblocked friends of the event owner can be newly invited."""
with get_db_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT id, username, COALESCE(display_name, username)
FROM users WHERE id <> %s
ORDER BY COALESCE(display_name, username), username
SELECT u.id, u.username, COALESCE(u.display_name, u.username)
FROM users u WHERE u.id <> %s
AND EXISTS (
SELECT 1 FROM friendships f WHERE f.status = 'accepted'
AND ((f.requester_id = %s AND f.addressee_id = u.id)
OR (f.addressee_id = %s AND f.requester_id = u.id))
)
AND NOT EXISTS (
SELECT 1 FROM user_blocks b
WHERE (b.blocker_id = %s AND b.blocked_id = u.id)
OR (b.blocked_id = %s AND b.blocker_id = u.id)
)
ORDER BY COALESCE(u.display_name, u.username), u.username
""",
(exclude_user_id,),
(user_id, user_id, user_id, user_id, user_id),
)
return [{"id": row[0], "username": row[1], "display_name": row[2]} for row in cursor.fetchall()]
def resolve_invitee_selection(owner_id: int, selected_ids, concert_id=None):
eligible = {member["id"] for member in get_invitable_users(owner_id)}
selected = set(selected_ids)
if not selected <= eligible:
raise ValueError(_("Es können nur bestätigte Freunde eingeladen werden."))
if concert_id is not None:
# Existing invitations outside the picker must not disappear on unrelated edits.
selected |= get_event_invitee_ids(concert_id) - eligible
return sorted(selected)
def get_event_invitee_ids(concert_id: int):
with get_db_connection() as connection:
with connection.cursor() as cursor:
@@ -4063,6 +4081,11 @@ async def create_concert(
return HTMLResponse(_("<h1>Ungültige Sichtbarkeit.</h1>"), status_code=400)
if event_type != "other":
visibility = "public"
if visibility == "private":
try:
invited_user_ids = resolve_invitee_selection(user["id"], invited_user_ids)
except ValueError as error:
return HTMLResponse(f"<h1>{error}</h1>", status_code=400)
duplicate_matches = find_duplicate_concerts(user, artist, start_datetime, band_names)
if duplicate_matches and not duplicate_confirmed:
match_items = "".join(
@@ -4213,7 +4236,7 @@ def edit_concert_page(request: Request, concert_id: int):
can_edit_title=can_edit_title(user, concert),
can_edit_details=can_edit_details(user, concert),
can_delete=can_delete_concert(user, concert),
invitable_users=get_invitable_users(user["id"]),
invitable_users=get_invitable_users(concert["created_by"]),
invited_user_ids=get_event_invitee_ids(concert_id),
can_manage_access=can_manage_event_access(user, concert),
band_names="\n".join(band["name"] for band in load_concert_bands(concert_id, concert["artist"], concert["event_type"])),
@@ -4295,6 +4318,13 @@ async def edit_concert(
return HTMLResponse(_("<h1>Ungültige Sichtbarkeit.</h1>"), status_code=400)
if event_type != "other":
visibility = "public"
if visibility == "private":
try:
invited_user_ids = resolve_invitee_selection(
concert["created_by"], invited_user_ids, concert_id
)
except ValueError as error:
return HTMLResponse(f"<h1>{error}</h1>", status_code=400)
else:
visibility = concert["visibility"]
effective_start = start_datetime or concert["start_local"]