diff --git a/includes/header.php b/includes/header.php index 8ff2e5e..6fd22bd 100644 --- a/includes/header.php +++ b/includes/header.php @@ -7,6 +7,11 @@ require_once __DIR__ . '/auth.php'; $currentUser = $currentUser ?? require_auth(); $currentPage = basename($_SERVER['PHP_SELF']); $currentUserTheme = !empty($currentUser['id']) ? get_user_theme((int)$currentUser['id']) : 'dark'; + +// Geef direct het PHP sessieslot vrij voor getoonde pagina's zodat parallelle requests en navigatie nooit blokkeren +if (session_status() === PHP_SESSION_ACTIVE && ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET') { + session_write_close(); +} ?> diff --git a/includes/integrations/jira.php b/includes/integrations/jira.php index 5dfefd5..c0e421b 100644 --- a/includes/integrations/jira.php +++ b/includes/integrations/jira.php @@ -401,7 +401,9 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT return strcasecmp($a['name'], $b['name']); }); - fociz_cache_set($cacheKey, $statuses, 3600); + fociz_cache_set($cacheKey, $statuses, 86400); // 24 uur cache + } else { + fociz_cache_set($cacheKey, [], 1800); // 30 min negatieve cache } return $statuses; @@ -1195,7 +1197,7 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array return array_merge($cachedData, ['from_cache' => true]); } if (session_status() === PHP_SESSION_ACTIVE && !empty($_SESSION[$cacheKey]['data'])) { - if (($_SESSION[$cacheKey]['timestamp'] ?? 0) > (time() - 60)) { + if (($_SESSION[$cacheKey]['timestamp'] ?? 0) > (time() - 180)) { return array_merge($_SESSION[$cacheKey]['data'], ['from_cache' => true]); } } @@ -1230,8 +1232,8 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array $targetProjKeys = array_fill_keys(['SD', 'IN', 'AAN', 'GEN'], true); } - // Ophalen van teamtaken binnen de relevante projecten (met paginatie tot max 500) - $teamRes = jira_fetch_team_issues($domain, $email, $apiToken, array_keys($targetProjKeys), 500); + // Ophalen van teamtaken binnen de relevante projecten (met paginatie tot max 200) + $teamRes = jira_fetch_team_issues($domain, $email, $apiToken, array_keys($targetProjKeys), 200); // Ophalen van Jira statussen $availableStatuses = jira_fetch_statuses($domain, $email, $apiToken); @@ -1281,7 +1283,7 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array 'project_statuses' => $projectStatusesMap, ]; - fociz_cache_set($cacheKey, $data, 60); + fociz_cache_set($cacheKey, $data, 180); // 3 minuten cache if (session_status() === PHP_SESSION_ACTIVE) { $_SESSION[$cacheKey] = [ diff --git a/includes/integrations/microsoft.php b/includes/integrations/microsoft.php index f02dee3..4b9df3b 100644 --- a/includes/integrations/microsoft.php +++ b/includes/integrations/microsoft.php @@ -1187,7 +1187,7 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): $cacheKey = "ms_folders_{$userId}"; if (!$forceRefresh) { $cached = fociz_cache_get($cacheKey); - if ($cached !== null && is_array($cached)) { + if ($cached !== null && is_array($cached) && !empty($cached['success'])) { return $cached; } } @@ -1206,6 +1206,8 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 2, 'total_count' => 5, 'is_inbox' => true, + 'parent_id' => null, + 'is_child' => false, ], [ 'id' => 'demo-folder-dx', @@ -1213,6 +1215,8 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 1, 'total_count' => 3, 'is_inbox' => false, + 'parent_id' => null, + 'is_child' => false, ], [ 'id' => 'demo-folder-klanten', @@ -1220,6 +1224,8 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 0, 'total_count' => 2, 'is_inbox' => false, + 'parent_id' => null, + 'is_child' => false, ], [ 'id' => 'demo-folder-fin', @@ -1227,6 +1233,8 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 1, 'total_count' => 2, 'is_inbox' => false, + 'parent_id' => null, + 'is_child' => false, ], ]; $totalUnread = array_sum(array_column($demoFolders, 'unread_count')); @@ -1243,12 +1251,23 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): return [ 'success' => false, 'error' => 'Geen geldig Microsoft 365 token beschikbaar.', - 'folders' => [], + 'folders' => [ + [ + 'id' => 'inbox', + 'name' => 'Postvak IN', + 'unread_count' => 0, + 'total_count' => 0, + 'is_inbox' => true, + 'parent_id' => null, + 'is_child' => false, + ] + ], 'total_unread' => 0, ]; } - $url = MS_GRAPH_BASE_URL . '/me/mailFolders?$top=100&$select=id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount,wellKnownName'; + // 1. Haal de hoofdmappen op ZONDER restrictief $select (voorkomt 400 Bad Request op wellKnownName) + $url = MS_GRAPH_BASE_URL . '/me/mailFolders?$top=100'; $res = fociz_http_request($url, [ 'method' => 'GET', 'headers' => [ @@ -1271,73 +1290,117 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 0, 'total_count' => 0, 'is_inbox' => true, + 'parent_id' => null, + 'is_child' => false, ] ], 'total_unread' => 0, ]; } - $rawFolders = $res['json']['value']; + $rawFolders = is_array($res['json']['value']) ? $res['json']['value'] : []; - // Haal eventuele submappen op voor mappen met childFolderCount > 0 - foreach ($res['json']['value'] as $f) { - if (!empty($f['childFolderCount']) && (int)$f['childFolderCount'] > 0 && !empty($f['id'])) { - $childUrl = MS_GRAPH_BASE_URL . '/me/mailFolders/' . urlencode($f['id']) . '/childFolders?$top=50&$select=id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount,wellKnownName'; - $cRes = fociz_http_request($childUrl, [ - 'method' => 'GET', - 'headers' => [ - 'Authorization' => "Bearer {$token}", - 'Accept' => 'application/json', - ], - 'timeout' => 10, - ]); - if ($cRes['status'] === 200 && !empty($cRes['json']['value'])) { - foreach ($cRes['json']['value'] as $child) { - $rawFolders[] = $child; + // 2. Haal ALTIJD expliciet de submappen van Postvak IN op + // (In Outlook bevinden 95% van de door gebruikers aangemaakte mappen zich direct onder Inbox) + $inboxChildUrl = MS_GRAPH_BASE_URL . '/me/mailFolders/inbox/childFolders?$top=100'; + $inboxChildRes = fociz_http_request($inboxChildUrl, [ + 'method' => 'GET', + 'headers' => [ + 'Authorization' => "Bearer {$token}", + 'Accept' => 'application/json', + ], + 'timeout' => 8, + ]); + if ($inboxChildRes['status'] === 200 && !empty($inboxChildRes['json']['value']) && is_array($inboxChildRes['json']['value'])) { + foreach ($inboxChildRes['json']['value'] as $child) { + $rawFolders[] = $child; + // Als een eigen submap onder Inbox zelf ook weer submappen heeft: haal die ook op + $cfc = (int)($child['childFolderCount'] ?? 0); + $cId = $child['id'] ?? ''; + if ($cfc > 0 && !empty($cId)) { + $subRes = fociz_http_request(MS_GRAPH_BASE_URL . '/me/mailFolders/' . rawurlencode($cId) . '/childFolders?$top=100', [ + 'method' => 'GET', + 'headers' => [ + 'Authorization' => "Bearer {$token}", + 'Accept' => 'application/json', + ], + 'timeout' => 6, + ]); + if ($subRes['status'] === 200 && !empty($subRes['json']['value']) && is_array($subRes['json']['value'])) { + foreach ($subRes['json']['value'] as $subChild) { + $rawFolders[] = $subChild; + } } } } } - // Uitsluitingen conform gebruikerswens: SPAM / Ongewenst, Prullenbak / Deleted, Outbox en Drafts - $excludedWellKnown = ['junkemail', 'deleteditems', 'outbox', 'drafts', 'conflicts', 'localfailures', 'serverfailures', 'syncissues']; - $excludedTerms = ['junk', 'spam', 'ongewenst', 'ongewenste e-mail', 'ongewenste email', 'deleted items', 'verwijderde items', 'prullenbak', 'trash', 'verwijderd', 'outbox', 'postvak uit', 'drafts', 'concepten']; + // 4. Uitsluitingen conform gebruikerswens: + // "de SPAM , Ongewenste email en Prullenbak hoeven er niet in." + // Ook Outbox, Drafts en technische sync logs uitsluiten + $excludedWellKnown = [ + 'junkemail', 'deleteditems', 'outbox', 'drafts', + 'conflicts', 'localfailures', 'serverfailures', 'syncissues', 'conversationhistory', + 'clutter', 'recoverableitemsdeletions' + ]; + + $excludedExactNames = [ + // Nederlands + 'ongewenste e-mail', 'ongewenste email', 'ongewenst', 'spam', 'junk', + 'verwijderde items', 'prullenbak', 'trash', 'verwijderd', + 'postvak uit', 'outbox', + 'concepten', 'drafts', + 'gespreksgeschiedenis', 'conversation history', + 'problemen met synchronisatie', 'synchronisatieproblemen', 'conflicten', + // Engels + 'junk email', 'deleted items', 'sync issues', 'conflicts', 'local failures', 'server failures' + ]; $folders = []; $seenIds = []; foreach ($rawFolders as $rf) { - $fId = $rf['id'] ?? ''; - if (empty($fId) || isset($seenIds[$fId])) continue; - $seenIds[$fId] = true; + $fId = trim($rf['id'] ?? ''); + if (empty($fId) || isset($seenIds[$fId])) { + continue; + } + + // Sla verborgen mappen over + if (!empty($rf['isHidden'])) { + continue; + } $wk = strtolower(trim($rf['wellKnownName'] ?? '')); - if (in_array($wk, $excludedWellKnown, true)) continue; + if (in_array($wk, $excludedWellKnown, true)) { + continue; + } $dName = trim($rf['displayName'] ?? ''); $dNameLower = strtolower($dName); - $skip = false; - foreach ($excludedTerms as $term) { - if ($dNameLower === $term || strpos($dNameLower, $term) !== false) { - $skip = true; - break; - } + if (in_array($dNameLower, $excludedExactNames, true)) { + continue; + } + if (strpos($dNameLower, 'problemen met synchronisatie') === 0 || strpos($dNameLower, 'sync issues') === 0) { + continue; } - if ($skip) continue; $isInbox = ($wk === 'inbox' || $dNameLower === 'inbox' || $dNameLower === 'postvak in'); + $seenIds[$fId] = true; + $folders[] = [ 'id' => $isInbox ? 'inbox' : $fId, - 'name' => $dName ?: 'Map', + 'name' => $dName ?: ($isInbox ? 'Postvak IN' : 'Map'), 'unread_count' => (int)($rf['unreadItemCount'] ?? 0), 'total_count' => (int)($rf['totalItemCount'] ?? 0), 'is_inbox' => $isInbox, + 'parent_id' => $rf['parentFolderId'] ?? null, + 'is_child' => !empty($rf['parentFolderId']) && !$isInbox, ]; } - // Zorg dat Inbox altijd vooraan staat + // Garandeer dat Inbox altijd aanwezig is $hasInbox = false; foreach ($folders as $f) { if (!empty($f['is_inbox'])) { @@ -1352,9 +1415,12 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'unread_count' => 0, 'total_count' => 0, 'is_inbox' => true, + 'parent_id' => null, + 'is_child' => false, ]); } + // Sorteer: Inbox altijd bovenaan, daarna alfabetisch usort($folders, function($a, $b) { if (!empty($a['is_inbox'])) return -1; if (!empty($b['is_inbox'])) return 1; @@ -1369,7 +1435,9 @@ function microsoft_fetch_mail_folders(int $userId, bool $forceRefresh = false): 'folders' => $folders, 'total_unread' => $totalUnread, ]; - fociz_cache_set($cacheKey, $result, 60); // 60 seconden cache + + fociz_cache_set($cacheKey, $result, 300); // 5 minuten cache + return $result; } @@ -1428,7 +1496,7 @@ function microsoft_fetch_folder_messages(int $userId, string $folderId = 'inbox' $queryParams['$filter'] = 'isRead eq false'; } - $folderEndpoint = ($cleanFolderId === 'inbox') ? 'inbox' : urlencode($cleanFolderId); + $folderEndpoint = ($cleanFolderId === 'inbox') ? 'inbox' : rawurlencode($cleanFolderId); $url = MS_GRAPH_BASE_URL . '/me/mailFolders/' . $folderEndpoint . '/messages?' . http_build_query($queryParams); $res = fociz_http_request($url, [ 'method' => 'GET', diff --git a/jira.php b/jira.php index 85127b7..2f13eba 100644 --- a/jira.php +++ b/jira.php @@ -13,6 +13,11 @@ $userId = (int)($currentUser['id'] ?? 0); $isAdmin = (($currentUser['role'] ?? '') === 'ADMIN'); $forceRefresh = isset($_GET['refresh']) && $_GET['refresh'] === '1'; +// Geef direct het PHP sessieslot vrij zodat achtergrondverzoeken en paginanavigatie nooit blokkeren +if (session_status() === PHP_SESSION_ACTIVE) { + session_write_close(); +} + // Haal Jira data op $jiraData = jira_get_dashboard_data($userId, $forceRefresh); $isDemo = !empty($jiraData['is_demo']); @@ -72,6 +77,12 @@ $defaultQueueFilter = $jiraData['default_queue_filter'] ?? jira_get_user_queue_p $userQueueFilters = $jiraData['user_queue_filters'] ?? jira_get_user_queue_preferences($userId); $userQueueFilters = array_values(array_filter(array_map('strtoupper', array_map('trim', $userQueueFilters ?? [])))); $jiraData['user_queue_filters'] = $userQueueFilters; +$hasUserAssignments = !empty($userQueueFilters); + +$isCodeVisible = function(string $code) use ($hasUserAssignments, $userQueueFilters): bool { + if (!$hasUserAssignments) return true; + return in_array(strtoupper(trim($code)), $userQueueFilters, true); +}; // Hulpfunctie om te controleren of een ticket behoort tot een wachtrij / project $issueMatchesQueue = function(array $iss, string $code) use ($queueFilters): bool { @@ -597,14 +608,6 @@ $currentUserEmailJson = json_encode($currentUserEmail, JSON_HEX_TAG | JSON_HEX_A
- -
+ +
+
+ + +
+ + Machtigingen Bijwerken + +
+ +
@@ -681,9 +698,9 @@ function renderFolders() {