Implementeer live Microsoft 365 Outlook en Atlassian Jira integraties met agendasync en taakimport
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
/**
|
||||
* Atlassian Jira Cloud REST API Integratie voor Fociz
|
||||
* Beheert verbindingstest, ophalen van aan de gebruiker toegewezen issues en import naar dagtaken.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../http.php';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
|
||||
/**
|
||||
* Normaliseert het Jira domein naar een schone URL zonder slash aan het einde
|
||||
*/
|
||||
function jira_normalize_domain(string $domain): string {
|
||||
$domain = trim($domain);
|
||||
if (!preg_match('#^https?://#i', $domain)) {
|
||||
$domain = 'https://' . $domain;
|
||||
}
|
||||
return rtrim($domain, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Genereert de Basic Authentication header voor Atlassian REST API
|
||||
*/
|
||||
function jira_get_auth_header(string $email, string $apiToken): string {
|
||||
return 'Basic ' . base64_encode(trim($email) . ':' . trim($apiToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test de verbinding met Atlassian Jira Cloud via /rest/api/3/myself
|
||||
*/
|
||||
function jira_test_connection(string $domain, string $email, string $apiToken): array {
|
||||
$cleanDomain = jira_normalize_domain($domain);
|
||||
if (empty($cleanDomain) || empty($email) || empty($apiToken)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Vul een Jira domein, e-mailadres en Atlassian API token in.',
|
||||
];
|
||||
}
|
||||
|
||||
$authHeader = jira_get_auth_header($email, $apiToken);
|
||||
$url = $cleanDomain . '/rest/api/3/myself';
|
||||
|
||||
$response = fociz_http_request($url, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
// Fallback naar api v2 indien v3 niet beschikbaar is
|
||||
if ($response['status'] === 404) {
|
||||
$urlV2 = $cleanDomain . '/rest/api/2/myself';
|
||||
$response = fociz_http_request($urlV2, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($response['status'] === 200 && is_array($response['json'])) {
|
||||
$user = $response['json'];
|
||||
return [
|
||||
'success' => true,
|
||||
'user' => [
|
||||
'displayName' => $user['displayName'] ?? 'Atlassian Gebruiker',
|
||||
'emailAddress' => $user['emailAddress'] ?? $email,
|
||||
'accountId' => $user['accountId'] ?? ($user['key'] ?? ''),
|
||||
'active' => $user['active'] ?? true,
|
||||
'timeZone' => $user['timeZone'] ?? 'Europe/Amsterdam',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$errMsg = 'Verbinding mislukt met Jira.';
|
||||
if ($response['status'] === 401) {
|
||||
$errMsg = 'Authenticatie geweigerd (HTTP 401). Controleer uw e-mailadres en zorg dat u een geldig Atlassian API token gebruikt.';
|
||||
} elseif ($response['status'] === 403) {
|
||||
$errMsg = 'Toegang geweigerd (HTTP 403). Uw account heeft onvoldoende rechten op dit Jira domein.';
|
||||
} elseif ($response['status'] === 404) {
|
||||
$errMsg = 'Jira Cloud domein niet gevonden (HTTP 404). Controleer het ingevoerde domein (bijv. uwbedrijf.atlassian.net).';
|
||||
} elseif (!empty($response['error'])) {
|
||||
$errMsg = "Netwerkfout: {$response['error']}";
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $errMsg,
|
||||
'status' => $response['status'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haalt alle actieve issues op die zijn toegewezen aan de ingelogde Jira gebruiker
|
||||
*/
|
||||
function jira_fetch_assigned_issues(string $domain, string $email, string $apiToken, int $maxResults = 50): array {
|
||||
$cleanDomain = jira_normalize_domain($domain);
|
||||
$authHeader = jira_get_auth_header($email, $apiToken);
|
||||
|
||||
// JQL: Alle openstaande taken toegewezen aan mij, gesorteerd op recent bijgewerkt
|
||||
$jql = 'assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC';
|
||||
|
||||
$params = [
|
||||
'jql' => $jql,
|
||||
'maxResults' => min($maxResults, 100),
|
||||
'fields' => 'summary,status,priority,issuetype,parent,project,duedate,updated',
|
||||
];
|
||||
|
||||
$url = $cleanDomain . '/rest/api/3/search?' . http_build_query($params);
|
||||
|
||||
$response = fociz_http_request($url, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 12,
|
||||
]);
|
||||
|
||||
// Fallback naar api v2
|
||||
if ($response['status'] === 404) {
|
||||
$urlV2 = $cleanDomain . '/rest/api/2/search?' . http_build_query($params);
|
||||
$response = fociz_http_request($urlV2, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 12,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($response['status'] !== 200 || !isset($response['json']['issues'])) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $response['json']['errorMessages'][0] ?? ($response['error'] ?? 'Kan Jira tickets niet ophalen'),
|
||||
'issues' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$rawIssues = $response['json']['issues'];
|
||||
$issues = [];
|
||||
|
||||
foreach ($rawIssues as $item) {
|
||||
$fields = $item['fields'] ?? [];
|
||||
$key = $item['key'] ?? '';
|
||||
$summary = $fields['summary'] ?? '(Geen titel)';
|
||||
$statusName = $fields['status']['name'] ?? 'To Do';
|
||||
$priorityName = $fields['priority']['name'] ?? 'Medium';
|
||||
$issueTypeName = $fields['issuetype']['name'] ?? 'Taak';
|
||||
$projectName = $fields['project']['name'] ?? 'Project';
|
||||
$dueDate = $fields['duedate'] ?? null;
|
||||
|
||||
$issues[] = [
|
||||
'key' => $key,
|
||||
'summary' => $summary,
|
||||
'status' => $statusName,
|
||||
'priority' => $priorityName,
|
||||
'issue_type' => $issueTypeName,
|
||||
'project' => $projectName,
|
||||
'due_date' => $dueDate,
|
||||
'url' => "{$cleanDomain}/browse/{$key}",
|
||||
'source' => 'JIRA_LIVE',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'issues' => $issues,
|
||||
'total' => count($issues),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haalt de Jira tickets op voor een Fociz gebruiker
|
||||
* Schakelt naadloos tussen live Jira tickets en sandbox demotickets
|
||||
*/
|
||||
function jira_get_user_issues(int $userId, bool $forceRefresh = false): array {
|
||||
$db = get_db_connection();
|
||||
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'JIRA' AND is_enabled = 1");
|
||||
$stmt->execute([$userId]);
|
||||
$integration = $stmt->fetch();
|
||||
|
||||
// Als de integratie niet is ingesteld of in demo mode staat, geef interactieve fallback issues terug
|
||||
if (!$integration || !empty($integration['is_demo_mode'])) {
|
||||
return [
|
||||
'is_demo' => true,
|
||||
'success' => true,
|
||||
'issues' => [
|
||||
[
|
||||
'key' => 'FOC-101',
|
||||
'summary' => 'Tweestapsverificatie (2FA TOTP) flow implementeren',
|
||||
'status' => 'In Progress',
|
||||
'priority' => 'High',
|
||||
'issue_type' => 'Story',
|
||||
'project' => 'Fociz Platform',
|
||||
'due_date' => date('Y-m-d', strtotime('+2 days')),
|
||||
'url' => '#',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
[
|
||||
'key' => 'FOC-102',
|
||||
'summary' => 'Interactief whiteboard canvas voor brainstormsessies',
|
||||
'status' => 'To Do',
|
||||
'priority' => 'High',
|
||||
'issue_type' => 'Feature',
|
||||
'project' => 'Fociz Platform',
|
||||
'due_date' => date('Y-m-d', strtotime('+4 days')),
|
||||
'url' => '#',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
[
|
||||
'key' => 'FOC-103',
|
||||
'summary' => 'Koppeling met Microsoft Outlook Calendar & Teams Graph API',
|
||||
'status' => 'In Review',
|
||||
'priority' => 'Medium',
|
||||
'issue_type' => 'Task',
|
||||
'project' => 'Integrations Hub',
|
||||
'due_date' => date('Y-m-d'),
|
||||
'url' => '#',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
[
|
||||
'key' => 'FOC-104',
|
||||
'summary' => 'Roadmap kwartaalweergave en voortgangsindicatoren',
|
||||
'status' => 'To Do',
|
||||
'priority' => 'Medium',
|
||||
'issue_type' => 'Story',
|
||||
'project' => 'Roadmap Module',
|
||||
'due_date' => date('Y-m-d', strtotime('+7 days')),
|
||||
'url' => '#',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$settings = json_decode($integration['settings'] ?? '{}', true) ?: [];
|
||||
$domain = $settings['jira_domain'] ?? fociz_env('JIRA_DOMAIN', '');
|
||||
$email = $settings['jira_email'] ?? fociz_env('JIRA_EMAIL', '');
|
||||
$apiToken = $settings['jira_api_token'] ?? fociz_env('JIRA_API_TOKEN', '');
|
||||
|
||||
if (empty($domain) || empty($email) || empty($apiToken)) {
|
||||
return [
|
||||
'is_demo' => false,
|
||||
'success' => false,
|
||||
'error' => 'Jira configuratie is onvolledig.',
|
||||
'issues' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// Sessiecache voor 90 seconden om onnodige Jira API calls bij snelle pagina refreshes te beperken
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
@session_start();
|
||||
}
|
||||
$cacheKey = "jira_issues_user_{$userId}";
|
||||
$cached = $_SESSION[$cacheKey] ?? null;
|
||||
|
||||
if (!$forceRefresh && $cached && is_array($cached) && ($cached['timestamp'] ?? 0) > (time() - 90)) {
|
||||
return [
|
||||
'is_demo' => false,
|
||||
'success' => true,
|
||||
'issues' => $cached['issues'],
|
||||
'from_cache' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$result = jira_fetch_assigned_issues($domain, $email, $apiToken, 50);
|
||||
|
||||
if ($result['success']) {
|
||||
$_SESSION[$cacheKey] = [
|
||||
'timestamp' => time(),
|
||||
'issues' => $result['issues'],
|
||||
];
|
||||
|
||||
// Werk last_sync_at bij
|
||||
$stmtUpdate = $db->prepare("UPDATE integrations SET last_sync_at = NOW() WHERE id = ?");
|
||||
$stmtUpdate->execute([$integration['id']]);
|
||||
|
||||
return [
|
||||
'is_demo' => false,
|
||||
'success' => true,
|
||||
'issues' => $result['issues'],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'is_demo' => false,
|
||||
'success' => false,
|
||||
'error' => $result['error'],
|
||||
'issues' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ontkoppelt Jira integratie voor een gebruiker
|
||||
*/
|
||||
function jira_disconnect(int $userId): bool {
|
||||
$db = get_db_connection();
|
||||
$stmt = $db->prepare("DELETE FROM integrations WHERE user_id = ? AND provider = 'JIRA'");
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
@session_start();
|
||||
}
|
||||
unset($_SESSION["jira_issues_user_{$userId}"]);
|
||||
return $stmt->execute([$userId]);
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?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 openid profile email User.Read Calendars.Read';
|
||||
|
||||
/**
|
||||
* Genereert de Microsoft OAuth2 login URL
|
||||
*/
|
||||
function microsoft_get_auth_url(string $clientId, string $redirectUri, string $tenantId = 'common', string $state = ''): string {
|
||||
$tenant = !empty($tenantId) ? trim($tenantId) : 'common';
|
||||
$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 = 'common'): array {
|
||||
$tenant = !empty($tenantId) ? trim($tenantId) : 'common';
|
||||
$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'] ?? '';
|
||||
$clientId = $settings['client_id'] ?? fociz_env('MICROSOFT_CLIENT_ID', '');
|
||||
$clientSecret = $settings['client_secret'] ?? fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
||||
$tenantId = $settings['tenant_id'] ?? fociz_env('MICROSOFT_TENANT_ID', 'common');
|
||||
|
||||
if (empty($refreshToken) || empty($clientId) || empty($clientSecret)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokenUrl = sprintf(MS_OAUTH_TOKEN_URL, urlencode(!empty($tenantId) ? $tenantId : 'common'));
|
||||
$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'])));
|
||||
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 [];
|
||||
}
|
||||
|
||||
// Venster van 00:00:00 tot 23:59:59 lokale tijd
|
||||
$startIso = "{$targetDate}T00:00:00";
|
||||
$endIso = "{$targetDate}T23:59:59";
|
||||
|
||||
$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 || 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)
|
||||
$startTime = '';
|
||||
$endTime = '';
|
||||
if (!empty($rawStart)) {
|
||||
$startTime = date('H:i', strtotime($rawStart));
|
||||
}
|
||||
if (!empty($rawEnd)) {
|
||||
$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,
|
||||
'is_all_day' => !empty($e['isAllDay']),
|
||||
'source' => 'OUTLOOK_LIVE',
|
||||
];
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ontkoppelt Microsoft integratie voor een gebruiker
|
||||
*/
|
||||
function microsoft_disconnect(int $userId): bool {
|
||||
$db = get_db_connection();
|
||||
$stmt = $db->prepare("DELETE FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||
return $stmt->execute([$userId]);
|
||||
}
|
||||
Reference in New Issue
Block a user