fix: CalDAV-Update DTSTART-Fehler und HA-Events read-only

- caldav_client: del+add statt direkter Zuweisung bei VEVENT-Properties
  (behebt "DTSTART MUST appear exactly once" Validierungsfehler)
- HA-Events als read-only behandeln (kein Bearbeiten/Löschen im Popup)
- [object Object] Toast behoben: HA-Events fallen nicht mehr in CalDAV-Pfad
This commit is contained in:
Scarriffle
2026-04-29 18:21:56 +02:00
parent d4ea097831
commit 134b238dea
2 changed files with 29 additions and 19 deletions

View File

@@ -237,31 +237,43 @@ def update_event(
continue
if "title" in data or "summary" in data:
component["SUMMARY"] = data.get("title", data.get("summary", ""))
if "SUMMARY" in component:
del component["SUMMARY"]
component.add("summary", data.get("title", data.get("summary", "")))
if "start" in data:
if "DTSTART" in component:
del component["DTSTART"]
if data.get("allDay"):
component["DTSTART"] = date.fromisoformat(data["start"][:10])
component.add("dtstart", date.fromisoformat(data["start"][:10]))
else:
component["DTSTART"] = _parse_dt(data["start"])
component.add("dtstart", _parse_dt(data["start"]))
if "end" in data:
if "DTEND" in component:
del component["DTEND"]
if data.get("allDay"):
component["DTEND"] = date.fromisoformat(data["end"][:10])
component.add("dtend", date.fromisoformat(data["end"][:10]))
else:
component["DTEND"] = _parse_dt(data["end"])
component.add("dtend", _parse_dt(data["end"]))
if "location" in data:
component["LOCATION"] = data["location"]
if "LOCATION" in component:
del component["LOCATION"]
component.add("location", data["location"])
if "description" in data:
component["DESCRIPTION"] = data["description"]
if "DESCRIPTION" in component:
del component["DESCRIPTION"]
component.add("description", data["description"])
if "color" in data:
component["X-CALENDARR-COLOR"] = data["color"]
if "X-CALENDARR-COLOR" in component:
del component["X-CALENDARR-COLOR"]
component.add("x-calendarr-color", data["color"])
if "rrule" in data:
if data["rrule"]:
component["RRULE"] = _parse_rrule_str(data["rrule"])
elif "RRULE" in component:
if "RRULE" in component:
del component["RRULE"]
if data["rrule"]:
component.add("rrule", _parse_rrule_str(data["rrule"]))
if "exdate" in data and data["exdate"]:
# Parse YYYYMMDD string into a proper EXDATE

View File

@@ -994,8 +994,8 @@ function showEventPopup(ev, anchor) {
popup.style.left = Math.max(8, left) + 'px';
popup.style.top = Math.max(8, top) + 'px';
// Hide edit/delete for read-only iCal subscription events
const isReadOnly = (ev.source === 'ical');
// Hide edit/delete for read-only sources (iCal subscriptions, Home Assistant)
const isReadOnly = (ev.source === 'ical' || ev.source === 'homeassistant');
document.getElementById('popup-edit').style.display = isReadOnly ? 'none' : '';
document.getElementById('popup-delete').style.display = isReadOnly ? 'none' : '';
@@ -1134,7 +1134,7 @@ function openNewEventModal(date) {
}
function openEditEventModal(ev) {
if (ev.source === 'ical') { showToast(t('event_readonly'), true); return; }
if (ev.source === 'ical' || ev.source === 'homeassistant') { showToast(t('event_readonly'), true); return; }
state.editingEvent = ev;
state.selectedEventColor = ev.color || '';
@@ -1426,11 +1426,9 @@ function bindEventModal() {
await api.put(`/local/events/${encodeURIComponent(ev.id)}`,
{ title, start, end, allDay, location: loc, description: desc, color: color || null, rrule: rrule || '' }
);
} else if (ev.source === 'ical') {
const subId = ev.calendar_id.replace('ical-', '');
await api.put(`/ical/events/${subId}/${encodeURIComponent(ev.id)}`,
{ title, start, end, allDay, location: loc, description: desc, color: color || null }
);
} else if (ev.source === 'ical' || ev.source === 'homeassistant') {
showToast(t('event_readonly'), true);
return;
} else {
await api.put(
`/caldav/events/${encodeURIComponent(ev.id)}?event_url=${encodeURIComponent(ev.url)}&calendar_id=${ev.calendar_id}`,