Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ddae4d883 | |||
| d4e4b69e02 |
+8
-1
@@ -126,8 +126,14 @@ function ensure_tables_exist(PDO $pdo): void {
|
||||
if ($tablesChecked) return;
|
||||
$tablesChecked = true;
|
||||
|
||||
try {
|
||||
$schemaFile = file_exists(__DIR__ . '/schema.sql') ? (__DIR__ . '/schema.sql') : (__DIR__ . '/../schema.sql');
|
||||
$lockFile = sys_get_temp_dir() . '/fociz_schema_' . md5(__DIR__) . '.lock';
|
||||
|
||||
if (file_exists($lockFile) && file_exists($schemaFile) && filemtime($lockFile) >= filemtime($schemaFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (file_exists($schemaFile)) {
|
||||
$sql = file_get_contents($schemaFile);
|
||||
$statements = array_filter(array_map('trim', explode(';', $sql)));
|
||||
@@ -300,6 +306,7 @@ function ensure_tables_exist(PDO $pdo): void {
|
||||
} catch (Throwable $e) {
|
||||
// Stil negeren indien system_settings tabel nog niet bestaat
|
||||
}
|
||||
@touch($lockFile);
|
||||
} catch (Throwable $e) {
|
||||
error_log("Fout bij controleren tabellen: " . $e->getMessage());
|
||||
}
|
||||
|
||||
@@ -347,6 +347,11 @@ function ensure_remember_token_columns(PDO $db): void {
|
||||
if ($columnsChecked) return;
|
||||
$columnsChecked = true;
|
||||
|
||||
$lockFile = sys_get_temp_dir() . '/fociz_auth_cols_' . md5(__DIR__) . '.lock';
|
||||
if (file_exists($lockFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'");
|
||||
if (!$result || $result->rowCount() === 0) {
|
||||
@@ -360,6 +365,7 @@ function ensure_remember_token_columns(PDO $db): void {
|
||||
if (!$resActivity || $resActivity->rowCount() === 0) {
|
||||
$db->exec("ALTER TABLE `users` ADD COLUMN `last_activity_at` DATETIME NULL AFTER `remember_token_expires_at`");
|
||||
}
|
||||
@touch($lockFile);
|
||||
} catch (Throwable $e) {
|
||||
// Stil negeren indien al aanwezig of tijdens migraties
|
||||
}
|
||||
@@ -374,6 +380,11 @@ function ensure_sessions_table(PDO $db): void {
|
||||
if ($sessionsTableChecked) return;
|
||||
$sessionsTableChecked = true;
|
||||
|
||||
$lockFile = sys_get_temp_dir() . '/fociz_sessions_tbl_' . md5(__DIR__) . '.lock';
|
||||
if (file_exists($lockFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS `user_sessions` (
|
||||
@@ -394,6 +405,7 @@ function ensure_sessions_table(PDO $db): void {
|
||||
CONSTRAINT `fk_user_sessions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
");
|
||||
@touch($lockFile);
|
||||
} catch (Throwable $e) {
|
||||
error_log("ensure_sessions_table notice: " . $e->getMessage());
|
||||
}
|
||||
|
||||
+2
-1
@@ -70,7 +70,8 @@ if (!function_exists('fociz_http_request')) {
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(4, max(1, $timeout - 1)));
|
||||
curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 600);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerLines);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl);
|
||||
|
||||
@@ -343,7 +343,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT
|
||||
|
||||
$cacheKey = "jira_proj_statuses_" . $pKey . "_" . md5($cleanDomain);
|
||||
$cached = fociz_cache_get($cacheKey);
|
||||
if (is_array($cached) && !empty($cached)) {
|
||||
if ($cached !== null && is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 8,
|
||||
'timeout' => 4,
|
||||
]);
|
||||
|
||||
if ($response['status'] === 404 || $response['status'] === 405) {
|
||||
@@ -367,7 +367,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 8,
|
||||
'timeout' => 4,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT
|
||||
|
||||
fociz_cache_set($cacheKey, $statuses, 86400); // 24 uur cache
|
||||
} else {
|
||||
fociz_cache_set($cacheKey, [], 1800); // 30 min negatieve cache
|
||||
fociz_cache_set($cacheKey, [], 86400); // 24 uur negatieve cache zodat ontbrekende projecten niet herhaaldelijk vertragen
|
||||
}
|
||||
|
||||
return $statuses;
|
||||
@@ -471,7 +471,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => $payload,
|
||||
'timeout' => 12,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
|
||||
if ($response['status'] === 405 || $response['status'] === 400) {
|
||||
@@ -500,7 +500,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 12,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
|
||||
if ($response['status'] === 404) {
|
||||
@@ -513,9 +513,13 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
||||
$getParams = [
|
||||
'jql' => $jql,
|
||||
'maxResults' => $currentPageSize,
|
||||
'startAt' => $startAt,
|
||||
'fields' => implode(',', $fieldsList),
|
||||
];
|
||||
if (!empty($nextPageToken)) {
|
||||
$getParams['nextPageToken'] = $nextPageToken;
|
||||
} elseif ($startAt > 0) {
|
||||
$getParams['startAt'] = $startAt;
|
||||
}
|
||||
$urlV2 = $cleanDomain . '/rest/api/2/search?' . http_build_query($getParams);
|
||||
$response = fociz_http_request($urlV2, [
|
||||
'method' => 'GET',
|
||||
@@ -523,7 +527,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
||||
'Authorization' => $authHeader,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'timeout' => 12,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1238,6 +1242,11 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
|
||||
$availableStatuses = jira_fetch_statuses($domain, $email, $apiToken);
|
||||
|
||||
if (!$assignedRes['success'] && !$teamRes['success']) {
|
||||
// Val terug op bestaande cache snapshot indien live request faalt
|
||||
$fallback = fociz_cache_get($cacheKey);
|
||||
if ($fallback && is_array($fallback)) {
|
||||
return array_merge($fallback, ['from_cache' => true, 'warning' => 'Kon Jira niet actueel bijwerken; tonen opgeslagen snapshot.']);
|
||||
}
|
||||
return [
|
||||
'is_demo' => false,
|
||||
'success' => false,
|
||||
@@ -1250,17 +1259,10 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
|
||||
$assignedIssues = $assignedRes['success'] ? $assignedRes['issues'] : [];
|
||||
$teamIssues = $teamRes['success'] ? $teamRes['issues'] : [];
|
||||
|
||||
// Project-specifieke statussen ophalen voor gebruikte projecten
|
||||
$activeProjKeys = ['SD', 'IN', 'AAN', 'GEN'];
|
||||
$allLoaded = array_merge($assignedIssues, $teamIssues);
|
||||
foreach ($allLoaded as $iss) {
|
||||
$pk = strtoupper(trim($iss['project_key'] ?? ''));
|
||||
if (!empty($pk) && !in_array($pk, $activeProjKeys, true)) {
|
||||
$activeProjKeys[] = $pk;
|
||||
}
|
||||
}
|
||||
// Projectstatussen: haal uitsluitend het actieve hoofdproject op om sequentiële HTTP latency te elimineren
|
||||
$projectStatusesMap = [];
|
||||
foreach ($activeProjKeys as $pk) {
|
||||
$targetProjects = ['SD'];
|
||||
foreach ($targetProjects as $pk) {
|
||||
$pStats = jira_fetch_project_statuses($domain, $email, $apiToken, $pk);
|
||||
if (!empty($pStats)) {
|
||||
$projectStatusesMap[$pk] = $pStats;
|
||||
@@ -1283,7 +1285,7 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
|
||||
'project_statuses' => $projectStatusesMap,
|
||||
];
|
||||
|
||||
fociz_cache_set($cacheKey, $data, 180); // 3 minuten cache
|
||||
fociz_cache_set($cacheKey, $data, 900); // 15 minuten cache voor supersnelle paginaladingen
|
||||
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
$_SESSION[$cacheKey] = [
|
||||
|
||||
@@ -330,7 +330,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en
|
||||
'Accept' => 'application/json',
|
||||
'Prefer' => 'outlook.timezone="Europe/Amsterdam"',
|
||||
],
|
||||
'timeout' => 12,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
|
||||
// Retry bij 401 Unauthorized
|
||||
@@ -345,7 +345,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en
|
||||
'Accept' => 'application/json',
|
||||
'Prefer' => 'outlook.timezone="Europe/Amsterdam"',
|
||||
],
|
||||
'timeout' => 12,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en
|
||||
];
|
||||
}
|
||||
|
||||
fociz_cache_set($cacheKey, $events, 60);
|
||||
fociz_cache_set($cacheKey, $events, 300); // 5 minuten cache
|
||||
return $events;
|
||||
}
|
||||
|
||||
@@ -1567,7 +1567,7 @@ function microsoft_fetch_folder_messages(int $userId, string $folderId = 'inbox'
|
||||
'Accept' => 'application/json',
|
||||
'Prefer' => 'outlook.body-content-type="text"',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'timeout' => 6,
|
||||
]);
|
||||
|
||||
if ($res['status'] !== 200 || !isset($res['json']['value'])) {
|
||||
|
||||
+50
-43
@@ -127,12 +127,11 @@ if ($isOutlookConnected) {
|
||||
// Synchroniseer / reconcilieer Fociz taken direct met actuele Outlook data
|
||||
// Alleen uitvoeren indien events gewijzigd zijn of bij expliciete refresh om overtollige DB load te voorkomen
|
||||
$eventsHash = md5(json_encode($weekOutlookEvents));
|
||||
$lastReconciledHash = $_SESSION["last_reconciled_{$userId}"] ?? '';
|
||||
$cacheReconcileKey = "last_reconciled_{$userId}";
|
||||
$lastReconciledHash = fociz_cache_get($cacheReconcileKey, '');
|
||||
if ($eventsHash !== $lastReconciledHash || $forceRefresh) {
|
||||
microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents);
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
$_SESSION["last_reconciled_{$userId}"] = $eventsHash;
|
||||
}
|
||||
fociz_cache_set($cacheReconcileKey, $eventsHash, 86400);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("Outlook synchronisatiefout in planner: " . $e->getMessage());
|
||||
@@ -304,45 +303,7 @@ for ($i = 0; $i < 7; $i++) {
|
||||
];
|
||||
}
|
||||
|
||||
// 5. Haal Jira dashboard data op voor de snelle import widget en wachtrijfiltering
|
||||
$jiraAssignedIssues = [];
|
||||
$jiraTeamIssues = [];
|
||||
$jiraAllIssues = [];
|
||||
$jiraQueueFilters = [];
|
||||
$isJiraLive = false;
|
||||
try {
|
||||
$jiraDashboardData = jira_get_dashboard_data($userId, $forceRefresh);
|
||||
$jiraAssignedIssues = $jiraDashboardData['assigned_issues'] ?? [];
|
||||
$jiraTeamIssues = $jiraDashboardData['team_issues'] ?? [];
|
||||
$jiraQueueFilters = $jiraDashboardData['queue_filters'] ?? [];
|
||||
$isJiraLive = !empty($jiraDashboardData['success']) && empty($jiraDashboardData['is_demo']);
|
||||
|
||||
$seenJiraKeys = [];
|
||||
foreach (array_merge($jiraAssignedIssues, $jiraTeamIssues) as $iss) {
|
||||
if (!empty($iss['key']) && !isset($seenJiraKeys[$iss['key']])) {
|
||||
$seenJiraKeys[$iss['key']] = true;
|
||||
$jiraAllIssues[] = $iss;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("Fout bij ophalen Jira issues in planner: " . $e->getMessage());
|
||||
}
|
||||
$jiraIssues = $jiraAllIssues;
|
||||
|
||||
// 5b. Haal recente e-mails op voor de Outlook E-mail Widget
|
||||
$recentEmails = [];
|
||||
$unreadEmailCount = 0;
|
||||
$mailNeedsReconsent = false;
|
||||
try {
|
||||
$mailData = microsoft_fetch_inbox_messages($userId, 5, false, $forceRefresh);
|
||||
$recentEmails = $mailData['messages'] ?? [];
|
||||
$unreadEmailCount = (int)($mailData['unread_count'] ?? 0);
|
||||
$mailNeedsReconsent = !empty($mailData['needs_reconsent']);
|
||||
} catch (Throwable $e) {
|
||||
error_log("Fout bij ophalen recente e-mails in planner: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// 5c. Widget Catalogus & Raster Indeling (Fociz v2.0)
|
||||
// 5. Widget Catalogus & Raster Indeling (Fociz v2.0)
|
||||
$availableWidgets = [
|
||||
'daily_goal' => ['id' => 'daily_goal', 'title' => 'Dagfocus & Hoofddoel', 'category' => 'Planning', 'icon' => 'target', 'enabled' => true, 'col_span' => 12, 'order' => 1, 'desc' => 'Centraal focusdoel voor de dag met snelle tellers'],
|
||||
'timeline' => ['id' => 'timeline', 'title' => 'Tijdlijn & Dag/Weekoverzicht', 'category' => 'Agenda', 'icon' => 'calendar', 'enabled' => true, 'col_span' => 12, 'order' => 2, 'desc' => 'Gecombineerd weekoverzicht, dagselectie en uren-tijdlijn met time-blocking en AI'],
|
||||
@@ -393,6 +354,52 @@ uasort($userWidgetLayout, function($a, $b) {
|
||||
return strcmp($a['id'] ?? '', $b['id'] ?? '');
|
||||
});
|
||||
|
||||
// 5b. Haal Jira dashboard data op voor de snelle import widget (indien ingeschakeld)
|
||||
$jiraAssignedIssues = [];
|
||||
$jiraTeamIssues = [];
|
||||
$jiraAllIssues = [];
|
||||
$jiraQueueFilters = [];
|
||||
$isJiraLive = false;
|
||||
$isJiraWidgetEnabled = !empty($userWidgetLayout['jira_queue']['enabled']);
|
||||
|
||||
if ($isJiraWidgetEnabled) {
|
||||
try {
|
||||
$jiraDashboardData = jira_get_dashboard_data($userId, $forceRefresh);
|
||||
$jiraAssignedIssues = $jiraDashboardData['assigned_issues'] ?? [];
|
||||
$jiraTeamIssues = $jiraDashboardData['team_issues'] ?? [];
|
||||
$jiraQueueFilters = $jiraDashboardData['queue_filters'] ?? [];
|
||||
$isJiraLive = !empty($jiraDashboardData['success']) && empty($jiraDashboardData['is_demo']);
|
||||
|
||||
$seenJiraKeys = [];
|
||||
foreach (array_merge($jiraAssignedIssues, $jiraTeamIssues) as $iss) {
|
||||
if (!empty($iss['key']) && !isset($seenJiraKeys[$iss['key']])) {
|
||||
$seenJiraKeys[$iss['key']] = true;
|
||||
$jiraAllIssues[] = $iss;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("Fout bij ophalen Jira issues in planner: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$jiraIssues = $jiraAllIssues;
|
||||
|
||||
// 5c. Haal recente e-mails op voor de Outlook E-mail Widget (indien ingeschakeld)
|
||||
$recentEmails = [];
|
||||
$unreadEmailCount = 0;
|
||||
$mailNeedsReconsent = false;
|
||||
$isMailWidgetEnabled = !empty($userWidgetLayout['outlook_mail']['enabled']);
|
||||
|
||||
if ($isMailWidgetEnabled) {
|
||||
try {
|
||||
$mailData = microsoft_fetch_inbox_messages($userId, 5, false, $forceRefresh);
|
||||
$recentEmails = $mailData['messages'] ?? [];
|
||||
$unreadEmailCount = (int)($mailData['unread_count'] ?? 0);
|
||||
$mailNeedsReconsent = !empty($mailData['needs_reconsent']);
|
||||
} catch (Throwable $e) {
|
||||
error_log("Fout bij ophalen recente e-mails in planner: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Bereken dagstatistieken
|
||||
$completedTasksCount = 0;
|
||||
$pendingTasksCount = 0;
|
||||
|
||||
Reference in New Issue
Block a user