prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'"); $stmtExisting->execute([$userId]); $existing = $stmtExisting->fetch(); $existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: []; if (($clientSecret === '' || $clientSecret === '••••••••••••••••') && !empty($existingSettings['client_secret']) && $existingSettings['client_secret'] !== '••••••••••••••••') { $clientSecret = $existingSettings['client_secret']; } $newSettings = array_merge($existingSettings, [ 'client_id' => $clientId, 'client_secret' => $clientSecret, 'tenant_id' => $tenantId, ]); $stmt = $db->prepare(" INSERT INTO integrations (user_id, provider, is_enabled, is_demo_mode, settings, last_sync_at) VALUES (?, 'OUTLOOK', 1, ?, ?, NOW()) ON DUPLICATE KEY UPDATE is_demo_mode = VALUES(is_demo_mode), settings = VALUES(settings), last_sync_at = NOW() "); $stmt->execute([$userId, $isDemo, json_encode($newSettings)]); $message = "Centrale Microsoft 365 instellingen (Agenda, E-mail & Teams) zijn succesvol opgeslagen."; $messageType = 'success'; } // 2. Centraal Jira Bedrijfsdomein opslaan elseif ($action === 'save_jira_domain') { $rawDomain = trim($_POST['jira_domain'] ?? ''); $companyDomain = !empty($rawDomain) ? jira_normalize_domain($rawDomain) : ''; $stmtExisting = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'JIRA'"); $stmtExisting->execute([$userId]); $existing = $stmtExisting->fetch(); $existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: []; $existingSettings['jira_domain'] = $companyDomain; $stmt = $db->prepare(" INSERT INTO integrations (user_id, provider, is_enabled, is_demo_mode, settings, last_sync_at) VALUES (?, 'JIRA', 1, 0, ?, NOW()) ON DUPLICATE KEY UPDATE settings = VALUES(settings), last_sync_at = NOW() "); $stmt->execute([$userId, json_encode($existingSettings)]); $message = "Standaard Atlassian Jira bedrijfsdomein is succesvol opgeslagen."; $messageType = 'success'; } // 2b. Jira Wachtrijfilter toevoegen of bewerken elseif ($action === 'save_jira_queue_filter') { $filterId = (int)($_POST['filter_id'] ?? 0); $filterCode = strtoupper(trim($_POST['filter_code'] ?? '')); $filterName = trim($_POST['filter_name'] ?? ''); $projectKey = strtoupper(trim($_POST['project_key'] ?? '')); $jqlClause = trim($_POST['jql_clause'] ?? ''); $description = trim($_POST['description'] ?? ''); $isActive = isset($_POST['is_active']) ? 1 : 0; $sortOrder = (int)($_POST['sort_order'] ?? 0); if (empty($filterCode) || empty($filterName) || empty($jqlClause)) { $message = "Filtercode, naam en JQL-clausule zijn verplicht."; $messageType = 'error'; } else { if ($filterId > 0) { $stmt = $db->prepare(" UPDATE jira_queue_filters SET filter_code = ?, filter_name = ?, project_key = ?, jql_clause = ?, description = ?, is_active = ?, sort_order = ?, updated_at = NOW() WHERE id = ? "); $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder, $filterId]); $message = "Wachtrijfilter '{$filterName}' is succesvol bijgewerkt."; } else { $stmt = $db->prepare(" INSERT INTO jira_queue_filters (filter_code, filter_name, project_key, jql_clause, description, is_active, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE filter_name = VALUES(filter_name), project_key = VALUES(project_key), jql_clause = VALUES(jql_clause), description = VALUES(description), is_active = VALUES(is_active), sort_order = VALUES(sort_order), updated_at = NOW() "); $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder]); $message = "Nieuw wachtrijfilter '{$filterName}' is succesvol toegevoegd."; } $messageType = 'success'; } } // 2c. Jira Wachtrijfilter verwijderen elseif ($action === 'delete_jira_queue_filter') { $filterId = (int)($_POST['filter_id'] ?? 0); if ($filterId > 0) { $stmt = $db->prepare("DELETE FROM jira_queue_filters WHERE id = ?"); $stmt->execute([$filterId]); $message = "Wachtrijfilter is succesvol verwijderd."; $messageType = 'success'; } } // 2d. Teamleden toewijzen aan Jira Wachtrijfilter elseif ($action === 'save_user_jira_assignments') { $assignments = $_POST['user_filter'] ?? []; if (is_array($assignments)) { $upd = $db->prepare("UPDATE users SET default_jira_filter_code = ? WHERE id = ?"); foreach ($assignments as $uId => $fCode) { $fCode = trim($fCode); $upd->execute([$fCode !== '' ? $fCode : null, (int)$uId]); } $message = "Standaard Jira wachtrijtoewijzingen voor het team zijn succesvol opgeslagen."; $messageType = 'success'; } } // 3. Organisatie Werkdag Standaarden opslaan (Release v1.4) elseif ($action === 'save_workday_settings') { $workdayStart = trim($_POST['workday_start'] ?? '08:30'); $workdayEnd = trim($_POST['workday_end'] ?? '17:30'); $lunchStart = trim($_POST['lunch_start'] ?? '12:30'); $lunchEnd = trim($_POST['lunch_end'] ?? '13:00'); $pomoFocus = max(10, min(120, (int)($_POST['pomodoro_focus'] ?? 25))); $pomoDeep = max(20, min(180, (int)($_POST['pomodoro_deep'] ?? 50))); $pomoBreak = max(2, min(30, (int)($_POST['pomodoro_break'] ?? 5))); $taskDuration = in_array((int)($_POST['default_task_duration'] ?? 30), [15, 30, 45, 60, 90, 120]) ? (int)$_POST['default_task_duration'] : 30; set_system_setting('workday_start', $workdayStart); set_system_setting('workday_end', $workdayEnd); set_system_setting('lunch_start', $lunchStart); set_system_setting('lunch_end', $lunchEnd); set_system_setting('pomodoro_focus', $pomoFocus); set_system_setting('pomodoro_deep', $pomoDeep); set_system_setting('pomodoro_break', $pomoBreak); set_system_setting('default_task_duration', $taskDuration); $message = "Organisatie werkdagstandaarden zijn succesvol bijgewerkt."; $messageType = 'success'; } // 4. AI Co-Pilot instellingen opslaan (Release v1.5) elseif ($action === 'save_ai_settings') { $aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0; $aiProvider = trim($_POST['ai_provider'] ?? 'openai'); $aiModel = trim($_POST['ai_model'] ?? 'gpt-4o-mini'); $apiKeyIn = trim($_POST['ai_api_key'] ?? ''); $currentAi = get_ai_settings(); if ($apiKeyIn !== '' && $apiKeyIn !== '••••••••••••••••') { set_system_setting('ai_api_key', fociz_encrypt($apiKeyIn)); } set_system_setting('ai_enabled', $aiEnabled); set_system_setting('ai_provider', $aiProvider); set_system_setting('ai_model', $aiModel); $message = "AI Co-Pilot instellingen zijn succesvol opgeslagen."; $messageType = 'success'; } // 5. Test e-mail versturen via SMTP (Release v1.6) elseif ($action === 'send_test_email') { require_once __DIR__ . '/includes/mailer.php'; $testEmail = trim($_POST['test_email'] ?? ''); if (empty($testEmail) || !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) { $message = "Vul een geldig e-mailadres in om een test e-mail te sturen."; $messageType = 'error'; } else { $mailResult = FocizMailer::sendTestEmail($testEmail, $currentUser['name']); if ($mailResult['success']) { $message = "Test e-mail succesvol verzonden via mail.fociz.nl naar " . htmlspecialchars($testEmail) . "!"; $messageType = 'success'; } else { $message = "Fout bij verzenden test e-mail via mail.fociz.nl: " . htmlspecialchars($mailResult['error'] ?? 'Onbekende fout'); $messageType = 'error'; } } } } } // Haal actuele configuratie op $appConfig = microsoft_get_app_config(); $companyJiraDomain = jira_get_company_domain(); $workdaySettings = get_workday_settings(); $aiSettings = get_ai_settings(); // Redirect URI $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php'; // Admin Consent URL $tenantForConsent = !empty($appConfig['tenant_id']) ? $appConfig['tenant_id'] : 'common'; $adminConsentUrl = "https://login.microsoftonline.com/" . urlencode($tenantForConsent) . "/adminconsent?client_id=" . urlencode($appConfig['client_id']) . "&redirect_uri=" . urlencode($redirectUri); // Statistieken ophalen voor Systeem & Team Monitor $totalUsers = 0; $totalAdmins = 0; $totalMembers = 0; $pendingInvites = 0; $outlookConnectedCount = 0; $jiraConnectedCount = 0; $recentUsers = []; try { $totalUsers = (int)$db->query("SELECT COUNT(*) FROM users")->fetchColumn(); $totalAdmins = (int)$db->query("SELECT COUNT(*) FROM users WHERE role = 'ADMIN'")->fetchColumn(); $totalMembers = $totalUsers - $totalAdmins; } catch (Throwable $e) { error_log("Fout bij ophalen user stats: " . $e->getMessage()); } try { $pendingInvites = (int)$db->query("SELECT COUNT(*) FROM invites WHERE used_at IS NULL AND expires_at > NOW()")->fetchColumn(); } catch (Throwable $e) { error_log("Fout bij ophalen invites stats: " . $e->getMessage()); ensure_tables_exist($db); } try { $outlookConnectedCount = (int)$db->query("SELECT COUNT(*) FROM integrations WHERE provider = 'OUTLOOK' AND access_token IS NOT NULL")->fetchColumn(); $jiraConnectedCount = (int)$db->query("SELECT COUNT(*) FROM integrations WHERE provider = 'JIRA' AND settings LIKE '%jira_api_token%' AND settings NOT LIKE '%\"jira_api_token\":\"\"%'")->fetchColumn(); } catch (Throwable $e) { error_log("Fout bij ophalen integrations stats: " . $e->getMessage()); ensure_tables_exist($db); } try { $recentUsers = $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.created_at DESC LIMIT 6 ")->fetchAll() ?: []; } catch (Throwable $e) { error_log("Fout bij ophalen recente gebruikers: " . $e->getMessage()); } $allUsersForJira = []; $allJiraFilters = []; try { $allUsersForJira = $db->query("SELECT id, name, email, role, avatar_url, default_jira_filter_code FROM users ORDER BY name ASC")->fetchAll() ?: []; $allJiraFilters = $db->query("SELECT * FROM jira_queue_filters ORDER BY sort_order ASC, id ASC")->fetchAll() ?: []; } catch (Throwable $e) { error_log("Fout bij ophalen jira filters/users: " . $e->getMessage()); } $pageTitle = 'Systeembeheer & Integraties — Fociz'; require_once __DIR__ . '/includes/header.php'; ?>
Beheerderspaneel • Alleen toegankelijk voor Beheer

Systeembeheer & Integraties

Beheer hier de centrale organisatiekoppelingen zoals Microsoft 365 Azure App Registratie en Jira, inspecteer de teamstatus en configureer toekomstige modules.

Totaal Teamleden Beheerders • Collega's
Outlook Live Gekoppeld
Jira Gekoppeld
Openstaande Invites Beheer uitnodigingen →

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

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

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.

Teamlid E-mailadres Rol Standaard Wachtrijfilter
Avatar

Werkdag & Dagplanning Standaarden

Centrale kantoortijden, lunchpauzes en Pomodoro presets voor het team

Release v1.4 Actief
Kantooruren / Tijdlijn Bereik

Bepaalt het standaard start- en einduur op de dagelijkse tijdlijn.

Automatisch Lunchpauze Blok

Wordt visueel gemarkeerd als pauzeblok op de tijdlijn.

Focus & Pomodoro Presets (minuten)
Standaard Taakduur bij Aanmaken

Selectie bij het toevoegen van nieuwe dagtaken.

AI & Brainstorm Co-Pilot

Intelligente assistentie voor post-it categorisatie, ideegeneratie en slimme planning

SMTP Mail Server & Notificaties

Uitgaande e-mailserver voor wachtwoord-vergeten, uitnodigingen en notificaties

mail.fociz.nl:587 • Actief
SMTP Host & Poort: : (STARTTLS)
SMTP Gebruiker:
Zichtbare Afzender (From): <>
Antwoordadres (Reply-To):

Directe SMTP Verbindingstest

Verstuur een realtime testmail met het Fociz HTML-sjabloon om de verbinding, STARTTLS-handshake en aflevering direct te verifiëren.

Systeem & Beveiliging
Tweestapsverificatie (2FA) Verplicht
Database Versie MariaDB 10.x
Push Notificaties Actief
Graph API Sync Live v1.0