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 Azure App instellingen 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'; } // 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', $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()); } $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 & Azure App Registratie

Geconfigureerd Configuratie Vereist

Centrale OAuth 2.0 instellingen voor de hele organisatie

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 kunnen al uw collega's met 1 klik hun Outlook agenda 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.

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