diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index 2067ccfab5..b9be370f31 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -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,