feat(planner): add widget layout saving and scratchpad note functionality
feat(notifications): implement real-time live sync for mail, Jira, and calendar updates fix(database): add new columns for widget layout and last check timestamps fix(schema): update users table schema to include new fields for widget layout and last checks feat(microsoft): enhance Microsoft integration with demo email data and inbox fetching feat(jira): add listener for live updates on Jira tickets
This commit is contained in:
+14
-4
@@ -161,8 +161,14 @@ require_once __DIR__ . '/env_badge.php';
|
||||
<!-- Nav Tabs -->
|
||||
<nav class="hidden md:flex items-center space-x-1">
|
||||
<a href="/planner.php" class="flex items-center space-x-2 px-3 py-1.5 rounded-xl text-xs font-semibold transition <?= $currentPage === 'planner.php' ? 'bg-indigo-50 text-indigo-700 border border-indigo-200/70 shadow-2xs dark:bg-indigo-500/10 dark:text-indigo-300 dark:border-indigo-500/20' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100 dark:text-slate-400 dark:hover:text-slate-200 dark:hover:bg-slate-800/60' ?>">
|
||||
<i data-lucide="calendar" class="w-4 h-4"></i>
|
||||
<span>Planning</span>
|
||||
<i data-lucide="layout-dashboard" class="w-4 h-4"></i>
|
||||
<span>Fociz</span>
|
||||
</a>
|
||||
|
||||
<a href="/mail.php" class="flex items-center space-x-2 px-3 py-1.5 rounded-xl text-xs font-semibold transition relative <?= $currentPage === 'mail.php' ? 'bg-indigo-50 text-indigo-700 border border-indigo-200/70 shadow-2xs dark:bg-indigo-500/10 dark:text-indigo-300 dark:border-indigo-500/20' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100 dark:text-slate-400 dark:hover:text-slate-200 dark:hover:bg-slate-800/60' ?>">
|
||||
<i data-lucide="mail" class="w-4 h-4 text-sky-500"></i>
|
||||
<span>Mail</span>
|
||||
<span id="navMailBadge" class="hidden px-1.5 py-0.2 rounded-full text-[10px] font-black bg-sky-600 text-white leading-none"></span>
|
||||
</a>
|
||||
|
||||
<!-- Dropdown: Projecten (Bord, Tijdlijn, Brainstorm, Roadmap) -->
|
||||
@@ -275,8 +281,12 @@ require_once __DIR__ . '/env_badge.php';
|
||||
<!-- Mobile Navigation -->
|
||||
<div class="flex md:hidden items-center justify-around py-2 border-t border-slate-200/80 dark:border-slate-800/60 text-xs">
|
||||
<a href="/planner.php" class="flex flex-col items-center py-1 <?= $currentPage === 'planner.php' ? 'text-indigo-600 dark:text-indigo-400 font-bold' : 'text-slate-500 dark:text-slate-400' ?>">
|
||||
<i data-lucide="calendar" class="w-4 h-4 mb-0.5"></i>
|
||||
<span>Planning</span>
|
||||
<i data-lucide="layout-dashboard" class="w-4 h-4 mb-0.5"></i>
|
||||
<span>Fociz</span>
|
||||
</a>
|
||||
<a href="/mail.php" class="flex flex-col items-center py-1 <?= $currentPage === 'mail.php' ? 'text-indigo-600 dark:text-indigo-400 font-bold' : 'text-slate-500 dark:text-slate-400' ?>">
|
||||
<i data-lucide="mail" class="w-4 h-4 mb-0.5"></i>
|
||||
<span>Mail</span>
|
||||
</a>
|
||||
<a href="/board.php" class="flex flex-col items-center py-1 <?= $currentPage === 'board.php' ? 'text-indigo-600 dark:text-indigo-400 font-bold' : 'text-slate-500 dark:text-slate-400' ?>">
|
||||
<i data-lucide="kanban" class="w-4 h-4 mb-0.5"></i>
|
||||
|
||||
@@ -10,7 +10,7 @@ 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';
|
||||
const MS_DEFAULT_SCOPES = 'offline_access User.Read Calendars.ReadWrite Mail.ReadWrite Mail.Send';
|
||||
|
||||
/**
|
||||
* Haalt de centrale Microsoft App Registratie configuratie op (Client ID, Secret, Tenant ID)
|
||||
@@ -881,3 +881,477 @@ function microsoft_reconcile_tasks_with_outlook(int $userId, string $startDate,
|
||||
'updated' => $updatedCount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* MICROSOFT 365 E-MAIL INTEGRATIE (GRAPH API)
|
||||
* ==============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Levert realistische demo e-mails voor accounts zonder actieve Outlook verbinding
|
||||
*/
|
||||
function microsoft_get_mail_demo_data(): array {
|
||||
$now = time();
|
||||
return [
|
||||
[
|
||||
'id' => 'demo-mail-1',
|
||||
'subject' => '⚡ Urgent: UWV Koppeling REST-endpoints en Datamigratie timing',
|
||||
'body_preview' => 'Beste Niek, we hebben vanuit de overheidsspecificaties aanvullende eisen ontvangen omtrent de UWV REST-endpoints en de validatie van werknemersdossiers...',
|
||||
'body_html' => '<p>Beste Niek,</p><p>We hebben vanuit de overheidsspecificaties aanvullende eisen ontvangen omtrent de <strong>UWV REST-endpoints</strong> en de validatie van werknemersdossiers.</p><p>Zou je kunnen verifiëren of onze SSO-koppeling en de datadump export hier al op aansluiten? Het zou fijn zijn als we dit vanmiddag kort kunnen afstemmen in de planning.</p><p>Met vriendelijke groet,<br><strong>Directie UWV Koppelingen & Integraties</strong></p>',
|
||||
'from_name' => 'Directie UWV Integraties',
|
||||
'from_email' => 'koppelingen@uwv-overheid.nl',
|
||||
'to_recipients' => [['name' => 'Niek Rabelink', 'address' => 'niek@fociz.nl']],
|
||||
'cc_recipients' => [],
|
||||
'received_at' => date('Y-m-d H:i:s', $now - (12 * 60)), // 12 minuten geleden
|
||||
'received_human' => '12 min geleden',
|
||||
'is_read' => false,
|
||||
'has_attachments' => true,
|
||||
'importance' => 'high',
|
||||
'web_link' => 'https://outlook.office.com/mail/inbox',
|
||||
'is_demo' => true,
|
||||
],
|
||||
[
|
||||
'id' => 'demo-mail-2',
|
||||
'subject' => 'Factuur & Urenverantwoording September — Goedkeuring vereist',
|
||||
'body_preview' => 'Hallo team, bijgaand tref je de voorlopige urenstaat en factuurspecificatie aan voor de lopende sprint. Graag voor het einde van de week accorderen...',
|
||||
'body_html' => '<p>Hallo team,</p><p>Bijgaand tref je de voorlopige urenstaat en factuurspecificatie aan voor de lopende sprint. Graag voor het einde van de werkweek accorderen in het systeem.</p><p>Laat het weten indien er correcties nodig zijn op de dagregistraties.</p><p>Hartelijke groet,<br><strong>Financiële Administratie Fociz</strong></p>',
|
||||
'from_name' => 'Financiële Administratie',
|
||||
'from_email' => 'facturatie@fociz.nl',
|
||||
'to_recipients' => [['name' => 'Niek Rabelink', 'address' => 'niek@fociz.nl']],
|
||||
'cc_recipients' => [],
|
||||
'received_at' => date('Y-m-d H:i:s', $now - (65 * 60)), // 1 uur geleden
|
||||
'received_human' => '1 uur geleden',
|
||||
'is_read' => false,
|
||||
'has_attachments' => false,
|
||||
'importance' => 'normal',
|
||||
'web_link' => 'https://outlook.office.com/mail/inbox',
|
||||
'is_demo' => true,
|
||||
],
|
||||
[
|
||||
'id' => 'demo-mail-3',
|
||||
'subject' => 'Jira SSO Migratie & Staging server testomgeving',
|
||||
'body_preview' => 'Goedemiddag, de testomgeving voor de nieuwe Atlassian API migratie (CHANGE-2046) is succesvol klaargezet. We kunnen starten met de verificatietesten...',
|
||||
'body_html' => '<p>Goedemiddag,</p><p>De testomgeving voor de nieuwe Atlassian API migratie (CHANGE-2046) is succesvol klaargezet. We kunnen starten met de verificatietesten op de Servicedesk wachtrijen en DX filters.</p><p>Alle endpoint logs zijn beschikbaar via het beheerpaneel.</p><p>Groeten,<br><strong>Team DevOps & Infrastructuur</strong></p>',
|
||||
'from_name' => 'DevOps Systeembeheer',
|
||||
'from_email' => 'infra@fociz.nl',
|
||||
'to_recipients' => [['name' => 'Niek Rabelink', 'address' => 'niek@fociz.nl']],
|
||||
'cc_recipients' => [],
|
||||
'received_at' => date('Y-m-d H:i:s', $now - (3 * 3600)), // 3 uur geleden
|
||||
'received_human' => '3 uur geleden',
|
||||
'is_read' => true,
|
||||
'has_attachments' => true,
|
||||
'importance' => 'normal',
|
||||
'web_link' => 'https://outlook.office.com/mail/inbox',
|
||||
'is_demo' => true,
|
||||
],
|
||||
[
|
||||
'id' => 'demo-mail-4',
|
||||
'subject' => 'Herinnering: Kwartaal Roadmap Review & Strategie sessie',
|
||||
'body_preview' => 'Beste collega\'s, vergeet niet je prioriteiten en feature requests in te dienen in de Fociz Roadmap module voor de meeting van aanstaande donderdag...',
|
||||
'body_html' => '<p>Beste collega\'s,</p><p>Vergeet niet je prioriteiten en feature requests in te dienen in de <strong>Fociz Roadmap</strong> module voor de meeting van aanstaande donderdag.</p><p>We behandelen dan tevens de nieuwe Kanban borden en de Outlook time-blocking ervaring.</p><p>Tot dan!<br><strong>Product Management</strong></p>',
|
||||
'from_name' => 'Product Management',
|
||||
'from_email' => 'product@fociz.nl',
|
||||
'to_recipients' => [['name' => 'Alle Medewerkers', 'address' => 'team@fociz.nl']],
|
||||
'cc_recipients' => [],
|
||||
'received_at' => date('Y-m-d H:i:s', $now - (24 * 3600)), // Gisteren
|
||||
'received_human' => 'Gisteren',
|
||||
'is_read' => true,
|
||||
'has_attachments' => false,
|
||||
'importance' => 'normal',
|
||||
'web_link' => 'https://outlook.office.com/mail/inbox',
|
||||
'is_demo' => true,
|
||||
],
|
||||
[
|
||||
'id' => 'demo-mail-5',
|
||||
'subject' => 'Welkom bij Fociz Live — Handige tips voor jouw dagplanning',
|
||||
'body_preview' => 'Welkom bij Fociz! Wist je dat je binnengekomen Outlook e-mails en Jira tickets met één klik direct kunt inplannen in jouw dagrooster?...',
|
||||
'body_html' => '<p>Welkom bij Fociz!</p><p>Wist je dat je binnengekomen Outlook e-mails en Jira tickets met één klik direct kunt inplannen in jouw persoonlijke dagplanning?</p><p>Hierdoor houd je maximale grip op je werkdag en behoud je de ultieme focus.</p><p>Veel productiviteit gewenst,<br><strong>Het Fociz Team</strong></p>',
|
||||
'from_name' => 'Fociz Support Team',
|
||||
'from_email' => 'no-reply@fociz.nl',
|
||||
'to_recipients' => [['name' => 'Niek Rabelink', 'address' => 'niek@fociz.nl']],
|
||||
'cc_recipients' => [],
|
||||
'received_at' => date('Y-m-d H:i:s', $now - (48 * 3600)), // 2 dagen geleden
|
||||
'received_human' => '2 dagen geleden',
|
||||
'is_read' => true,
|
||||
'has_attachments' => false,
|
||||
'importance' => 'low',
|
||||
'web_link' => 'https://outlook.office.com/mail/inbox',
|
||||
'is_demo' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haalt inbox berichten op van Microsoft Graph API (of retourneert demo data)
|
||||
*/
|
||||
function microsoft_fetch_inbox_messages(int $userId, int $limit = 25, bool $unreadOnly = false): array {
|
||||
$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();
|
||||
|
||||
$isLive = !empty($integration['access_token']) && empty($integration['is_demo_mode']);
|
||||
if (!$isLive) {
|
||||
$demo = microsoft_get_mail_demo_data();
|
||||
if ($unreadOnly) {
|
||||
$demo = array_values(array_filter($demo, function($m) { return empty($m['is_read']); }));
|
||||
}
|
||||
return [
|
||||
'success' => true,
|
||||
'is_demo' => true,
|
||||
'messages' => array_slice($demo, 0, $limit),
|
||||
'unread_count' => count(array_filter($demo, function($m) { return empty($m['is_read']); })),
|
||||
'total_count' => count($demo),
|
||||
];
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Geen geldig Microsoft 365 token beschikbaar.',
|
||||
'messages' => [],
|
||||
'unread_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$queryParams = [
|
||||
'$top' => min(50, max(1, $limit)),
|
||||
'$select' => 'id,subject,bodyPreview,from,toRecipients,receivedDateTime,isRead,hasAttachments,importance,webLink',
|
||||
'$orderby' => 'receivedDateTime desc',
|
||||
];
|
||||
|
||||
if ($unreadOnly) {
|
||||
$queryParams['$filter'] = 'isRead eq false';
|
||||
}
|
||||
|
||||
$url = MS_GRAPH_BASE_URL . '/me/mailFolders/inbox/messages?' . http_build_query($queryParams);
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Accept' => 'application/json',
|
||||
'Prefer' => 'outlook.body-content-type="text"',
|
||||
],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
if ($res['status'] !== 200 || !isset($res['json']['value'])) {
|
||||
error_log("Microsoft fetch inbox error: HTTP {$res['status']} - " . ($res['error'] ?? json_encode($res['json'])));
|
||||
// Val terug op demo data indien API fout optreedt
|
||||
$demo = microsoft_get_mail_demo_data();
|
||||
return [
|
||||
'success' => true,
|
||||
'is_demo' => true,
|
||||
'messages' => array_slice($demo, 0, $limit),
|
||||
'unread_count' => count(array_filter($demo, function($m) { return empty($m['is_read']); })),
|
||||
'total_count' => count($demo),
|
||||
'warning' => 'Kon live e-mails niet ophalen van Microsoft Graph. Demomodus actief.',
|
||||
];
|
||||
}
|
||||
|
||||
$tzAmsterdam = new DateTimeZone('Europe/Amsterdam');
|
||||
$messages = [];
|
||||
|
||||
foreach ($res['json']['value'] as $msg) {
|
||||
$fromObj = $msg['from']['emailAddress'] ?? [];
|
||||
$fromName = $fromObj['name'] ?? ($fromObj['address'] ?? 'Onbekende afzender');
|
||||
$fromEmail = $fromObj['address'] ?? '';
|
||||
|
||||
$receivedIso = $msg['receivedDateTime'] ?? '';
|
||||
$receivedFormatted = '';
|
||||
$receivedHuman = '';
|
||||
|
||||
if (!empty($receivedIso)) {
|
||||
try {
|
||||
$dt = new DateTime($receivedIso);
|
||||
$dt->setTimezone($tzAmsterdam);
|
||||
$receivedFormatted = $dt->format('Y-m-d H:i:s');
|
||||
|
||||
$todayStr = date('Y-m-d');
|
||||
$msgDate = $dt->format('Y-m-d');
|
||||
if ($msgDate === $todayStr) {
|
||||
$receivedHuman = $dt->format('H:i');
|
||||
} elseif ($msgDate === date('Y-m-d', strtotime('-1 day'))) {
|
||||
$receivedHuman = 'Gisteren ' . $dt->format('H:i');
|
||||
} else {
|
||||
$receivedHuman = $dt->format('d-m H:i');
|
||||
}
|
||||
} catch (Throwable $t) {
|
||||
$receivedFormatted = $receivedIso;
|
||||
$receivedHuman = substr($receivedIso, 0, 10);
|
||||
}
|
||||
}
|
||||
|
||||
$messages[] = [
|
||||
'id' => $msg['id'] ?? '',
|
||||
'subject' => $msg['subject'] ?: '(Geen onderwerp)',
|
||||
'body_preview' => $msg['bodyPreview'] ?? '',
|
||||
'from_name' => $fromName,
|
||||
'from_email' => $fromEmail,
|
||||
'to_recipients' => array_map(function($r) {
|
||||
return [
|
||||
'name' => $r['emailAddress']['name'] ?? '',
|
||||
'address' => $r['emailAddress']['address'] ?? '',
|
||||
];
|
||||
}, $msg['toRecipients'] ?? []),
|
||||
'received_at' => $receivedFormatted,
|
||||
'received_human' => $receivedHuman,
|
||||
'is_read' => !empty($msg['isRead']),
|
||||
'has_attachments' => !empty($msg['hasAttachments']),
|
||||
'importance' => strtolower($msg['importance'] ?? 'normal'),
|
||||
'web_link' => $msg['webLink'] ?? 'https://outlook.office.com/mail/',
|
||||
'is_demo' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$unreadCount = count(array_filter($messages, function($m) { return empty($m['is_read']); }));
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'is_demo' => false,
|
||||
'messages' => $messages,
|
||||
'unread_count' => $unreadCount,
|
||||
'total_count' => count($messages),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haalt de volledige inhoud en details van een enkel e-mailbericht op
|
||||
*/
|
||||
function microsoft_fetch_message_detail(int $userId, string $messageId): ?array {
|
||||
// Check op demo ID
|
||||
if (strpos($messageId, 'demo-mail-') === 0) {
|
||||
$demo = microsoft_get_mail_demo_data();
|
||||
foreach ($demo as $m) {
|
||||
if ($m['id'] === $messageId) {
|
||||
$m['is_read'] = true;
|
||||
return $m;
|
||||
}
|
||||
}
|
||||
return $demo[0] ?? null;
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$url = MS_GRAPH_BASE_URL . '/me/messages/' . urlencode($messageId) . '?$select=id,subject,body,bodyPreview,from,toRecipients,ccRecipients,receivedDateTime,isRead,hasAttachments,importance,webLink';
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
if ($res['status'] !== 200 || !isset($res['json']['id'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$msg = $res['json'];
|
||||
$tzAmsterdam = new DateTimeZone('Europe/Amsterdam');
|
||||
$receivedFormatted = '';
|
||||
$receivedHuman = '';
|
||||
|
||||
if (!empty($msg['receivedDateTime'])) {
|
||||
try {
|
||||
$dt = new DateTime($msg['receivedDateTime']);
|
||||
$dt->setTimezone($tzAmsterdam);
|
||||
$receivedFormatted = $dt->format('Y-m-d H:i:s');
|
||||
$receivedHuman = $dt->format('d-m-Y H:i');
|
||||
} catch (Throwable $t) {
|
||||
$receivedFormatted = $msg['receivedDateTime'];
|
||||
$receivedHuman = $msg['receivedDateTime'];
|
||||
}
|
||||
}
|
||||
|
||||
$bodyType = strtolower($msg['body']['contentType'] ?? 'text');
|
||||
$bodyContent = $msg['body']['content'] ?? '';
|
||||
|
||||
// Markeer direct als gelezen via Graph API
|
||||
if (empty($msg['isRead'])) {
|
||||
microsoft_mark_message_read($userId, $messageId, true);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $msg['id'],
|
||||
'subject' => $msg['subject'] ?: '(Geen onderwerp)',
|
||||
'body_preview' => $msg['bodyPreview'] ?? '',
|
||||
'body_html' => ($bodyType === 'html') ? $bodyContent : nl2br(htmlspecialchars($bodyContent)),
|
||||
'body_text' => ($bodyType === 'text') ? $bodyContent : strip_tags($bodyContent),
|
||||
'from_name' => $msg['from']['emailAddress']['name'] ?? ($msg['from']['emailAddress']['address'] ?? 'Onbekend'),
|
||||
'from_email' => $msg['from']['emailAddress']['address'] ?? '',
|
||||
'to_recipients' => array_map(function($r) {
|
||||
return [
|
||||
'name' => $r['emailAddress']['name'] ?? '',
|
||||
'address' => $r['emailAddress']['address'] ?? '',
|
||||
];
|
||||
}, $msg['toRecipients'] ?? []),
|
||||
'cc_recipients' => array_map(function($r) {
|
||||
return [
|
||||
'name' => $r['emailAddress']['name'] ?? '',
|
||||
'address' => $r['emailAddress']['address'] ?? '',
|
||||
];
|
||||
}, $msg['ccRecipients'] ?? []),
|
||||
'received_at' => $receivedFormatted,
|
||||
'received_human' => $receivedHuman,
|
||||
'is_read' => true,
|
||||
'has_attachments' => !empty($msg['hasAttachments']),
|
||||
'importance' => strtolower($msg['importance'] ?? 'normal'),
|
||||
'web_link' => $msg['webLink'] ?? 'https://outlook.office.com/mail/',
|
||||
'is_demo' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Markeert een e-mailbericht als gelezen of ongelezen
|
||||
*/
|
||||
function microsoft_mark_message_read(int $userId, string $messageId, bool $isRead = true): bool {
|
||||
if (strpos($messageId, 'demo-mail-') === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) return false;
|
||||
|
||||
$url = MS_GRAPH_BASE_URL . '/me/messages/' . urlencode($messageId);
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'PATCH',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => ['isRead' => $isRead],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
return ($res['status'] >= 200 && $res['status'] < 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* Beantwoordt een e-mailbericht via Microsoft Graph API
|
||||
*/
|
||||
function microsoft_reply_message(int $userId, string $messageId, string $comment, bool $replyAll = false): array {
|
||||
if (strpos($messageId, 'demo-mail-') === 0) {
|
||||
return ['success' => true, 'message' => 'Demo antwoord succesvol gesimuleerd.'];
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) {
|
||||
return ['success' => false, 'error' => 'Geen geldige Microsoft verbinding.'];
|
||||
}
|
||||
|
||||
$action = $replyAll ? 'replyAll' : 'reply';
|
||||
$url = MS_GRAPH_BASE_URL . '/me/messages/' . urlencode($messageId) . '/' . $action;
|
||||
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'POST',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => [
|
||||
'comment' => $comment,
|
||||
],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
if ($res['status'] >= 200 && $res['status'] < 300) {
|
||||
return ['success' => true, 'message' => 'Antwoord succesvol verzonden via Outlook.'];
|
||||
}
|
||||
|
||||
$errMsg = $res['json']['error']['message'] ?? ($res['error'] ?? 'Fout bij verzenden van antwoord.');
|
||||
return ['success' => false, 'error' => $errMsg];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stuurt een e-mailbericht door via Microsoft Graph API
|
||||
*/
|
||||
function microsoft_forward_message(int $userId, string $messageId, array $toRecipients, string $comment = ''): array {
|
||||
if (strpos($messageId, 'demo-mail-') === 0) {
|
||||
return ['success' => true, 'message' => 'Demo doorsturen succesvol gesimuleerd.'];
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) {
|
||||
return ['success' => false, 'error' => 'Geen geldige Microsoft verbinding.'];
|
||||
}
|
||||
|
||||
$recipientsPayload = [];
|
||||
foreach ($toRecipients as $r) {
|
||||
$addr = is_array($r) ? ($r['address'] ?? '') : trim($r);
|
||||
$name = is_array($r) ? ($r['name'] ?? $addr) : $addr;
|
||||
if (!empty($addr)) {
|
||||
$recipientsPayload[] = [
|
||||
'emailAddress' => [
|
||||
'address' => $addr,
|
||||
'name' => $name,
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($recipientsPayload)) {
|
||||
return ['success' => false, 'error' => 'Geen geldige ontvangers opgegeven om door te sturen.'];
|
||||
}
|
||||
|
||||
$url = MS_GRAPH_BASE_URL . '/me/messages/' . urlencode($messageId) . '/forward';
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'POST',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => [
|
||||
'toRecipients' => $recipientsPayload,
|
||||
'comment' => $comment,
|
||||
],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
if ($res['status'] >= 200 && $res['status'] < 300) {
|
||||
return ['success' => true, 'message' => 'E-mail succesvol doorgestuurd via Outlook.'];
|
||||
}
|
||||
|
||||
$errMsg = $res['json']['error']['message'] ?? ($res['error'] ?? 'Fout bij doorsturen van e-mail.');
|
||||
return ['success' => false, 'error' => $errMsg];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haalt het aantal ongelezen inbox e-mails op
|
||||
*/
|
||||
function microsoft_get_unread_mail_count(int $userId): int {
|
||||
$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']) || !empty($integration['is_demo_mode'])) {
|
||||
$demo = microsoft_get_mail_demo_data();
|
||||
return count(array_filter($demo, function($m) { return empty($m['is_read']); }));
|
||||
}
|
||||
|
||||
$token = microsoft_get_valid_token($userId);
|
||||
if (!$token) return 0;
|
||||
|
||||
$url = MS_GRAPH_BASE_URL . '/me/mailFolders/inbox?$select=unreadItemCount';
|
||||
$res = fociz_http_request($url, [
|
||||
'method' => 'GET',
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$token}",
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
if ($res['status'] === 200 && isset($res['json']['unreadItemCount'])) {
|
||||
return (int)$res['json']['unreadItemCount'];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user