feat(sharing): group-shared calendars in every member's sidebar, person share picker, hidden profiles
- Backend: co-member group_visible calendars now surface in /local/calendars
(owned=false, shared_by=owner, read-only, group_shared) and in the normal
/caldav/events merge (via readable_local_calendar_ids), deduped against
direct shares / group calendars so nothing appears twice.
- Backend: new User.directory_hidden — a user can hide from sharing/group
pickers (/users/directory), while admin user management (/users/) still lists
them. Migration + profile GET/PUT.
- Backend: /groups/{id} members carry shares_calendar so clients can drop
phantom rows for members who share nothing.
- Frontend: reachable "Teilen" button on owned local calendars; share modal is
now a checkbox multi-select of users (checked = shared). Hidden-profile toggle
in Settings → Profile. Group member filter only lists members who actually
share (phantom fix). Calendars shared with me moved to a dedicated read-only
"shared with me" section in the manage table.
- Tests: group_visible propagation, no-share absence, dedup, directory_hidden.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -97,8 +97,44 @@ def is_calendar_owner(db: Session, user: models.User, calendar_id: int) -> model
|
||||
return cal
|
||||
|
||||
|
||||
def co_member_group_visible_calendars(db: Session, user: models.User) -> list[models.LocalCalendar]:
|
||||
"""Calendars that co-members of the user's groups share into the group.
|
||||
|
||||
Each user designates ONE of their own calendars via
|
||||
UserSettings.group_visible_calendar_id. This returns those calendars for
|
||||
every co-member of any group the user belongs to (excluding the user's own).
|
||||
The calendar must be owned by the designating member. Deduped (each calendar
|
||||
appears once even across multiple shared groups).
|
||||
"""
|
||||
my_group_ids = (
|
||||
db.query(models.GroupMember.group_id)
|
||||
.filter(models.GroupMember.user_id == user.id)
|
||||
)
|
||||
co_member_ids = (
|
||||
db.query(models.GroupMember.user_id)
|
||||
.filter(
|
||||
models.GroupMember.group_id.in_(my_group_ids),
|
||||
models.GroupMember.user_id != user.id,
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
return (
|
||||
db.query(models.LocalCalendar)
|
||||
.join(
|
||||
models.UserSettings,
|
||||
models.UserSettings.group_visible_calendar_id == models.LocalCalendar.id,
|
||||
)
|
||||
.filter(
|
||||
models.UserSettings.user_id.in_(co_member_ids),
|
||||
# the designating member must own the calendar they share
|
||||
models.LocalCalendar.user_id == models.UserSettings.user_id,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
def readable_local_calendar_ids(db: Session, user: models.User) -> list[int]:
|
||||
"""All local calendar ids the user may read: own + shared + group calendars."""
|
||||
"""All local calendar ids the user may read: own + shared + group + co-member group-visible."""
|
||||
ids: set[int] = set()
|
||||
|
||||
own = (
|
||||
@@ -123,4 +159,7 @@ def readable_local_calendar_ids(db: Session, user: models.User) -> list[int]:
|
||||
)
|
||||
ids.update(r[0] for r in group_cals)
|
||||
|
||||
# Calendars co-members share into shared groups (group-visible).
|
||||
ids.update(c.id for c in co_member_group_visible_calendars(db, user))
|
||||
|
||||
return list(ids)
|
||||
|
||||
Reference in New Issue
Block a user