From 32632dab917d194fd7b40547ce8ec668d1ad0c27 Mon Sep 17 00:00:00 2001 From: Niek Rabelink Date: Sat, 12 Sep 2026 13:09:37 +0200 Subject: [PATCH] IT882 --- admin.php | 1680 +++++++++++++++++++++++++---------------- includes/settings.php | 20 + planner.php | 88 +-- 3 files changed, 1110 insertions(+), 678 deletions(-) diff --git a/admin.php b/admin.php index 2324757..49ec067 100644 --- a/admin.php +++ b/admin.php @@ -17,6 +17,12 @@ $db = get_db_connection(); $message = null; $messageType = 'success'; +$validTabs = ['users', 'calendar', 'microsoft', 'jira', 'ai', 'security', 'system']; +$activeTab = $_GET['tab'] ?? 'users'; +if (!in_array($activeTab, $validTabs, true)) { + $activeTab = 'users'; +} + if (!empty($_GET['success'])) { $message = $_GET['success']; $messageType = 'success'; @@ -27,6 +33,10 @@ if (!empty($_GET['success'])) { // POST Verwerking VÓÓR header output zodat redirects en opslaan vlekkeloos werken if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!empty($_POST['active_tab']) && in_array($_POST['active_tab'], $validTabs, true)) { + $activeTab = $_POST['active_tab']; + } + if (!verify_csrf_token($_POST['csrf_token'] ?? null)) { $message = "Beveiligingsfout: CSRF-token ongeldig of verlopen. Ververs de pagina en probeer opnieuw."; $messageType = 'error'; @@ -35,6 +45,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 1. Centrale Azure App Registratie opslaan if ($action === 'save_azure') { + $activeTab = 'microsoft'; $clientId = trim($_POST['client_id'] ?? ''); $clientSecret = trim($_POST['client_secret'] ?? ''); $tenantId = trim($_POST['tenant_id'] ?? ''); @@ -76,6 +87,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 2. Centraal Jira Bedrijfsdomein opslaan elseif ($action === 'save_jira_domain') { + $activeTab = 'jira'; $rawDomain = trim($_POST['jira_domain'] ?? ''); $companyDomain = !empty($rawDomain) ? jira_normalize_domain($rawDomain) : ''; @@ -103,6 +115,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 2b. Jira Wachtrijfilter toevoegen of bewerken elseif ($action === 'save_jira_queue_filter') { + $activeTab = 'jira'; $filterId = (int)($_POST['filter_id'] ?? 0); $filterCode = strtoupper(trim($_POST['filter_code'] ?? '')); $filterName = trim($_POST['filter_name'] ?? ''); @@ -147,6 +160,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 2c. Jira Wachtrijfilter verwijderen elseif ($action === 'delete_jira_queue_filter') { + $activeTab = 'jira'; $filterId = (int)($_POST['filter_id'] ?? 0); if ($filterId > 0) { $stmt = $db->prepare("DELETE FROM jira_queue_filters WHERE id = ?"); @@ -159,6 +173,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 2d. Teamleden toewijzen aan Jira Wachtrijfilter elseif ($action === 'save_user_jira_assignments') { + $activeTab = 'jira'; $assignments = $_POST['user_filter'] ?? []; if (is_array($assignments)) { $upd = $db->prepare("UPDATE users SET default_jira_filter_code = ? WHERE id = ?"); @@ -206,10 +221,84 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $message = "Organisatie werkdagstandaarden zijn succesvol bijgewerkt."; $messageType = 'success'; + $activeTab = 'calendar'; + } + + // 3b. Agenda Blokkade Opties / Redenen opslaan + elseif ($action === 'save_calendar_block_reasons') { + $rawReasons = $_POST['reasons'] ?? []; + $sanitizedReasons = []; + if (is_array($rawReasons)) { + foreach ($rawReasons as $r) { + $code = strtoupper(trim((string)($r['code'] ?? ''))); + $code = preg_replace('/[^A-Z0-9_]/', '', $code); + $title = trim((string)($r['title'] ?? '')); + $icon = trim((string)($r['icon'] ?? '📅')); + $isActive = isset($r['is_active']) && ($r['is_active'] == '1' || $r['is_active'] === 'on') ? 1 : 0; + $isDelete = isset($r['is_delete']) && ($r['is_delete'] == '1' || $r['is_delete'] === 'on'); + + if ($isDelete || empty($code) || empty($title)) { + continue; + } + + $sanitizedReasons[] = [ + 'code' => $code, + 'title' => $title, + 'icon' => !empty($icon) ? $icon : '📅', + 'is_active' => $isActive, + ]; + } + } + + // Nieuwe optie toevoegen indien ingevuld + $newCode = strtoupper(trim((string)($_POST['new_code'] ?? ''))); + $newCode = preg_replace('/[^A-Z0-9_]/', '', $newCode); + $newTitle = trim((string)($_POST['new_title'] ?? '')); + $newIcon = trim((string)($_POST['new_icon'] ?? '📅')); + + if (!empty($newCode) && !empty($newTitle)) { + $exists = false; + foreach ($sanitizedReasons as &$sr) { + if ($sr['code'] === $newCode) { + $sr['title'] = $newTitle; + $sr['icon'] = !empty($newIcon) ? $newIcon : '📅'; + $sr['is_active'] = 1; + $exists = true; + break; + } + } + unset($sr); + if (!$exists) { + $sanitizedReasons[] = [ + 'code' => $newCode, + 'title' => $newTitle, + 'icon' => !empty($newIcon) ? $newIcon : '📅', + 'is_active' => 1, + ]; + } + } + + if (empty($sanitizedReasons)) { + $sanitizedReasons = [ + ['code' => 'VACATION', 'title' => 'Vakantie', 'icon' => '🏖️', 'is_active' => 1], + ['code' => 'LEAVE', 'title' => 'Verlof / Vrij', 'icon' => '✈️', 'is_active' => 1], + ['code' => 'SICK', 'title' => 'Ziek / Afwezig', 'icon' => '🤒', 'is_active' => 1], + ['code' => 'FOCUS', 'title' => 'Focusdag', 'icon' => '🎯', 'is_active' => 1], + ['code' => 'ADV', 'title' => 'ADV Dag', 'icon' => '📋', 'is_active' => 1], + ]; + } + + set_system_setting('calendar_block_reasons', $sanitizedReasons); + fociz_log_audit($userId, 'SETTINGS_UPDATED', 'calendar_block_reasons', null, ['count' => count($sanitizedReasons)], 'SUCCESS', $adminUser['email']); + + $message = "Agenda blokkade opties zijn succesvol opgeslagen."; + $messageType = 'success'; + $activeTab = 'calendar'; } // 4. AI Co-Pilot instellingen opslaan (Release v1.5) elseif ($action === 'save_ai_settings') { + $activeTab = 'ai'; try { $aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0; $aiModel = trim((string)($_POST['ai_model'] ?? 'gemini-1.5-pro')); @@ -244,6 +333,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 5. Test e-mail versturen via SMTP (Release v1.6) elseif ($action === 'send_test_email') { + $activeTab = 'system'; require_once __DIR__ . '/includes/mailer.php'; $testEmail = trim($_POST['test_email'] ?? ''); if (empty($testEmail) || !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) { @@ -263,6 +353,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 6. Sessie- & Beveiligingsinstellingen opslaan (ISO 27001 / NIST SP 800-63B) elseif ($action === 'save_security_settings') { + $activeTab = 'security'; $timeout = (int)($_POST['session_idle_timeout'] ?? 1800); if (!in_array($timeout, [900, 1800, 3600, 7200], true)) { $timeout = 1800; @@ -283,6 +374,7 @@ $companyJiraDomain = jira_get_company_domain(); $workdaySettings = get_workday_settings(); $aiSettings = get_ai_settings(); $sessionIdleTimeout = get_session_idle_timeout(); +$calendarBlockReasons = get_calendar_block_reasons(); // Redirect URI $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php'; @@ -336,6 +428,19 @@ try { error_log("Fout bij ophalen recente gebruikers: " . $e->getMessage()); } +$allUsersList = []; +try { + $allUsersList = $db->query(" + SELECT u.id, u.name, u.email, u.role, u.created_at, + (SELECT COUNT(*) FROM integrations i WHERE i.user_id = u.id AND i.provider = 'OUTLOOK' AND i.access_token IS NOT NULL) as has_outlook, + (SELECT COUNT(*) FROM integrations i WHERE i.user_id = u.id AND i.provider = 'JIRA' AND i.settings LIKE '%jira_api_token%' AND i.settings NOT LIKE '%\"jira_api_token\":\"\"%') as has_jira + FROM users u + ORDER BY u.name ASC + ")->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen allUsersList: " . $e->getMessage()); +} + $allUsersForJira = []; $allJiraFilters = []; try { @@ -444,492 +549,162 @@ require_once __DIR__ . '/includes/header.php'; - -
- - -
- - -
-
-
-
- -
-
-
-

Microsoft 365 Koppeling (Agenda, E-mail & Teams)

- - - Geconfigureerd - - - - Configuratie Vereist - - -
-

Centrale OAuth 2.0 koppeling voor Outlook Agenda, E-mail (Postvak IN) en Microsoft Teams

-
-
- - - - Admin Consent Verlenen - - - -
+ +
+
+ + + + + + + +
+
-
- - + +
- -
-
- - Kopieer dit exact naar de Azure Web Redirect URIs -
-
- - -
-
- - -
-
- - -
- -
- - - Vul uw domeinnaam in (planningsagenda.nl) of de Directory (tenant) ID uit Azure. Let op: Gebruik géén 'common' voor single-tenant apps. -
-
- - -
-
- - Wordt veilig en versleuteld opgeslagen -
-
- - -
-
- -
-
- Zodra opgeslagen en goedgekeurd kunnen al uw collega's met 1 klik hun Outlook agenda én e-mail koppelen. -
- -
- - - -
- - - -
-
- - -
-
-
- -
-
-
-

Atlassian Jira Organisatie-instellingen

- - - Actief - - - - Optioneel - - -
-

Standaard Jira Cloud domein voor het hele team

-
-
- -
- - - -
- - -

- Wanneer u dit instelt, wordt dit domein automatisch vooraf ingevuld op de profielpagina van alle collega's. Zij hoeven dan enkel nog hun eigen persoonlijke e-mailadres en Atlassian API-token in te voeren. -

-
- -
- -
-
-
- - + +
+
-
+
-
- +
+
-

Jira Wachtrijfilters & Toewijzingen

- - ServiceDesk & Teams +

Team & Medewerkers

+ + Leden
-

- Beheer aangepaste JQL-wachtrijfilters (zoals DX en SD) en wijs teamleden toe aan hun standaard wachtrij. -

+

Centraal overzicht van alle teamaccounts, beheerdersrollen en gekoppelde integraties

- +
- -
-
-

- - Geconfigureerde Wachtrijfilters () -

- Worden getoond als snelle filtertabs op de Jira pagina -
- - -
-

Nog geen wachtrijfilters ingesteld. Klik op "Nieuw Wachtrijfilter" om te beginnen.

-
- - -
- - -
-
-
- - - - - - - - - Project: - - - - Alle projecten - - - - - Inactief - - -
- - -

- -

- - - -
- JQL: -
- - -
- Standaard gekoppeld aan: - - - - - - - - - - Nog niemand specifiek toegewezen - + +
+ + + + + + + + + + + + + + + + + + + + + + + - - - - - -
-
-
-

- - Team Wachtrijmatrix: Standaard Wachtrij per Collega -

-

- Wijs collega's toe aan hun primaire wachtrij (bijv. DX of SD). Wanneer zij de Jira pagina openen, wordt deze wachtrij direct als standaard getoond. -

-
-
- -
- - - -
-
MedewerkerE-mailadresRolOutlook 365JiraGeregistreerdBeheer
+
+
+
+
- - -
- -
- - - - -
-
- +
+ + + + + Beheerder + + + + Medewerker + + + + + + Gekoppeld + + + + Niet gekoppeld + + + + + + Gekoppeld + + + + Niet gekoppeld + + + + + + + Beheren + + +
- - - - - - - - - - - - - - - - - - -
TeamlidE-mailadresRolStandaard Wachtrijfilter
-
- - Avatar - - -
- -
- - -
-
- - - - - - - -
-
- -
- -
- -
-
- - - +
+ +