Zum Inhalt springen

Kalender und Terminplanung

Control Center verbindet sich mit Google Calendar, damit dein Tag neben deiner Flotte liegt. Es synchronisiert deine Termine, zeigt sie in den Ansichten Monat, Woche, Tag und Agenda, warnt dich vor Beginn einer Besprechung und kann aus jedem Termin eine aufgezeichnete, zusammengefasste Besprechung machen.

Fast nichts. Die Integration holt Termine herein und stellt sie dar. Sie legt nichts an, bearbeitet nichts, löscht nichts und beantwortet keine Einladungen. Jede Device-Code-Verbindung, mit dem eingebauten oder einem eigenen Client, fordert calendar.readonly sowie openid email an.

Termine werden auf dem Server in die eigene Datenbank des Arbeitsbereichs synchronisiert (<dataDir>/<workspaceId>/workspace.db) und von jedem Client über RPC dargestellt. Kein Client verfügt über ein Google-Zugangsdatum.

Kalenderkonten sind an den Arbeitsbereich gebunden, wie alles andere in Control Center. Jeder Arbeitsbereich verbindet sein eigenes Google-Konto (oder mehrere), und Termine sowie Tokens eines Arbeitsbereichs sind aus einem anderen nie sichtbar. Mit dem Arbeitsbereich wechseln auch die Kalender, die du siehst.

Ein Arbeitsbereich kann mehrere Google-Konten verbinden, etwa ein Arbeitskonto und ein privates, und ihre Termine fließen in dieselben Ansichten.

Der cc_server im Headless-Betrieb hat keinen Browser, der einen OAuth-Redirect entgegennehmen könnte. Deshalb nutzt er keinen: Er autorisiert mit dem Device-Code-Grant (RFC 8628). Der Server zeigt einen kurzen Nutzer-Code und eine Google-URL, du bestätigst auf einem beliebigen Gerät mit Browser, und der Server fragt so lange nach, bis du das tust. Eine Headless-Installation kann ein Konto genau deshalb verbinden, weil kein Redirect-Endpunkt zu betreiben ist.

Bevor du eigene Zugangsdaten mitbringst: Der Client-Typ muss Googles „TV and Limited Input devices“ sein. Google gewährt diesem Client-Typ nur eine fest zugelassene Menge an Scopes, und ein Projekt, in dem Calendar dafür nicht erlaubt ist, scheitert mit invalid_scope. Dann brauchst du einen Loopback- oder Web-Client.

Tokens verlassen den Server nie. Sie stehen in der secrets.json des Servers in seinem Datenverzeichnis, unter dem Schlüssel google_google:<workspaceId>:<email>, neben den Datenbanken und innerhalb derselben Vertrauensgrenze des Host-Dateisystems. Der Server erneuert den Zugriff automatisch und veröffentlicht, wenn Google ein Konto dauerhaft für ungültig erklärt (eine widerrufene oder lange abgelaufene Sitzung), CalendarAuthExpired. Dazu zeigt er das Banner Erneut verbinden, statt still zu scheitern.

Ein Build aus dem Quellcode bringt keine Google-Zugangsdaten mit. Client-ID und Secret gibst du selbst an. Offizielle Release-Builds bringen einen nicht vertraulichen Device-Code-Client mit, damit die Verbindung ohne weitere Einrichtung funktioniert. Der Server speichert nur eine Markierung, die darauf verweist, nie eine Kopie. GOOGLE_OAUTH_CLIENT_ID (und das zugehörige Secret) in der Umgebung hat immer Vorrang vor dem mitgelieferten Paar. Absichtlich gibt es kein Flag --google-client-id: Unbekannte Flags werden ignoriert, und ein Secret auf der Befehlszeile lässt sich über ps lesen.

Für ein eigenes Google-Projekt siehe Einen Google Calendar verbinden.

Der Server holt ein gleitendes Fenster der Termine jedes verbundenen Kontos, jeden Kalender des Kontos, nicht nur den primären, alle zwei Minuten, und zusätzlich bei Bedarf (Start, Verbinden, manuelles Aktualisieren). Das kurze Intervall bleibt vertretbar, weil der Durchlauf inkrementell ist: Jeder Kalender trägt ein eigenes Sync-Token. Ein unveränderter Kalender braucht damit nur eine fast leere Antwort, statt ein Fenster von fünf Monaten erneut zu laden.

Wiederkehrende Termine werden auf dem Server expandiert, sodass kein Client je eine Wiederholungsregel auswertet. Ein Monat außerhalb des aktuellen Fensters wird erst geladen, wenn du dorthin navigierst. Gelöschte Termine und solche, die aus dem Fenster verschoben wurden, fallen bei der nächsten Synchronisierung weg.

Ansicht Was sie zeigt
Monat Ein volles Monatsraster (über das Paket kalender)
Woche Termine mit Uhrzeit einer Woche im Zeitraster
Tag Termine mit Uhrzeit eines Tages im Zeitraster
Agenda Eine scrollbar durchlaufbare Liste anstehender Termine

Den Kalender öffnest du unter /calendar. Wählst du einen Termin, öffnet sich seine Detailansicht unter /calendar/:eventId. Die Ansicht, die du wählst, liegt in den eigenen Einstellungen dieses Clients und folgt dir nicht auf ein anderes Gerät.

Ein Scheduler prüft minütlich, ob Termine innerhalb eines einstellbaren Vorlaufs beginnen, und löst die Benachrichtigung „Meeting beginnt bald“ aus (ein MeetingStartingSoon-Domain-Ereignis). Liegt eine Beitritts-URL vor, hängt sie an der Benachrichtigung, und ein Klick öffnet die Termindetails. Hinweise werden anhand von alertedAt des Termins nur einmal zugestellt. Dieselbe Besprechung warnt damit nie zweimal, auch nicht über Neustarts hinweg, und ein Termin, dessen Beginn verstrich, während die App geschlossen war, wird nicht nachgeholt.

Zwei Grenzen sind leicht zu übersehen. Der Scheduler läuft clientseitig auf dem Desktop und nur für den Arbeitsbereich, den du gerade geöffnet hast. Ein Arbeitsbereich, den du nicht geöffnet hast, löst keinen Hinweis aus. Der Web-Build hat keine Pipeline für Betriebssystembenachrichtigungen und löst deshalb ebenfalls keine aus.

Von einem Kalendertermin aus kannst du eine Besprechungsaufnahme starten, die den Titel des Termins übernimmt. Control Center speichert eine Eins-zu-eins-Verknüpfung zwischen Aufnahme und Termin. Die Notizen der Besprechung und der Kalendereintrag bleiben so verbunden, ohne dass eine der beiden Funktionen die andere voraussetzt.

Aufnehmen und verknüpfen gibt es nur auf dem Desktop. Im Web-Client meldet die Aktion, dass die Aufnahme nicht verfügbar ist, obwohl der Aufnahmeablauf des Besprechungsbildschirms selbst im Browser funktioniert.