Das Modul repräsentiert einen einzelnen Online-Kalender.
Eine Kalender-Instanz wird nicht über Instanz hinzufügen eingerichtet, sondern aus der gefundenen Liste des Kalender Konfigurators erstellt. Dabei werden Name, technische Kalender-ID, Anbieter-ID, Farbe, Schreibrechte und das richtige Kalender Konto automatisch übernommen.
Nach der Erstellung:
Kalender-Instanzen nicht manuell anlegen oder lediglich über „Gateway ändern“ mit einem Konto verbinden. Sie sollen über den zum Konto gehörenden Kalender Konfigurator erstellt werden. Nur der Konfigurator trägt den tatsächlichen Kalendernamen, die interne Identität, die Farbe, die Schreibrechte und die korrekte Kontoverbindung vollständig ein. Das ist besonders bei Konten mit mehreren Kalendern erforderlich.
Nach der Erstellung durch den Konfigurator darf die Instanz im Objektbaum beliebig verschoben oder vom Benutzer umbenannt werden.
Google-, Microsoft-, Apple-iCloud- und CalDAV-Serien können als einzelnes Vorkommnis, als vollständige Serie oder ab dem ausgewählten Vorkommnis für alle folgenden Termine bearbeitet und gelöscht werden. Beim Bearbeiten teilt OpenCalendar eine unterstützte Serie am gewählten Termin in einen unveränderten vorderen und einen neu angelegten hinteren Serienteil. Beim Löschen wird der bestehende Parent direkt vor dem ausgewählten Vorkommnis beendet; beginnt die Auswahl beim ersten Vorkommnis, wird die komplette Serie gelöscht. Bei nummerierten Serien übernimmt der neue Serienteil nur die verbleibende Anzahl. Bestehende Ausnahmen ab dem Trennpunkt werden beim Teilen nicht in die neue Serie übernommen. Bei CalDAV werden Einzeländerungen weiterhin als RECURRENCE-ID-Ausnahmen gespeichert und Einzellöschungen über EXDATE abgebildet. Beim Bearbeiten der vollständigen CalDAV-Serie wird nur der Serien-Master geändert; vorhandene Ausnahmen bleiben erhalten.
| Eigenschaft | Beschreibung |
|---|---|
| Aktiv | Aktiviert die regelmäßige Synchronisation |
| Aktualisierungsplan | Vorgegebener Rhythmus von fünf Minuten bis jährlich oder ausschließlich manuelle Synchronisation |
| Benutzerdefiniertes Intervall | Eigener Abstand in Minuten; wird nur beim Zeitplan „Benutzerdefiniertes Intervall“ angezeigt |
| Vergangene Termine laden | Anzahl der Tage vor dem aktuellen Datum |
| Zukünftige Termine laden | Anzahl der Tage nach dem aktuellen Datum |
| Kalenderidentität | Vom Konfigurator gesetzte, schreibgeschützte Anbieterinformationen |
Bestehende Instanzen behalten ihren bisherigen Minutenwert als benutzerdefiniertes Intervall. Monatliche und jährliche Zeitpläne werden intern täglich auf Fälligkeit geprüft, damit keine für lange Zeiträume ungeeigneten Millisekunden-Timer verwendet werden. Jetzt synchronisieren bleibt unabhängig vom Zeitplan jederzeit verfügbar.
| Variable | Typ | Beschreibung |
|---|---|---|
| Anzahl Termine | Integer | Anzahl der aktuell zwischengespeicherten Termine |
| Termine heute | Integer | Anzahl der Termine, die den aktuellen lokalen Kalendertag zeitlich überlappen |
| Letzte Synchronisation | Integer | Unix-Zeitpunkt der letzten erfolgreichen Abfrage |
Termine heute berücksichtigt auch ganztägige und mehrtägige Termine. Der Wert wird bei jeder Synchronisation und zusätzlich beim lokalen Tageswechsel neu berechnet. Beim Tageswechsel werden außerdem offene überfällige Aufgabentermine eines beschreibbaren Kalenders auf den neuen Tag verschoben.
Die eigentlichen Termindaten werden bewusst nicht in einer Statusvariable gespiegelt, sondern nur im internen Modulcache gehalten. Konto, Kalender und Kalenderansicht übertragen große Terminmengen automatisch in begrenzten Seiten. Dadurch wird weder bei der Synchronisation noch beim Aufbau der Ansicht eine einzelne JSON-Antwort mit sämtlichen Terminen benötigt.
Ein Termin enthält unter anderem id, uid, resourceUrl, etag, summary, description, location, start, end, startTimestamp, endTimestamp, allDay, status, recurrenceRule und recurrenceId. Wurde der Titel durch ein ausgewähltes iCalendar-Übersetzungsprofil angepasst, enthält originalSummary zusätzlich den unveränderten Originaltitel. Aufgabentermine enthalten außerdem task, taskCompleted, taskStatus (open oder completed), taskFollowPlanned und einen von der Statusmarkierung bereinigten displaySummary. Als Jahresereignis markierte Termine erhalten zusätzlich anniversaryType, anniversaryDate, years und displaySummary. Unterstützt werden birthday, anniversary, wedding und death. Für Geburtstage bleiben zusätzlich die kompatiblen Felder birthday, birthDate und age erhalten. Das Ausgangsdatum wird lokal in OpenCalendar gespeichert; der eigentliche Titel beim Kalenderanbieter bleibt unverändert.
bool IPSKAL_Synchronize(int $InstanzID);
string IPSKAL_GetEvents(int $InstanzID);
string IPSKAL_GetEventForEdit(int $InstanzID, string $EventJSON);
string IPSKAL_GetAnniversaryList(int $InstanzID, int $Days = 0, string $Type = '');
string IPSKAL_GetBirthdayList(int $InstanzID, int $Days = 0);
bool IPSKAL_SetAnniversary(int $InstanzID, string $EventJSON, string $Type, string $Date);
string IPSKAL_GetRecurringSeries(int $InstanzID, string $SeriesID, string $ResourceURL = '');
string IPSKAL_GetRecurringFollowing(int $InstanzID, string $SeriesID, string $OccurrenceID, string $OriginalStart, string $ResourceURL = '');
string IPSKAL_BeginEventsTransfer(int $InstanzID, int $StartTimestamp, int $EndTimestamp);
string IPSKAL_ReadEventsTransferPage(int $InstanzID, string $Token, int $Page);
bool IPSKAL_FinishEventsTransfer(int $InstanzID, string $Token);
string IPSKAL_CreateEvent(int $InstanzID, string $EventJSON);
string IPSKAL_UpdateEvent(int $InstanzID, string $EventJSON);
bool IPSKAL_DeleteEvent(int $InstanzID, string $EventJSON);
string IPSKAL_GetCalendarStatus(int $InstanzID);
void IPSKAL_ClearCache(int $InstanzID);
IPSKAL_GetEvents() bleibt als kompatibler Direktabruf für kleine Datenmengen
erhalten. Eigene Integrationen mit potenziell vielen Terminen sollten einen
Transfer beginnen, die Seiten von 0 bis PageCount - 1 abrufen und den
Transfer anschließend auch im Fehlerfall beenden. StartTimestamp ist inklusiv,
EndTimestamp exklusiv.
IPSKAL_GetEventForEdit() lädt vor dem Bearbeiten den aktuellen Providerstand
eines Termins mit seinen schreibrelevanten Identitätsfeldern und dem ETag.
EventJSON enthält den aus GetEvents() erhaltenen Termin einschließlich
startTimestamp, endTimestamp und seiner Provideridentität. Die Rückgabe ist
der normalisierte Termin als JSON, kein success-Wrapper. Bei einem fehlgeschlagenen
Providerabruf kann ausschließlich für einen bereits lokal bekannten Aufgabentermin
der passende Cacheeintrag zurückgegeben werden. Andernfalls wird eine Ausnahme
ausgelöst und der Fehler im Kalenderstatus gespeichert.
IPSKAL_GetAnniversaryList() liefert die in dieser Kalenderinstanz von OpenCalendar verwalteten Jahresereignisse nach dem nächsten Vorkommnis sortiert. Days = 0 liefert alle Einträge; jeder positive Wert begrenzt die Ausgabe auf die frei wählbare Anzahl der nächsten Kalendertage. Der optionale Filter Type akzeptiert birthday, anniversary, wedding oder death; ein leerer Wert liefert alle Typen. Die Datensätze enthalten name, anniversaryType, anniversaryDate, nextDate, years, displayName und daysUntil. Für Geburtstage werden zusätzlich birthDate, nextBirthday und age geliefert. IPSKAL_GetBirthdayList() bleibt als kompatibler Spezialfall erhalten und entspricht dem Filter birthday.
IPSKAL_SetAnniversary() markiert eine bereits vorhandene wiederkehrende Serie lokal als Jahresereignis. Type akzeptiert ebenfalls birthday, anniversary, wedding oder death; Date enthält das ursprüngliche Datum im Format YYYY-MM-DD. Als EventJSON kann ein Termin aus IPSKAL_GetEvents() verwendet werden. Für eine vollständige Serie ist der von IPSKAL_GetRecurringSeries() gelieferte Parent-Termin vorzuziehen. Die Funktion verändert weder Titel noch Wiederholungsregel beim Kalenderanbieter, sondern speichert ausschließlich die OpenCalendar-Metadaten.
$series = IPSKAL_GetRecurringSeries(12345, 'provider-series-id');
IPSKAL_SetAnniversary(12345, $series, 'birthday', '1993-07-20');
IPSKAL_GetCalendarStatus() liefert neben Synchronisations- und Zählerinformationen
auch calendarColor, canWrite, timezone, canCreateRecurrence, canUpdateFollowing,
canUpdateSeries und canDeleteSeries. Die Serienfähigkeiten und die Zeitzone werden aus den vom
Provider erkannten Kalender-Metadaten übernommen.
$result = IPSKAL_CreateEvent(12345, json_encode([
'summary' => 'Besprechung',
'description' => 'Projektstatus abstimmen',
'location' => 'Büro',
'start' => '2026-07-20T10:00:00+02:00',
'end' => '2026-07-20T11:00:00+02:00'
]));
Bei ganztägigen Terminen werden start und end als Datum angegeben. Das Ende ist entsprechend iCalendar exklusiv:
$result = IPSKAL_CreateEvent(12345, json_encode([
'summary' => 'Urlaub',
'start' => '2026-08-03',
'end' => '2026-08-08',
'allDay' => true
]));
Ein Aufgabentermin ist ein eintägiger, ganztägiger Kalendertermin. Er darf
wiederkehrend sein.
task = true setzt beim Anbieter automatisch den offenen Marker [OC:TODO] vor den
Titel. taskCompleted = true verwendet stattdessen [OC:DONE]. Die Steuerfelder werden
nicht als eigene Providerdaten übertragen; der Titelmarker ist die dauerhafte und
anbieterübergreifende Kennzeichnung.
$result = IPSKAL_CreateEvent(12345, json_encode([
'summary' => 'Versicherung prüfen',
'task' => true,
'taskCompleted' => false,
'allDay' => true,
'start' => '2026-09-10',
'end' => '2026-09-11'
]));
Zum Erledigen wird die Identität aus IPSKAL_GetEvents() zusammen mit der
Statusänderung übergeben. Eine erneute Änderung auf false öffnet die Aufgabe
wieder:
$result = IPSKAL_UpdateEvent(12345, json_encode([
'uid' => 'event-uid@example',
'resourceUrl' => 'https://server.example/calendar/task.ics',
'etag' => '"123456"',
'changes' => [
'task' => true,
'taskCompleted' => true
]
]));
Offene Aufgabentermine mit einem Datum vor heute werden beim lokalen
Tageswechsel und bei jeder Synchronisation auf heute verschoben. Erledigte
Aufgaben bleiben unverändert. Bei einer Aufgabenserie verschiebt
taskFollowPlanned = true den ab dem überfälligen Termin verbleibenden
Serienteil; ohne diese Option bleibt der ursprüngliche Serienplan erhalten und
nur das älteste überfällige Vorkommnis wird nachgezogen. Das Mitverschieben
erfordert eine Kalenderanbieter-Unterstützung für „diesen und alle folgenden
Termine“. Da dabei der echte Kalendertermin aktualisiert wird, muss der Kalender
beschreibbar sein. Zeitgebundene Aufgabentermine werden abgewiesen. Liegt ohne
Mitverschieben bereits ein weiteres geplantes Vorkommnis zwischen dem alten und
dem neuen Datum, wird die offene Aufgabe als Einzeltermin weitergeführt. So
bleibt der Serienplan erhalten und Microsoft 365 kann die Synchronisation nicht
wegen eines überlappenden Serienelements ablehnen.
Bei UpdateEvent gilt taskFollowPlanned ebenfalls für eine Änderung des
Startdatums einer Serienaufgabe: Der ausgewählte und alle folgenden Termine
werden mit neu verankertem Serienplan verschoben. Beim ersten Vorkommnis ist
das die ganze Serie. Statusänderungen ohne Datumsänderung bleiben auf das
einzelne Vorkommnis begrenzt. Bestehende Ausnahmen im verschobenen Serienteil
werden zurückgesetzt.
Die Zuordnung eines nachgezogenen Einzeltermins zu seiner Ursprungsserie bleibt auch außerhalb des eingestellten Synchronisationszeitraums erhalten. Eine fehlende Aufgabe wird, soweit der Anbieter dies unterstützt, gezielt anhand ihrer Identität geprüft. Nur bestätigte Erledigung, Entfernung der Aufgabenkennzeichnung oder Löschung gibt die Serie wieder frei. Bei einem vorübergehenden Abfragefehler bleibt die Zuordnung vorsichtshalber bestehen.
Eine vom Kalenderanbieter bestätigte Erstellung, Änderung oder Löschung bleibt
erfolgreich, auch wenn das anschließende Aktualisieren des lokalen Caches
fehlschlägt. CreateEvent und UpdateEvent liefern dann weiterhin
success = true und den bestätigten Termin; error enthält gegebenenfalls die
nachgelagerte Fehlermeldung. DeleteEvent liefert weiterhin true.
Der Aktualisierungsfehler ist außerdem über GetCalendarStatus().lastError
erkennbar. Den Schreibvorgang deshalb nicht erneut ausführen, sondern die
Synchronisation wiederholen. Beim Wechsel in einen anderen Kalender wird die
Zielkopie nicht aufgrund eines solchen nachgelagerten Lesefehlers gelöscht.
Erfolgreich empfangene Kalenderdaten werden vor dem zugehörigen Synchronisationsmarker gespeichert. Scheitert danach das automatische Nachziehen einer Aufgabe, bleiben auch unabhängige neue oder geänderte Termine erhalten. Die Synchronisation meldet den Aufgabenfehler und kann erneut gestartet werden.
Für beschreibbare Google-, Microsoft-, Apple-iCloud- und CalDAV-Kalender können beim Erstellen zusätzlich
providerneutrale Serienangaben übergeben werden. Bei Google verwendet OpenCalendar
die Kalenderzeitzone. Für Microsoft und CalDAV wird die übergebene Zeitzone verwendet;
fehlt sie bei einem Aufruf über die Visualisierung, wird die Zeitzone des Clients
verwendet. CalDAV schreibt für zeitgebundene Serien zusätzlich einen passenden
VTIMEZONE-Block. Dadurch bleibt die lokale Uhrzeit auch über
Sommer-/Winterzeitwechsel erhalten:
$result = IPSKAL_CreateEvent(12345, json_encode([
'summary' => 'Jour fixe',
'start' => '2026-08-17T10:00:00+02:00',
'end' => '2026-08-17T11:00:00+02:00',
'recurrence' => [
'frequency' => 'WEEKLY',
'interval' => 1,
'byDay' => ['MO'],
'endMode' => 'until',
'until' => '2026-12-31'
]
]));
Unterstützt werden für Google, Microsoft und CalDAV/Apple iCloud DAILY, WEEKLY, MONTHLY und
YEARLY, ein Intervall, bei wöchentlichen Serien optionale Wochentage sowie die
Endarten never, count und until. Bei Microsoft entspricht eine monatliche
Serie dem Kalendertag des Starttermins und eine jährliche Serie zusätzlich dessen
Monat. Bei Google, Microsoft, Apple iCloud und CalDAV können einzelne Vorkommnisse,
die vollständige Serie sowie dieses und alle folgenden Vorkommnisse bearbeitet
und gelöscht werden, sofern OpenCalendar die Wiederholungsregel verlustfrei teilen
kann. Vor dem Bearbeiten einer vollständigen Serie lädt
IPSKAL_GetRecurringSeries() den verifizierten Parent-Termin. Für „dieses und
folgende“ liefert IPSKAL_GetRecurringFollowing() zusätzlich das verifizierte
Zielvorkommnis und passt bei COUNT-Serien die verbleibende Anzahl an. Für CalDAV
kann die bereits bekannte ResourceURL an beide Funktionen übergeben werden; damit
wird das Kalenderobjekt direkt geladen. Beim Speichern wird die ursprüngliche Serie
unmittelbar vor dem Zieltermin beendet und ab dem Ziel eine neue Serie angelegt.
Bestehende Ausnahmen ab dem Trennpunkt gehören dadurch nicht zum neuen Serienteil.
Beim Löschen bleibt nur der vordere Serienteil bestehen.
uid, resourceUrl und etag stammen bei Einzelterminen und Vorkommnissen aus
IPSKAL_GetEvents. Für die vollständige Google-Serie sollten diese Werte aus
IPSKAL_GetRecurringSeries() verwendet werden; für „dieses und folgende“ aus
IPSKAL_GetRecurringFollowing(). Unter changes werden nur die zu
ändernden Felder übergeben:
$result = IPSKAL_UpdateEvent(12345, json_encode([
'uid' => 'event-uid@example',
'resourceUrl' => 'https://server.example/calendar/event.ics',
'etag' => '"123456"',
'changes' => [
'summary' => 'Geänderte Besprechung',
'location' => 'Konferenzraum'
]
]));
$success = IPSKAL_DeleteEvent(12345, json_encode([
'resourceUrl' => 'https://server.example/calendar/event.ics',
'etag' => '"123456"'
]));
Nach einer vom Anbieter bestätigten Schreiboperation wird versucht, den lokalen Termincache erneut vom Server zu laden. Schlägt nur dieses Nachladen fehl, bleibt der Schreibvorgang erfolgreich; Details und Hinweise zum erneuten Synchronisieren stehen unter Schreibvorgänge und Synchronisationsfehler.
| Problem | Prüfung |
|---|---|
| Konfiguration unvollständig | Instanz im Kalender Konfigurator löschen und aus der aktuellen Kontoliste neu erstellen; die technischen Identitätsfelder nicht manuell setzen |
| Synchronisation fehlgeschlagen | Zuerst im verbundenen Kalender Konto Verbindung testen, anschließend Konto und Kalender erneut synchronisieren |
| Keine Termine sichtbar | Zeitraum für vergangene und zukünftige Termine prüfen und kontrollieren, ob der Online-Kalender im gewählten Zeitraum Termine enthält |
| Kalender ist schreibgeschützt | Schreibrechte beim Anbieter prüfen; ICS/Webcal-Abonnements sind immer schreibgeschützt |
| Ändern oder Löschen wird bei einem Serientermin verweigert | Google, Microsoft und unterstützte Apple/CalDAV-RRULEs erlauben Vorkommnis, dieses und folgende sowie vollständige Serie; komplexe Wiederholungsregeln werden nicht verlustbehaftet geteilt |
| Schreibkonflikt | Kalender erneut synchronisieren; der ETag-Schutz verhindert das Überschreiben einer zwischenzeitlich geänderten Serverversion |