diff --git a/AddOn/Calender_Office365/GraphCalendar.php b/AddOn/Calender_Office365/GraphCalendar.php new file mode 100644 index 00000000..c452ab89 --- /dev/null +++ b/AddOn/Calender_Office365/GraphCalendar.php @@ -0,0 +1,305 @@ +clientId = $clientId; + $this->clientSecret = $clientSecret; + $this->tenantId = $tenantId; + $this->defaultUserEmail = $defaultUserEmail; + + // Authentifizierung gegen Microsoft Graph API durchführen + $this->authenticate(); + } + + /** + * Authentifiziert sich mit dem Client-Credentials-Flow (ohne Benutzerinteraktion) + */ + private function authenticate() + { + $tokenUrl = "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token"; + + // Daten für den OAuth2 Token-Request + $data = [ + 'grant_type' => 'client_credentials', + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'scope' => 'https://graph.microsoft.com/.default', // Zugriff auf alle berechtigten Graph APIs + ]; + + // Token abrufen + $response = $this->postCurl($tokenUrl, ['Content-Type: application/x-www-form-urlencoded'], $data); + $decoded = json_decode($response, true); + $this->token = $decoded['access_token']; + + // Standard-Header für alle API-Requests + $this->headers = [ + "Authorization: Bearer {$this->token}", + "Content-Type: application/json" + ]; + } + + /** + * Gibt eine gültige Benutzer-E-Mail zurück oder wirft einen Fehler + */ + private function resolveUserEmail($userEmail) + { + if (!empty($userEmail)) { + return $userEmail; + } elseif (!empty($this->defaultUserEmail)) { + return $this->defaultUserEmail; + } else { + throw new Exception("Keine Benutzer-E-Mail angegeben."); + } + } + + /** + * Gibt alle oder ein bestimmtes Kalender-Event zurück + */ + public function getEvents($eventId = "", $userEmail = "") + { + $email = $this->resolveUserEmail($userEmail); + + if($eventId == ""){ + // Alle Events des Nutzers abrufen + $eventsResponse = $this->getCurl("$this->graphUrl/users/{$email}/calendar/events", $this->headers); + return json_decode($eventsResponse, true)['value'] ?? []; + } else { + // Nur ein einzelnes Event anhand der ID abrufen + $eventsResponse = $this->getCurl("{$this->graphUrl}/users/{$email}/calendar/events/{$eventId}", $this->headers); + return json_decode($eventsResponse, true) ?? []; + } + } + + /** + * Erstellt ein neues Kalender-Event (mit optionalem Teams-Link und Teilnehmern) + */ + public function createEvent($subject, $startTime, $endTime, $isTeamsTermin = false, $description = '', $location = "", $gustArray = array(), $userEmail = "", $timezone = 'Europe/Berlin') + { + $email = $this->resolveUserEmail($userEmail); + + // Event-Daten vorbereiten + $event = [ + 'subject' => $subject, + 'start' => [ + 'dateTime' => $startTime, + 'timeZone' => $timezone + ], + 'end' => [ + 'dateTime' => $endTime, + 'timeZone' => $timezone + ], + 'body' => [ + 'contentType' => 'HTML', + 'content' => $description + ], + 'location' => [ + 'displayName' => $location + ] + ]; + + // Optional: Teams-Termin aktivieren + if($isTeamsTermin == true){ + $event['isOnlineMeeting'] = true; + $event['onlineMeetingProvider'] = 'teamsForBusiness'; + } + + // Teilnehmer hinzufügen, falls vorhanden + if(count($gustArray) > 0){ + $event['attendees'] = $gustArray; + } + + // Event erstellen + $url = "{$this->graphUrl}/users/{$email}/calendar/events"; + $response = $this->postJsonCurl($url, $this->headers, $event); + return json_decode($response, true); + } + + /** + * Bestehendes Event aktualisieren + */ + public function updateEvent($eventId, $subject, $startTime, $endTime, $isTeamsTermin = false, $description = '', $location = "", $gustArray = array(), $userEmail = "", $timezone = 'Europe/Berlin') + { + $email = $this->resolveUserEmail($userEmail); + + // Neue Eventdaten + $event = [ + 'subject' => $subject, + 'start' => [ + 'dateTime' => $startTime, + 'timeZone' => $timezone + ], + 'end' => [ + 'dateTime' => $endTime, + 'timeZone' => $timezone + ], + 'body' => [ + 'contentType' => 'HTML', + 'content' => $description + ], + 'location' => [ + 'displayName' => $location + ], + 'isOnlineMeeting' => $isTeamsTermin + ]; + + // Teams-Link ggf. setzen + if($isTeamsTermin == true){ + $event['onlineMeetingProvider'] = 'teamsForBusiness'; + } + + // Teilnehmer aktualisieren + if(count($gustArray) > 0){ + $event['attendees'] = $gustArray; + } + + // Event patchen (aktualisieren) + $url = "{$this->graphUrl}/users/{$email}/calendar/events/{$eventId}"; + $result = $this->patchJsonCurl($url, $this->headers, $event); + return [ + 'status' => $result['status'], + 'response' => json_decode($result['response'], true) + ]; + } + + /** + * Löscht ein Kalender-Event anhand der ID + */ + public function deleteEvent($eventId, $userEmail = "") + { + $email = $this->resolveUserEmail($userEmail); + $url = "{$this->graphUrl}/users/{$email}/calendar/events/{$eventId}"; + return $this->deleteCurl($url, $this->headers); + } + + /** + * Hängt eine Datei als Attachment an ein Kalender-Event. + * + * @param string $eventId Die ID des Events + * @param string $fileName Der Dateiname (z.B. "dokument.pdf") + * @param string $fileContent Der Inhalt der Datei als Base64-kodierter String + * @param string $contentType Der MIME-Typ der Datei (z.B. "application/pdf") + * @param string $userEmail Optional: Benutzer-E-Mail, falls abweichend + * @return array Antwort von der Graph API + */ + public function addAttachmentToEvent($eventId, $fileName, $fileContent, $contentType = 'application/octet-stream', $userEmail = '') + { + $email = $this->resolveUserEmail($userEmail); + + // Attachment-Objekt + $attachment = [ + '@odata.type' => '#microsoft.graph.fileAttachment', + 'name' => $fileName, + 'contentType' => $contentType, + 'contentBytes' => base64_encode($fileContent) + ]; + + $url = "{$this->graphUrl}/users/{$email}/events/{$eventId}/attachments"; + $response = $this->postJsonCurl($url, $this->headers, $attachment); + + return json_decode($response, true); + } + + + // === HELPER-FUNKTIONEN FÜR CURL === + + /** + * POST-Request mit URL-encoded Daten + */ + private function postCurl($url, $headers, $data) + { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => http_build_query($data), + ]); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } + + /** + * GET-Request + */ + private function getCurl($url, $headers) + { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers, + ]); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } + + /** + * POST-Request mit JSON-Daten + */ + private function postJsonCurl($url, $headers, $json) + { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => json_encode($json), + ]); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } + + /** + * PATCH-Request (teilweises Update von Ressourcen) + */ + private function patchJsonCurl($url, $headers, $json) + { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'PATCH', + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => json_encode($json), + ]); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return ['status' => $httpCode, 'response' => $response]; + } + + /** + * DELETE-Request + */ + private function deleteCurl($url, $headers) + { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_HTTPHEADER => $headers, + ]); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return $httpCode; + } +}