Commit Graph
8 Commits
Author SHA1 Message Date
arnefandCopilot d0e1dab837 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>
2026-08-21 06:41:51 +02:00
arnefandCopilot 1c039e6396 Add week view to web calendar with Month/Week toggle
Extract the shared per-calendar event-collection logic out of
buildMonthView into collectCalendarEvents, keyed by day so it can feed
either the existing 42-cell month grid or a new 7-cell week grid.

Add templates.WeekDay/WeekViewData, a WeekView templ, and a
viewSwitcher component shown in both views' headers to switch between
Month and Week. Add buildWeekView, handleCalendarWeek, parseWeekStart,
and mondayOf in internal/web/calendar.go, and register the new
/calendar/week route.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-21 06:19:59 +02:00
arnefandCopilot 213b22f821 Fix 500 on REPORT for recurring events with Windows timezone IDs
DAVx5 reported a 500 Internal Server Error on a time-range REPORT
against a calendar containing a recurring event whose DTSTART/DTEND used
a Windows-style TZID (e.g. "W. Europe Standard Time", as commonly written
by Outlook/Exchange and some Thunderbird/Lightning setups) instead of an
IANA zone name.

go-ical resolves TZID via a plain time.LoadLocation call, which only
understands IANA names. A simple decode of such an event succeeds (RRULE
dates aren't parsed eagerly), but expanding its recurrence - which
go-webdav's caldav.Filter does for every time-range REPORT - calls
Component.RecurrenceSet, which does call time.LoadLocation(tzid) and
fails with "ical: error parsing start time: unknown time zone ...".

Add internal/icalfix, a small shared helper that rewrites recognized
Windows timezone identifiers (TZID parameters and VTIMEZONE TZID: lines)
to their IANA equivalent in raw ICS bytes before decoding. Wire it into
every ical.NewDecoder call site: internal/caldav/backend.go's
decodeObject (fixes the reported bug), internal/web/calendar.go's event
rendering/ICS import, and internal/icssub's remote feed fetching, so a
subscribed feed with the same issue doesn't hit it either.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 22:23:11 +02:00
arnefandCopilot 1707ab4060 Add ICS/webcal HTTP subscription calendars
Users can now subscribe to a remote ICS/webcal feed from the dashboard
(name + color), the same way they set up a real calendar or the virtual
Birthdays calendar. Subscriptions are read-only, per-user, and share the
"/cal/home/<name>/" namespace with real calendars and "birthdays" (name
collisions are rejected in both directions).

- internal/db: new ics_subscriptions table + CRUD (internal/db/ics.go);
  CreateCalendarWithColor checks for a colliding subscription name.
- internal/icssub: shared HTTP-fetch + TTL cache (15 min) for remote ICS
  calendars, with webcal:// -> https:// rewriting and stale-on-error
  fallback, used by both the web UI and the CalDAV backend.
- internal/web: dashboard "Subscribe to an ICS/webcal calendar" form,
  color picker, delete button (internal/web/ics.go,
  templates/dashboard.templ); month view renders subscription events in
  their chosen color, read-only (internal/web/calendar.go).
- internal/caldav: subscriptions are exposed as read-only calendars
  (internal/caldav/ics.go) - listed in PROPFIND, events served via
  GET/REPORT, PUT/DELETE on individual events rejected with 403, but
  DELETE on the calendar itself unsubscribes; calendar-color is injected
  the same way as for real calendars and Birthdays.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 22:10:49 +02:00
arnefandCopilot b451f1a76e Make birthdays calendar color configurable and expose it via CalDAV
- Add a per-user birthday_calendars table (color, cascade-deletes with
  the user) and GetBirthdayCalendarColor/SetBirthdayCalendarColor in
  internal/db, plus a reservedCalendarNames guard ("birthdays") in
  CreateCalendarWithColor so no real calendar can collide with the
  synthetic one, whether created via the web UI, nidusctl, or CalDAV
  MKCALENDAR.
- Add a "Birthdays" virtual resource card to the dashboard (color
  picker only, no delete/share controls) backed by a new
  ResourceCard.Virtual flag and POST /web/resources/birthdays/color
  handler.
- Extract the birthday-parsing/generation logic shared by the web
  calendar view and CalDAV into internal/birthdays (ParseBirthday,
  Collect, OccurrenceDate, Summary) instead of duplicating it.
- Expose the Birthdays calendar over real CalDAV in
  internal/caldav/backend.go + birthdays.go: it's always listed for
  every user, generates one VEVENT per (contact, year) for a rolling
  window (current year -2..+8) with "🎂 Name (Age)" titles, is
  read-only (Put/Delete/DeleteCalendar all return 403), and its
  Apple/DAVx5 calendar-color is injected from the same per-user
  setting used by the dashboard/web view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 20:09:59 +02:00
arnefandCopilot cd96b365d0 Add virtual Birthdays calendar computed from contacts
The combined month view now always includes a read-only "Birthdays"
calendar (ref "@birthdays"), generated on the fly from every contact's
BDAY field across the user's address books — no calendar objects are
stored for it. Each birthday appears as an all-day event titled
"🎂 Name (Age)" (age omitted if BDAY has no year, e.g. "--MM-DD"), colored
pink, and links to the contact's edit page instead of an event editor.
Handles both "YYYY-MM-DD"/"YYYYMMDD" and year-less vCard BDAY formats,
and skips Feb 29 birthdays in non-leap years.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 19:27:55 +02:00
arnefandCopilot adabfd22ca Add ICS import/export and shared calendar support to calendar UI
- Combine own and shared calendars into a single /web/calendar month
  view, each event colored per its source calendar.
- New event creation now uses a calendar <select> (only writable
  calendars offered); editing keeps the event's original calendar fixed.
- Shared calendars use an "owner~name" reference in URLs, resolved via
  resolveCalRef which also enforces read/write permissions from
  calendar_shares (read-only shares can view/export but not edit/delete).
- Add per-calendar ICS export (single event and export-all) and import,
  splitting multi-VEVENT uploads into individual stored objects while
  preserving VTIMEZONE definitions and source VERSION/PRODID.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 18:58:04 +02:00
arnefandCopilot cd2cfa6c06 Add web UI for CalDAV calendars (web/calendar)
- List of the user's own calendars (web/calendar)
- Month view per calendar with a Monday-first 6-week grid, showing
  all-day and timed events, colored dot matching the calendar's color
- Navigation between months (prev/next/today) via query params
- Create/edit/delete events: title, description, location, all-day
  toggle, start/end date+time
- Clicking a day cell prefills a new all-day event on that date; the
  "New event" button defaults to a one-hour slot starting next full hour
- Deleting an event or invalid time ranges (end before/equal start) are
  validated with inline error messages
- Verified round-trip with the real CalDAV protocol handler (events
  created via the web UI are correctly visible to a REPORT query)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 16:58:59 +02:00