mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 05:27:35 +00:00
fix: calendar attendee RSVP correctness — server-derived status and hide declined invites (#27007)
* fix: let only the attendee set their own calendar RSVP status
set_attendees took each attendee's status from the caller-supplied value, so an
event organiser could set another user's RSVP (for example to 'accepted') on
create or update. RSVP is meant to be self-service: the /events/{id}/rsvp
endpoint already scopes status changes to the calling user.
Derive attendee status server-side instead of from the request. An existing
attendee keeps the status they set via RSVP and a newly added attendee starts
'pending'; any caller-supplied status is ignored. Event edits no longer reset
attendees' existing responses.
Co-authored-by: legobattman <302282032+legobattman@users.noreply.github.com>
* fix: hide declined calendar invites from the attendee view
`get_events_by_range` surfaced every event where the user is an attendee regardless of their RSVP status, so declining an invite left it in the calendar with no way to remove it. Exclude `declined` attendee rows from the attendee branch, so a decline now removes the event from the user's own view while pending, accepted and tentative invitations still surface.
Co-Authored-By: legobattman <302282032+legobattman@users.noreply.github.com>
---------
Co-authored-by: legobattman <302282032+legobattman@users.noreply.github.com>
This commit is contained in:
parent
48f78ca58d
commit
9b635d8f3d
1 changed files with 21 additions and 6 deletions
|
|
@ -500,9 +500,12 @@ class CalendarEventTable:
|
|||
# Filter to requested calendars only
|
||||
accessible_cal_ids = [c for c in accessible_cal_ids if c in calendar_ids]
|
||||
|
||||
# Also get event IDs where user is an attendee
|
||||
# Also get event IDs where the user is an attendee, excluding invites they declined
|
||||
attendee_event_ids_result = await db.execute(
|
||||
select(CalendarEventAttendee.event_id).filter(CalendarEventAttendee.user_id == user_id)
|
||||
select(CalendarEventAttendee.event_id).filter(
|
||||
CalendarEventAttendee.user_id == user_id,
|
||||
CalendarEventAttendee.status != 'declined',
|
||||
)
|
||||
)
|
||||
attendee_event_ids = [r[0] for r in attendee_event_ids_result.all()]
|
||||
|
||||
|
|
@ -764,22 +767,34 @@ class CalendarEventAttendeeTable:
|
|||
async def set_attendees(
|
||||
self, event_id: str, attendees: list[dict], db: Optional[AsyncSession] = None
|
||||
) -> list[CalendarEventAttendeeModel]:
|
||||
"""Replace all attendees for an event.
|
||||
"""Replace all attendees for an event ({user_id, meta?} per dict).
|
||||
|
||||
Each dict in attendees: {user_id: str, status?: str, meta?: dict}
|
||||
RSVP status is the attendee's alone to set (via update_rsvp): an existing
|
||||
attendee keeps their status, a newly added one starts 'pending'. A
|
||||
caller-supplied status is ignored so an organiser cannot set it for others.
|
||||
"""
|
||||
async with get_async_db_context(db) as db:
|
||||
existing_status = {
|
||||
row.user_id: row.status
|
||||
for row in (
|
||||
await db.execute(
|
||||
select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id)
|
||||
)
|
||||
).scalars()
|
||||
}
|
||||
|
||||
# Remove existing
|
||||
await db.execute(delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id))
|
||||
|
||||
now = int(time.time_ns())
|
||||
models = []
|
||||
for att in attendees:
|
||||
user_id = att['user_id']
|
||||
row = CalendarEventAttendee(
|
||||
id=str(uuid4()),
|
||||
event_id=event_id,
|
||||
user_id=att['user_id'],
|
||||
status=att.get('status', 'pending'),
|
||||
user_id=user_id,
|
||||
status=existing_status.get(user_id, 'pending'),
|
||||
meta=att.get('meta'),
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue