From d0e1dab83756d72b6a2b9418a0b22e1a3539c760 Mon Sep 17 00:00:00 2001 From: arnef Date: Fri, 21 Aug 2026 06:41:51 +0200 Subject: [PATCH] Use display name instead of username for shared calendar owners Add db.DisplayName(username), returning the user's display name if set, otherwise the raw username. Use it everywhere a shared calendar's owner is shown: the web calendar legend ("shared by ..."), the "new event"/edit calendar select label, the dashboard's "Shared with you" list, and the CalDAV-side description/name-collision disambiguation ("Name (Owner)") shown to CalDAV clients like DAVx5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/caldav/backend.go | 7 ++++--- internal/db/users.go | 13 +++++++++++++ internal/web/calendar.go | 6 +++--- internal/web/dashboard.go | 4 ++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/internal/caldav/backend.go b/internal/caldav/backend.go index 122f02a..c1c0903 100644 --- a/internal/caldav/backend.go +++ b/internal/caldav/backend.go @@ -382,7 +382,7 @@ func (b *Backend) calendarMeta(owner, realName, localName string) caldav.Calenda // their own calendars (see sharedDisplayName) — the Description still // always names the real calendar, only the client-visible Name changes. func (b *Backend) calendarMetaNamed(owner, realName, localName, displayName string) caldav.Calendar { - desc := fmt.Sprintf("%s's %s calendar", owner, realName) + desc := fmt.Sprintf("%s's %s calendar", b.dbase.DisplayName(owner), realName) return caldav.Calendar{ Path: calHomePath() + localName + "/", // The displayed name is always the plain calendar name (never the @@ -408,9 +408,10 @@ func (b *Backend) sharedDisplayName(requester, owner, realName string) string { if err != nil { b.logger.Warn("listing own calendars for disambiguation", "error", err) } + ownerLabel := b.dbase.DisplayName(owner) for _, c := range own { if c.Name == realName { - return fmt.Sprintf("%s (%s)", realName, owner) + return fmt.Sprintf("%s (%s)", realName, ownerLabel) } } @@ -423,7 +424,7 @@ func (b *Backend) sharedDisplayName(requester, owner, realName string) string { continue // the calendar itself, not a collision } if sh.CalendarName == realName { - return fmt.Sprintf("%s (%s)", realName, owner) + return fmt.Sprintf("%s (%s)", realName, ownerLabel) } } return realName diff --git a/internal/db/users.go b/internal/db/users.go index 1f5e48b..6edf783 100644 --- a/internal/db/users.go +++ b/internal/db/users.go @@ -90,6 +90,19 @@ func (d *DB) SetProfile(username, displayName, email string) error { return requireRowsAffected(res, ErrUserNotFound) } +// DisplayName returns username's display name if one is set, otherwise +// username itself. This is the friendly label to show wherever a user's +// identity is surfaced in the UI (e.g. "shared by "), instead of +// the raw login username. +func (d *DB) DisplayName(username string) string { + u, err := d.GetUser(username) + if err != nil || u.DisplayName == "" { + return username + } + return u.DisplayName +} + + // DeleteUser removes username along with all of its calendars, address // books, and sharing grants (calendars/addressbooks cascade via foreign diff --git a/internal/web/calendar.go b/internal/web/calendar.go index 5b6a969..acd7574 100644 --- a/internal/web/calendar.go +++ b/internal/web/calendar.go @@ -444,7 +444,7 @@ func (s *Server) collectCalendarEvents(username string, gridStart, gridEnd time. hasWritable = true } calSummaries = append(calSummaries, templates.CalendarSummary{ - Ref: entry.Ref, Name: entry.Name, Owner: entry.Owner, Color: entry.Color, + Ref: entry.Ref, Name: entry.Name, Owner: s.dbase.DisplayName(entry.Owner), Color: entry.Color, Shared: entry.Owner != username, Writable: entry.Writable, Virtual: entry.Virtual, }) @@ -565,7 +565,7 @@ func (s *Server) handleEventNew(w http.ResponseWriter, r *http.Request) { } label := e.Name if e.Owner != username { - label = e.Name + " (" + e.Owner + ")" + label = e.Name + " (" + s.dbase.DisplayName(e.Owner) + ")" } options = append(options, templates.CalendarOption{Ref: e.Ref, Label: label}) } @@ -668,7 +668,7 @@ func (s *Server) handleEventEdit(w http.ResponseWriter, r *http.Request) { form.CalRef = ref label := name if owner != username { - label = name + " (" + owner + ")" + label = name + " (" + s.dbase.DisplayName(owner) + ")" } form.CalendarLabel = label form.Writable = owner == username diff --git a/internal/web/dashboard.go b/internal/web/dashboard.go index 9a870c5..cc1368f 100644 --- a/internal/web/dashboard.go +++ b/internal/web/dashboard.go @@ -26,7 +26,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) { } for _, sh := range calShares { sharedWithMe = append(sharedWithMe, templates.SharedWithMeItem{ - Kind: "calendar", Owner: sh.Owner, Name: sh.CalendarName, Permission: string(sh.Permission), + Kind: "calendar", Owner: s.dbase.DisplayName(sh.Owner), Name: sh.CalendarName, Permission: string(sh.Permission), }) } bookShares, err := s.dbase.AddressBooksSharedWith(username) @@ -35,7 +35,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) { } for _, sh := range bookShares { sharedWithMe = append(sharedWithMe, templates.SharedWithMeItem{ - Kind: "addressbook", Owner: sh.Owner, Name: sh.AddressBookName, Permission: string(sh.Permission), + Kind: "addressbook", Owner: s.dbase.DisplayName(sh.Owner), Name: sh.AddressBookName, Permission: string(sh.Permission), }) }