548 lines
20 KiB
PHP
548 lines
20 KiB
PHP
<?php
|
|
/**
|
|
* Microsoft 365 / Graph API Integratie voor Fociz
|
|
* Beheert OAuth2 authenticatie, token refresh en kalendersynchronisatie.
|
|
*/
|
|
|
|
require_once __DIR__ . '/../http.php';
|
|
require_once __DIR__ . '/../../config/database.php';
|
|
|
|
const MS_OAUTH_AUTHORIZE_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0/authorize';
|
|
const MS_OAUTH_TOKEN_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0/token';
|
|
const MS_GRAPH_BASE_URL = 'https://graph.microsoft.com/v1.0';
|
|
const MS_DEFAULT_SCOPES = 'offline_access User.Read Calendars.ReadWrite';
|
|
|
|
/**
|
|
* Haalt de centrale Microsoft App Registratie configuratie op (Client ID, Secret, Tenant ID)
|
|
* Prioriteit:
|
|
* 1. Environment variabelen (.env of server env)
|
|
* 2. Opgeslagen configuratie van een Beheerder (ADMIN) in de integrations tabel
|
|
* 3. Fallback naar integrations tabel waar settings client_id bevat
|
|
*/
|
|
function microsoft_get_app_config(): array {
|
|
$clientId = fociz_env('MICROSOFT_CLIENT_ID', '');
|
|
$clientSecret = fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
|
$tenantId = fociz_env('MICROSOFT_TENANT_ID', '');
|
|
|
|
try {
|
|
$db = get_db_connection();
|
|
$adminOrgDomain = '';
|
|
|
|
// Zoek bij actieve beheerder(s) in de integrations tabel
|
|
$stmt = $db->query("
|
|
SELECT i.settings, u.email as admin_email
|
|
FROM integrations i
|
|
JOIN users u ON i.user_id = u.id
|
|
WHERE u.role = 'ADMIN' AND i.provider = 'OUTLOOK' AND i.settings IS NOT NULL
|
|
ORDER BY i.updated_at DESC
|
|
LIMIT 5
|
|
");
|
|
$rows = $stmt->fetchAll();
|
|
foreach ($rows as $row) {
|
|
if (!empty($row['admin_email']) && empty($adminOrgDomain)) {
|
|
$emailParts = explode('@', $row['admin_email']);
|
|
$domain = strtolower(trim(end($emailParts)));
|
|
if (!in_array($domain, ['gmail.com', 'outlook.com', 'hotmail.com', 'live.com', 'yahoo.com', 'icloud.com'])) {
|
|
$adminOrgDomain = $domain;
|
|
}
|
|
}
|
|
if (!empty($row['settings'])) {
|
|
$saved = json_decode($row['settings'], true) ?: [];
|
|
if (empty($clientId) && !empty($saved['client_id'])) {
|
|
$clientId = trim($saved['client_id']);
|
|
}
|
|
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
|
$clientSecret = trim($saved['client_secret']);
|
|
}
|
|
if ((empty($tenantId) || $tenantId === 'common') && !empty($saved['tenant_id']) && $saved['tenant_id'] !== 'common') {
|
|
$tenantId = trim($saved['tenant_id']);
|
|
}
|
|
if (!empty($clientId) && !empty($clientSecret) && !empty($tenantId) && $tenantId !== 'common') {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: zoek een willekeurige OUTLOOK rij met ingevulde client_id
|
|
if (empty($clientId) || empty($clientSecret) || empty($tenantId) || $tenantId === 'common') {
|
|
$stmtFallback = $db->query("
|
|
SELECT settings
|
|
FROM integrations
|
|
WHERE provider = 'OUTLOOK' AND settings LIKE '%client_id%'
|
|
ORDER BY updated_at DESC
|
|
LIMIT 3
|
|
");
|
|
$fallbackRows = $stmtFallback->fetchAll();
|
|
foreach ($fallbackRows as $fbRow) {
|
|
if (!empty($fbRow['settings'])) {
|
|
$saved = json_decode($fbRow['settings'], true) ?: [];
|
|
if (empty($clientId) && !empty($saved['client_id'])) {
|
|
$clientId = trim($saved['client_id']);
|
|
}
|
|
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
|
$clientSecret = trim($saved['client_secret']);
|
|
}
|
|
if ((empty($tenantId) || $tenantId === 'common') && !empty($saved['tenant_id']) && $saved['tenant_id'] !== 'common') {
|
|
$tenantId = trim($saved['tenant_id']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Als tenantId nog leeg of 'common' is, leid het domein af van de beheerder
|
|
if (empty($tenantId) || $tenantId === 'common') {
|
|
if (empty($adminOrgDomain)) {
|
|
$firstAdminEmail = $db->query("SELECT email FROM users WHERE role = 'ADMIN' ORDER BY id ASC LIMIT 1")->fetchColumn();
|
|
if ($firstAdminEmail) {
|
|
$parts = explode('@', $firstAdminEmail);
|
|
$domain = strtolower(trim(end($parts)));
|
|
if (!in_array($domain, ['gmail.com', 'outlook.com', 'hotmail.com', 'live.com', 'yahoo.com', 'icloud.com'])) {
|
|
$adminOrgDomain = $domain;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($adminOrgDomain)) {
|
|
$tenantId = $adminOrgDomain;
|
|
}
|
|
}
|
|
} catch (Throwable $e) {
|
|
error_log("microsoft_get_app_config error: " . $e->getMessage());
|
|
}
|
|
|
|
// Default tenantId voor de organisatie
|
|
if (empty($tenantId) || $tenantId === 'common') {
|
|
$tenantId = 'planningsagenda.nl';
|
|
}
|
|
|
|
return [
|
|
'client_id' => $clientId ?: '',
|
|
'client_secret' => $clientSecret ?: '',
|
|
'tenant_id' => $tenantId,
|
|
'is_configured' => !empty($clientId) && !empty($clientSecret),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Genereert de Microsoft OAuth2 login URL
|
|
*/
|
|
function microsoft_get_auth_url(string $clientId, string $redirectUri, string $tenantId = '', string $state = ''): string {
|
|
$tenant = (!empty($tenantId) && $tenantId !== 'common') ? trim($tenantId) : 'planningsagenda.nl';
|
|
$baseUrl = sprintf(MS_OAUTH_AUTHORIZE_URL, urlencode($tenant));
|
|
|
|
$params = [
|
|
'client_id' => trim($clientId),
|
|
'response_type' => 'code',
|
|
'redirect_uri' => trim($redirectUri),
|
|
'response_mode' => 'query',
|
|
'scope' => MS_DEFAULT_SCOPES,
|
|
'state' => $state,
|
|
'prompt' => 'select_account',
|
|
];
|
|
|
|
return $baseUrl . '?' . http_build_query($params);
|
|
}
|
|
|
|
/**
|
|
* Wisselt de ontvangen autorisatiecode om voor access- en refresh tokens
|
|
*/
|
|
function microsoft_exchange_code(string $code, string $clientId, string $clientSecret, string $redirectUri, string $tenantId = ''): array {
|
|
$tenant = (!empty($tenantId) && $tenantId !== 'common') ? trim($tenantId) : 'planningsagenda.nl';
|
|
$tokenUrl = sprintf(MS_OAUTH_TOKEN_URL, urlencode($tenant));
|
|
|
|
$payload = [
|
|
'client_id' => trim($clientId),
|
|
'client_secret' => trim($clientSecret),
|
|
'code' => trim($code),
|
|
'redirect_uri' => trim($redirectUri),
|
|
'grant_type' => 'authorization_code',
|
|
'scope' => MS_DEFAULT_SCOPES,
|
|
];
|
|
|
|
$response = fociz_http_request($tokenUrl, [
|
|
'method' => 'POST',
|
|
'body' => $payload,
|
|
'timeout' => 15,
|
|
]);
|
|
|
|
if ($response['status'] !== 200 || empty($response['json']['access_token'])) {
|
|
$errorMsg = $response['json']['error_description'] ?? ($response['error'] ?? 'Onbekende fout bij token uitwisseling');
|
|
return [
|
|
'success' => false,
|
|
'error' => $errorMsg,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'access_token' => $response['json']['access_token'],
|
|
'refresh_token' => $response['json']['refresh_token'] ?? null,
|
|
'expires_in' => (int)($response['json']['expires_in'] ?? 3600),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Haalt het Microsoft gebruikersprofiel op via Graph API
|
|
*/
|
|
function microsoft_get_user_profile(string $accessToken): ?array {
|
|
$response = fociz_http_request(MS_GRAPH_BASE_URL . '/me', [
|
|
'method' => 'GET',
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$accessToken}",
|
|
'Accept' => 'application/json',
|
|
],
|
|
'timeout' => 10,
|
|
]);
|
|
|
|
if ($response['status'] === 200 && is_array($response['json'])) {
|
|
return [
|
|
'displayName' => $response['json']['displayName'] ?? 'Microsoft 365 Gebruiker',
|
|
'mail' => $response['json']['mail'] ?? ($response['json']['userPrincipalName'] ?? ''),
|
|
'id' => $response['json']['id'] ?? '',
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Haalt een geldige access_token op voor de gebruiker; vernieuwt automatisch indien verlopen
|
|
*/
|
|
function microsoft_get_valid_token(int $userId): ?string {
|
|
$db = get_db_connection();
|
|
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK' AND is_enabled = 1");
|
|
$stmt->execute([$userId]);
|
|
$integration = $stmt->fetch();
|
|
|
|
if (!$integration || empty($integration['access_token'])) {
|
|
return null;
|
|
}
|
|
|
|
$settings = json_decode($integration['settings'] ?? '{}', true) ?: [];
|
|
$expiresAt = (int)($settings['expires_at'] ?? 0);
|
|
|
|
// Als token nog minstens 4 minuten geldig is, direct retourneren
|
|
if ($expiresAt > (time() + 240)) {
|
|
return $integration['access_token'];
|
|
}
|
|
|
|
// Token is verlopen of verloopt bijna; refresh met refresh_token
|
|
$refreshToken = $integration['refresh_token'] ?? '';
|
|
$appConfig = microsoft_get_app_config();
|
|
$clientId = !empty($settings['client_id']) ? $settings['client_id'] : $appConfig['client_id'];
|
|
$clientSecret = !empty($settings['client_secret']) ? $settings['client_secret'] : $appConfig['client_secret'];
|
|
$tenantId = !empty($settings['tenant_id']) ? $settings['tenant_id'] : $appConfig['tenant_id'];
|
|
|
|
if (empty($refreshToken) || empty($clientId) || empty($clientSecret)) {
|
|
return null;
|
|
}
|
|
|
|
$cleanTenant = (!empty($tenantId) && $tenantId !== 'common') ? $tenantId : 'planningsagenda.nl';
|
|
$tokenUrl = sprintf(MS_OAUTH_TOKEN_URL, urlencode($cleanTenant));
|
|
$payload = [
|
|
'client_id' => $clientId,
|
|
'client_secret' => $clientSecret,
|
|
'grant_type' => 'refresh_token',
|
|
'refresh_token' => $refreshToken,
|
|
'scope' => MS_DEFAULT_SCOPES,
|
|
];
|
|
|
|
$response = fociz_http_request($tokenUrl, [
|
|
'method' => 'POST',
|
|
'body' => $payload,
|
|
'timeout' => 15,
|
|
]);
|
|
|
|
if ($response['status'] === 200 && !empty($response['json']['access_token'])) {
|
|
$newAccessToken = $response['json']['access_token'];
|
|
$newRefreshToken = $response['json']['refresh_token'] ?? $refreshToken;
|
|
$expiresIn = (int)($response['json']['expires_in'] ?? 3600);
|
|
$settings['expires_at'] = time() + $expiresIn;
|
|
|
|
$updateStmt = $db->prepare("
|
|
UPDATE integrations
|
|
SET access_token = ?, refresh_token = ?, settings = ?, last_sync_at = NOW()
|
|
WHERE id = ?
|
|
");
|
|
$updateStmt->execute([$newAccessToken, $newRefreshToken, json_encode($settings), $integration['id']]);
|
|
|
|
return $newAccessToken;
|
|
}
|
|
|
|
error_log("Microsoft token refresh mislukt voor user {$userId}: " . ($response['error'] ?? json_encode($response['json'])));
|
|
// Fallback: indien het huidige token nog aanwezig is, probeer dit toch te gebruiken
|
|
if (!empty($integration['access_token'])) {
|
|
return $integration['access_token'];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Haalt agenda-afspraken van Microsoft Outlook op voor de opgegeven datum
|
|
*/
|
|
function microsoft_fetch_today_events(int $userId, ?string $date = null): array {
|
|
$targetDate = $date ?: date('Y-m-d');
|
|
$token = microsoft_get_valid_token($userId);
|
|
|
|
if (!$token) {
|
|
return [];
|
|
}
|
|
|
|
// Bereken het dagvenster in Europe/Amsterdam en converteer naar UTC ISO 8601 (eindigend op 'Z')
|
|
// Microsoft Graph vereist strikt een ISO 8601 formaat met tijdzone of 'Z' (bijv. 2026-09-09T00:00:00Z)
|
|
try {
|
|
$tzLocal = new DateTimeZone('Europe/Amsterdam');
|
|
$tzUtc = new DateTimeZone('UTC');
|
|
|
|
$dtStart = new DateTime("{$targetDate} 00:00:00", $tzLocal);
|
|
$dtEnd = new DateTime("{$targetDate} 23:59:59", $tzLocal);
|
|
|
|
$startIso = $dtStart->setTimezone($tzUtc)->format('Y-m-d\TH:i:s\Z');
|
|
$endIso = $dtEnd->setTimezone($tzUtc)->format('Y-m-d\TH:i:s\Z');
|
|
} catch (Throwable $e) {
|
|
$startIso = "{$targetDate}T00:00:00Z";
|
|
$endIso = "{$targetDate}T23:59:59Z";
|
|
}
|
|
|
|
$url = MS_GRAPH_BASE_URL . '/me/calendarView?' . http_build_query([
|
|
'startDateTime' => $startIso,
|
|
'endDateTime' => $endIso,
|
|
'$select' => 'id,subject,bodyPreview,start,end,location,isAllDay,webLink,onlineMeeting,isCancelled',
|
|
'$orderby' => 'start/dateTime',
|
|
'$top' => '50',
|
|
]);
|
|
|
|
$response = fociz_http_request($url, [
|
|
'method' => 'GET',
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$token}",
|
|
'Accept' => 'application/json',
|
|
'Prefer' => 'outlook.timezone="Europe/Amsterdam"',
|
|
],
|
|
'timeout' => 12,
|
|
]);
|
|
|
|
if ($response['status'] !== 200) {
|
|
error_log("microsoft_fetch_today_events error (HTTP {$response['status']}): " . ($response['error'] ?? json_encode($response['json'])));
|
|
return [];
|
|
}
|
|
|
|
if (empty($response['json']['value'])) {
|
|
return [];
|
|
}
|
|
|
|
$rawEvents = $response['json']['value'];
|
|
$events = [];
|
|
|
|
foreach ($rawEvents as $e) {
|
|
// Annuleringen overslaan
|
|
if (!empty($e['isCancelled'])) {
|
|
continue;
|
|
}
|
|
|
|
$rawStart = $e['start']['dateTime'] ?? '';
|
|
$rawEnd = $e['end']['dateTime'] ?? '';
|
|
|
|
// Formatteer naar H:i (bijv. 09:30). Omdat we 'Prefer: outlook.timezone="Europe/Amsterdam"'
|
|
// meesturen, levert Graph de tijden al aan in de Nederlandse tijdzone.
|
|
$startTime = '';
|
|
$endTime = '';
|
|
if (!empty($rawStart)) {
|
|
if (preg_match('/T(\d{2}:\d{2})/', $rawStart, $m)) {
|
|
$startTime = $m[1];
|
|
} else {
|
|
$startTime = date('H:i', strtotime($rawStart));
|
|
}
|
|
}
|
|
if (!empty($rawEnd)) {
|
|
if (preg_match('/T(\d{2}:\d{2})/', $rawEnd, $m)) {
|
|
$endTime = $m[1];
|
|
} else {
|
|
$endTime = date('H:i', strtotime($rawEnd));
|
|
}
|
|
}
|
|
|
|
// Teams of meeting URL detecteren
|
|
$meetingUrl = $e['onlineMeeting']['joinUrl'] ?? ($e['webLink'] ?? 'https://outlook.office.com');
|
|
if (empty($meetingUrl) && !empty($e['bodyPreview'])) {
|
|
if (preg_match('#https://teams\.microsoft\.com/l/meetup-join/[^\s"\'<>]+#i', $e['bodyPreview'], $m)) {
|
|
$meetingUrl = $m[0];
|
|
}
|
|
}
|
|
|
|
$locationName = $e['location']['displayName'] ?? '';
|
|
if (empty($locationName) && !empty($e['onlineMeeting']['joinUrl'])) {
|
|
$locationName = 'Microsoft Teams';
|
|
}
|
|
|
|
$events[] = [
|
|
'id' => $e['id'] ?? uniqid('ms_'),
|
|
'subject' => !empty($e['subject']) ? $e['subject'] : '(Geen onderwerp)',
|
|
'start' => $startTime ?: '09:00',
|
|
'end' => $endTime ?: '10:00',
|
|
'start_raw' => $rawStart,
|
|
'end_raw' => $rawEnd,
|
|
'location' => $locationName ?: 'Geen locatie',
|
|
'meeting_url' => $meetingUrl,
|
|
'web_link' => $e['webLink'] ?? '',
|
|
'is_all_day' => !empty($e['isAllDay']),
|
|
'source' => 'OUTLOOK_LIVE',
|
|
];
|
|
}
|
|
|
|
return $events;
|
|
}
|
|
|
|
/**
|
|
* Maakt een nieuwe afspraak aan in de Microsoft Outlook agenda via Graph API
|
|
*
|
|
* @param int $userId
|
|
* @param array $eventData [
|
|
* 'subject' => string,
|
|
* 'description' => ?string,
|
|
* 'date' => ?string (Y-m-d),
|
|
* 'start_time' => ?string (H:i),
|
|
* 'end_time' => ?string (H:i),
|
|
* 'duration_minutes' => ?int,
|
|
* ]
|
|
* @return array ['success' => bool, 'event_id' => ?string, 'web_link' => ?string, 'error' => ?string]
|
|
*/
|
|
function microsoft_create_event(int $userId, array $eventData): array {
|
|
$token = microsoft_get_valid_token($userId);
|
|
if (!$token) {
|
|
return ['success' => false, 'error' => 'Geen geldige Outlook 365 verbinding gevonden'];
|
|
}
|
|
|
|
$subject = trim($eventData['subject'] ?? 'Fociz Dagplanning Taak');
|
|
$description = trim($eventData['description'] ?? 'Gepland via Fociz Dagplanning & Structuur');
|
|
$date = !empty($eventData['date']) ? trim($eventData['date']) : date('Y-m-d');
|
|
$startTime = !empty($eventData['start_time']) ? trim($eventData['start_time']) : '09:00';
|
|
|
|
// Als tijd alleen een uur is (bijv. "9" of "09"), converteer naar "09:00"
|
|
if (preg_match('/^(\d{1,2})$/', $startTime, $m)) {
|
|
$startTime = sprintf('%02d:00', (int)$m[1]);
|
|
}
|
|
|
|
// Bepaal de eindtijd
|
|
$endTime = !empty($eventData['end_time']) ? trim($eventData['end_time']) : '';
|
|
$duration = (int)($eventData['duration_minutes'] ?? 30);
|
|
if ($duration <= 0) $duration = 30;
|
|
|
|
if (empty($endTime)) {
|
|
try {
|
|
$dt = new DateTime("{$date} {$startTime}:00", new DateTimeZone('Europe/Amsterdam'));
|
|
$dt->modify("+{$duration} minutes");
|
|
$endTime = $dt->format('H:i');
|
|
} catch (Throwable $e) {
|
|
$endTime = '10:00';
|
|
}
|
|
}
|
|
|
|
$startDateTimeStr = "{$date}T{$startTime}:00";
|
|
$endDateTimeStr = "{$date}T{$endTime}:00";
|
|
|
|
$payload = [
|
|
'subject' => $subject,
|
|
'body' => [
|
|
'contentType' => 'HTML',
|
|
'content' => nl2br(htmlspecialchars($description)) . '<br><br><small style="color: #64748b;">Aangemaakt via <a href="https://dev.fociz.nl/planner.php">Fociz Dagplanning</a></small>',
|
|
],
|
|
'start' => [
|
|
'dateTime' => $startDateTimeStr,
|
|
'timeZone' => 'Europe/Amsterdam',
|
|
],
|
|
'end' => [
|
|
'dateTime' => $endDateTimeStr,
|
|
'timeZone' => 'Europe/Amsterdam',
|
|
],
|
|
'showAs' => 'busy',
|
|
'isReminderOn' => true,
|
|
'reminderMinutesBeforeStart' => 15,
|
|
];
|
|
|
|
$response = fociz_http_request(MS_GRAPH_BASE_URL . '/me/events', [
|
|
'method' => 'POST',
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$token}",
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
],
|
|
'body' => $payload,
|
|
'timeout' => 15,
|
|
]);
|
|
|
|
if ($response['status'] === 201 && is_array($response['json'])) {
|
|
return [
|
|
'success' => true,
|
|
'event_id' => $response['json']['id'] ?? null,
|
|
'web_link' => $response['json']['webLink'] ?? null,
|
|
'start_time'=> $startTime,
|
|
'end_time' => $endTime,
|
|
];
|
|
}
|
|
|
|
$errMsg = $response['json']['error']['message'] ?? ($response['error'] ?? "Fout bij aanmaken Outlook afspraak (HTTP {$response['status']})");
|
|
error_log("microsoft_create_event fout: {$errMsg}");
|
|
return [
|
|
'success' => false,
|
|
'error' => $errMsg,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Verwijdert een afspraak uit Microsoft Outlook via Graph API
|
|
*/
|
|
function microsoft_delete_event(int $userId, string $eventId): bool {
|
|
if (empty($eventId)) {
|
|
return false;
|
|
}
|
|
|
|
$token = microsoft_get_valid_token($userId);
|
|
if (!$token) {
|
|
return false;
|
|
}
|
|
|
|
$url = MS_GRAPH_BASE_URL . '/me/events/' . urlencode($eventId);
|
|
$response = fociz_http_request($url, [
|
|
'method' => 'DELETE',
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$token}",
|
|
],
|
|
'timeout' => 12,
|
|
]);
|
|
|
|
return in_array($response['status'], [200, 204, 404]);
|
|
}
|
|
|
|
/**
|
|
* Ontkoppelt Microsoft integratie voor een gebruiker
|
|
* Wist uitsluitend de persoonlijke tokens; behoudt de centrale Azure App gegevens indien aanwezig.
|
|
*/
|
|
function microsoft_disconnect(int $userId): bool {
|
|
$db = get_db_connection();
|
|
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
|
$stmt->execute([$userId]);
|
|
$integration = $stmt->fetch();
|
|
|
|
if (!$integration) {
|
|
return true;
|
|
}
|
|
|
|
$settings = json_decode($integration['settings'] ?? '{}', true) ?: [];
|
|
// Verwijder gebruikers-specifieke token gegevens
|
|
unset($settings['account_email'], $settings['account_name'], $settings['expires_at']);
|
|
|
|
// Als er centrale app credentials in staan (bijv. van beheerder), behouden we deze netjes
|
|
if (!empty($settings['client_id']) || !empty($settings['client_secret'])) {
|
|
$updateStmt = $db->prepare("
|
|
UPDATE integrations
|
|
SET access_token = NULL, refresh_token = NULL, settings = ?, last_sync_at = NULL, is_enabled = 1, is_demo_mode = 0
|
|
WHERE id = ?
|
|
");
|
|
return $updateStmt->execute([json_encode($settings), $integration['id']]);
|
|
} else {
|
|
$delStmt = $db->prepare("DELETE FROM integrations WHERE id = ?");
|
|
return $delStmt->execute([$integration['id']]);
|
|
}
|
|
}
|
|
|