prepare("SELECT settings FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'"); $stmtExisting->execute([$userId]); $existing = $stmtExisting->fetch(); $settings = json_decode($existing['settings'] ?? '{}', true) ?: []; $settings['sync_interval_seconds'] = $syncInterval; $stmtUp = $db->prepare("UPDATE integrations SET settings = ? WHERE user_id = ? AND provider = 'OUTLOOK'"); $stmtUp->execute([json_encode($settings), $userId]); $message = "Uw Microsoft Outlook synchronisatievoorkeur is succesvol opgeslagen."; $messageType = 'success'; } // 3. Jira Opslaan & Testen elseif ($action === 'save_jira') { $companyDomain = jira_get_company_domain(); $rawDomain = trim($_POST['jira_domain'] ?? ''); $jiraDomain = !empty($rawDomain) ? jira_normalize_domain($rawDomain) : $companyDomain; $jiraEmail = trim($_POST['jira_email'] ?? ''); $jiraToken = trim($_POST['jira_api_token'] ?? ''); // Haal bestaande instellingen op $stmtExisting = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'JIRA'"); $stmtExisting->execute([$userId]); $existing = $stmtExisting->fetch(); $existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: []; if (($jiraToken === '' || $jiraToken === '••••••••••••••••') && !empty($existingSettings['jira_api_token']) && $existingSettings['jira_api_token'] !== '••••••••••••••••') { $jiraToken = fociz_decrypt($existingSettings['jira_api_token']); } $accountName = $existingSettings['account_name'] ?? ''; $accountId = $existingSettings['account_id'] ?? ''; if (!empty($jiraDomain) && !empty($jiraEmail) && !empty($jiraToken)) { $testRes = jira_test_connection($jiraDomain, $jiraEmail, $jiraToken); if (!$testRes['success']) { $message = "Jira Verbindingsfout: " . $testRes['error']; $messageType = 'error'; } else { $accountName = $testRes['user']['displayName'] ?? 'Jira Gebruiker'; $accountId = $testRes['user']['accountId'] ?? ''; $message = "Verbinding met Jira geslaagd! Ingelogd als: {$accountName}"; $messageType = 'success'; } } else { $message = "Jira instellingen opgeslagen."; $messageType = 'success'; } $newSettings = [ 'jira_domain' => $jiraDomain, 'jira_email' => $jiraEmail, 'jira_api_token' => fociz_encrypt($jiraToken), 'account_name' => $accountName, 'account_id' => $accountId, ]; $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 is_demo_mode = 0, settings = VALUES(settings), last_sync_at = NOW() "); $stmt->execute([$userId, json_encode($newSettings)]); unset($_SESSION["jira_issues_user_{$userId}"]); } // 4. Jira ontkoppelen elseif ($action === 'disconnect_jira') { jira_disconnect($userId); header('Location: /profile.php?success=' . urlencode('Atlassian Jira is succesvol ontkoppeld.')); exit; } // 4b. Persoonlijke Google Gemini / AI instellingen opslaan elseif ($action === 'save_personal_ai') { $aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0; $aiProvider = trim($_POST['ai_provider'] ?? 'gemini'); $aiModel = trim($_POST['ai_model'] ?? 'gemini-2.0-flash'); $apiKeyIn = trim($_POST['ai_api_key'] ?? ''); // Haal eventueel bestaande integratie op $stmtEx = $db->prepare("SELECT id, access_token, settings FROM integrations WHERE user_id = ? AND provider IN ('GEMINI', 'AI') LIMIT 1"); $stmtEx->execute([$userId]); $existing = $stmtEx->fetch(); $tokenToSave = $existing['access_token'] ?? null; if ($apiKeyIn !== '' && $apiKeyIn !== '••••••••••••••••') { $tokenToSave = fociz_encrypt($apiKeyIn); } $settingsJson = json_encode([ 'provider' => $aiProvider, 'model' => $aiModel, ]); if ($existing) { $stmtUp = $db->prepare("UPDATE integrations SET provider = 'GEMINI', is_enabled = ?, access_token = ?, settings = ?, last_sync_at = NOW() WHERE id = ?"); $stmtUp->execute([$aiEnabled, $tokenToSave, $settingsJson, $existing['id']]); } else { $stmtIn = $db->prepare("INSERT INTO integrations (user_id, provider, is_enabled, access_token, settings, last_sync_at) VALUES (?, 'GEMINI', ?, ?, ?, NOW())"); $stmtIn->execute([$userId, $aiEnabled, $tokenToSave, $settingsJson]); } $message = "Uw persoonlijke AI instellingen zijn succesvol opgeslagen."; $messageType = 'success'; } // 4c. Persoonlijke AI sleutel ontkoppelen / verwijderen elseif ($action === 'disconnect_personal_ai') { $stmtDel = $db->prepare("DELETE FROM integrations WHERE user_id = ? AND provider IN ('GEMINI', 'AI')"); $stmtDel->execute([$userId]); $message = "Uw persoonlijke AI sleutel is ontkoppeld. Fociz gebruikt nu de centrale instellingen van de organisatie."; $messageType = 'success'; } // 5. Notificatievoorkeuren opslaan elseif ($action === 'save_notifications') { $settings = [ 'enabled' => isset($_POST['notif_enabled']) ? 1 : 0, 'lead_time_minutes' => (int)($_POST['lead_time_minutes'] ?? 5), 'repeat_count' => (int)($_POST['repeat_count'] ?? 2), 'repeat_interval_minutes' => (int)($_POST['repeat_interval_minutes'] ?? 2), 'sound_enabled' => isset($_POST['sound_enabled']) ? 1 : 0, ]; update_user_notification_settings($userId, $settings); $message = "Uw persoonlijke notificatievoorkeuren zijn succesvol opgeslagen."; $messageType = 'success'; } // 6. Profielnaam bijwerken elseif ($action === 'update_profile') { $name = trim($_POST['name'] ?? ''); if (strlen($name) < 2) { $message = "Voer een geldige naam in van minimaal 2 tekens."; $messageType = 'error'; } else { $stmt = $db->prepare("UPDATE users SET name = ? WHERE id = ?"); $stmt->execute([$name, $userId]); $_SESSION['user_name'] = $name; $user['name'] = $name; $message = "Uw profielnaam is succesvol bijgewerkt."; $messageType = 'success'; } } // 7. Wachtwoord wijzigen elseif ($action === 'change_password') { $currentPassword = $_POST['current_password'] ?? ''; $newPassword = $_POST['new_password'] ?? ''; $confirmPassword = $_POST['confirm_password'] ?? ''; $stmtU = $db->prepare("SELECT password_hash FROM users WHERE id = ?"); $stmtU->execute([$userId]); $storedHash = $stmtU->fetchColumn(); if (!password_verify($currentPassword, $storedHash)) { $message = "Uw huidige wachtwoord is niet correct ingevoerd."; $messageType = 'error'; } elseif (($pwErr = fociz_validate_password($newPassword)) !== null) { $message = $pwErr; $messageType = 'error'; } elseif ($newPassword !== $confirmPassword) { $message = "Het nieuwe wachtwoord en de bevestiging komen niet overeen."; $messageType = 'error'; $newHash = password_hash($newPassword, PASSWORD_DEFAULT); $stmtUp = $db->prepare("UPDATE users SET password_hash = ? WHERE id = ?"); $stmtUp->execute([$newHash, $userId]); // SOC 2 CC6.1 / ISO 27001 A.9.4.2: Trek overige sessies in bij wachtwoordwijziging fociz_revoke_other_sessions($userId, session_id()); fociz_log_audit($userId, 'PASSWORD_CHANGED', 'user', $userId, ['method' => 'profile'], 'SUCCESS', $user['email']); $message = "Uw wachtwoord is succesvol gewijzigd. Alle overige sessies op andere apparaten zijn uit veiligheidsoverwegingen beëindigd."; $messageType = 'success'; } } // 7b. Alle overige sessies op afstand beëindigen (SOC 2 / ISO 27001) elseif ($action === 'revoke_other_sessions') { $count = fociz_revoke_other_sessions($userId, session_id()); $message = "Succesvol {$count} andere actieve sessie(s) op afstand beëindigd."; $messageType = 'success'; } // 8. Standaard avatar kiezen uit bibliotheek elseif ($action === 'select_preset_avatar') { $preset = trim($_POST['avatar_preset'] ?? ''); if (preg_match('/^\/assets\/avatars\/avatar-(0[1-9]|1[0-9]|2[0-4])\.svg$/', $preset)) { $stmtUp = $db->prepare("UPDATE users SET avatar_url = ? WHERE id = ?"); $stmtUp->execute([$preset, $userId]); $_SESSION['user_avatar_url'] = $preset; $user['avatar_url'] = $preset; $message = "Uw avatar is succesvol bijgewerkt!"; $messageType = 'success'; } else { $message = "Ongeldige avatar geselecteerd."; $messageType = 'error'; } } // 9. Eigen profielfoto uploaden elseif ($action === 'upload_custom_avatar') { if (!isset($_FILES['avatar_file']) || $_FILES['avatar_file']['error'] !== UPLOAD_ERR_OK) { $message = "Selecteer een geldige afbeelding om te uploaden."; $messageType = 'error'; } else { $file = $_FILES['avatar_file']; $maxSize = 4 * 1024 * 1024; // 4MB if ($file['size'] > $maxSize) { $message = "De afbeelding is te groot (maximaal 4MB toegestaan)."; $messageType = 'error'; } else { $processed = fociz_process_avatar_upload($file, $userId); if (!$processed['success']) { $message = $processed['error'] ?? "Fout bij verwerken van de afbeelding."; $messageType = 'error'; } else { $avatarUri = $processed['data_uri']; $stmtUp = $db->prepare("UPDATE users SET avatar_url = ? WHERE id = ?"); $stmtUp->execute([$avatarUri, $userId]); $_SESSION['user_avatar_url'] = $avatarUri; $user['avatar_url'] = $avatarUri; $message = "Uw profielfoto is succesvol geüpload en permanent bewaard!"; $messageType = 'success'; } } } } // 10. Avatar verwijderen elseif ($action === 'remove_avatar') { $stmtUp = $db->prepare("UPDATE users SET avatar_url = NULL WHERE id = ?"); $stmtUp->execute([$userId]); unset($_SESSION['user_avatar_url']); $user['avatar_url'] = null; $message = "Uw avatar is teruggezet naar de standaard weergave met uw initialen."; $messageType = 'success'; } } } // Actuele gegevens ophalen $stmtUser = $db->prepare("SELECT * FROM users WHERE id = ?"); $stmtUser->execute([$userId]); $userProfile = $stmtUser->fetch(); if ($userProfile) { $userProfile['avatar_url'] = fociz_get_valid_avatar_url($userProfile['avatar_url'] ?? null, $userId); } // Outlook status ophalen $stmtOutlook = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'"); $stmtOutlook->execute([$userId]); $outlookRow = $stmtOutlook->fetch(); $isOutlookConnected = !empty($outlookRow['access_token']) && empty($outlookRow['is_demo_mode']); $outlookSettings = json_decode($outlookRow['settings'] ?? '{}', true) ?: []; // Jira status ophalen $stmtJira = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'JIRA'"); $stmtJira->execute([$userId]); $jiraRow = $stmtJira->fetch(); $jiraSettings = json_decode($jiraRow['settings'] ?? '{}', true) ?: []; $isJiraConnected = !empty($jiraSettings['jira_api_token']); $companyJiraDomain = jira_get_company_domain(); $activeJiraDomain = !empty($jiraSettings['jira_domain']) ? $jiraSettings['jira_domain'] : $companyJiraDomain; // Centrale Azure App configuratie status $appConfig = microsoft_get_app_config(); // Notificatie-instellingen $notifSettings = get_user_notification_settings($userId); // AI Co-Pilot & Google Gemini instellingen voor gebruiker $userAiSettings = get_ai_settings($userId); $stmtAiInt = $db->prepare("SELECT id, access_token, is_enabled, settings FROM integrations WHERE user_id = ? AND provider IN ('GEMINI', 'AI') LIMIT 1"); $stmtAiInt->execute([$userId]); $userAiIntRow = $stmtAiInt->fetch(); $hasPersonalAiKey = !empty($userAiIntRow['access_token']); $userAiRowSettings = json_decode($userAiIntRow['settings'] ?? '{}', true) ?: []; // Thema ophalen $userTheme = get_user_theme($userId); // Actieve apparaten & sessies ophalen (SOC 2 / ISO 27001) $activeSessions = fociz_get_user_active_sessions($userId, session_id()); $pageTitle = 'Mijn Profiel & Koppelingen — Fociz'; require_once __DIR__ . '/includes/header.php'; ?>
Avatar

Beheerder Collega 2FA Beveiligd

• Lid sinds

Weergave & Thema

Actief:

Kies uw gewenste uiterlijk: rustig donker grafiet, kraakhelder licht of synchroniseer met uw apparaat.

Profielfoto & Avatar

Kies een mini-avatar uit de 24 stijlvolle karakters of upload een eigen afbeelding.

Kies een Karakter (24 Stijlen)

'avatar-01.svg', 'cat' => 'pro', 'name' => 'Alex (Business)'], ['file' => 'avatar-02.svg', 'cat' => 'pro', 'name' => 'Sophie (Corporate)'], ['file' => 'avatar-03.svg', 'cat' => 'pro', 'name' => 'Liam (Consultant)'], ['file' => 'avatar-04.svg', 'cat' => 'pro', 'name' => 'Emma (Manager)'], ['file' => 'avatar-05.svg', 'cat' => 'pro', 'name' => 'Daan (Director)'], ['file' => 'avatar-06.svg', 'cat' => 'pro', 'name' => 'Noa (Executive)'], ['file' => 'avatar-07.svg', 'cat' => 'creative', 'name' => 'Mila (Designer)'], ['file' => 'avatar-08.svg', 'cat' => 'creative', 'name' => 'Lucas (Creator)'], ['file' => 'avatar-09.svg', 'cat' => 'creative', 'name' => 'Zara (Artist)'], ['file' => 'avatar-10.svg', 'cat' => 'creative', 'name' => 'Finn (Illustrator)'], ['file' => 'avatar-11.svg', 'cat' => 'creative', 'name' => 'Lotte (Writer)'], ['file' => 'avatar-12.svg', 'cat' => 'creative', 'name' => 'Sem (Architect)'], ['file' => 'avatar-13.svg', 'cat' => 'tech', 'name' => 'Bram (Developer)'], ['file' => 'avatar-14.svg', 'cat' => 'tech', 'name' => 'Julia (Engineer)'], ['file' => 'avatar-15.svg', 'cat' => 'tech', 'name' => 'Jesse (DevOps)'], ['file' => 'avatar-16.svg', 'cat' => 'tech', 'name' => 'Tess (Data Scientist)'], ['file' => 'avatar-17.svg', 'cat' => 'tech', 'name' => 'Milan (Cyber Sec)'], ['file' => 'avatar-18.svg', 'cat' => 'tech', 'name' => 'Liv (AI Engineer)'], ['file' => 'avatar-19.svg', 'cat' => 'heroes', 'name' => 'Max (Ninja)'], ['file' => 'avatar-20.svg', 'cat' => 'heroes', 'name' => 'Sara (Astronaut)'], ['file' => 'avatar-21.svg', 'cat' => 'heroes', 'name' => 'Robin (Pilot)'], ['file' => 'avatar-22.svg', 'cat' => 'heroes', 'name' => 'Eva (Explorer)'], ['file' => 'avatar-23.svg', 'cat' => 'heroes', 'name' => 'Leo (Cosmic)'], ['file' => 'avatar-24.svg', 'cat' => 'heroes', 'name' => 'Nova (Cyberpunk)'], ]; foreach ($avatarPresets as $av): $avatarPath = "/assets/avatars/{$av['file']}"; $isSelected = ($userProfile['avatar_url'] === $avatarPath); ?>

Eigen Foto Uploaden

JPG, PNG, WebP of GIF (max. 4MB)

Mijn Microsoft 365 (Agenda & E-mail)

Live Verbonden Niet Gekoppeld

Synchroniseer uw agenda-afspraken en zakelijke Outlook e-mails met Fociz

Gekoppeld Microsoft Account
Microsoft Teams Koppeling Automatische Meeting Links Vergaderlinks in uw agenda worden direct klikbaar in uw dagplanning

Bepaalt hoe vaak het dagplanningscherm in de achtergrond controleert op gewijzigde of verwijderde Outlook afspraken.

Uw agenda synchroniseert live. Afspraken van vandaag verschijnen direct in uw Dagplanning.
Naar Dagplanning →

Verbind uw Microsoft Outlook Agenda

Krijg uw dagelijkse afspraken automatisch te zien in Fociz en ontvang herinneringen vóórdat uw vergaderingen beginnen.

U wordt veilig doorgestuurd naar Microsoft om in te loggen
Microsoft 365 koppeling is nog niet geconfigureerd door de beheerder. Stel Azure in via Systeembeheer → Vraag uw beheerder om dit in te schakelen.

Mijn Atlassian Jira Koppeling

Verbonden Niet Gekoppeld

Importeer uw toegewezen Jira issues direct naar uw dagelijkse takenlijst

Standaard ingevuld door beheerder indien beschikbaar
API Token Aanmaken
Verbonden als: Verbinding wordt automatisch gevalideerd bij het opslaan

AI Co-Pilot & Google Gemini Pro

Eigen Sleutel Organisatiesleutel Actief Geen Sleutel

Koppel uw eigen Google Gemini Pro account of gebruik de centrale organisatieconfiguratie

Zero-Data Leakage Actief
100% Zero-Data Leakage Garantie

Uw gevoelige gegevens uit Jira tickets, Outlook e-mails en Microsoft Teams worden strikt afgeschermd en NOOIT naar Google Gemini of OpenAI verstuurd.

Gratis sleutel aanmaken? Google AI Studio →

Mijn Meldingen & Agenda Herinneringen

Krijg realtime meldingen vanuit uw browser zodra een afspraak nadert

Toestemming van Browser Vereist Klik op de knop om pushmeldingen in te schakelen voor Fociz.
Meldingen Actief Automatisch waarschuwen bij naderende afspraken
Subtiel Geluidssignaal Speel een zachte chime af bij binnenkomst van de herinnering

Tweestapsverificatie (2FA) is Actief

Beveiligd

Uw Fociz account is optimaal beveiligd met een Authenticator app (TOTP) voor maximale privacy- en dataveiligheid.

Verplicht & Actief

Persoonlijke Gegevens

E-mailadres is gekoppeld aan uw account en 2FA.

Wachtwoord Wijzigen

Minimaal 12 tekens (incl. hoofdletter, kleine letter, cijfer en speciaal teken).

Actieve Apparaten & Sessies

Centraal overzicht van alle actieve inlogapparaten conform ISO 27001 en SOC 2 richtlijnen (maximaal 20 actieve sessies).

1): ?>
Dit apparaat (Huidig)
• Laatst actief: