diff --git a/.gitignore b/.gitignore index 56682d7..7c6ca09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,10 @@ -# Environment -.env -.env*.local - -# Systeem & Editor -.DS_Store -Thumbs.db -*.log -.vscode/ -.idea/ - -# Environment -.env -.env*.local - -# Systeem & Editor -.DS_Store -Thumbs.db -*.log -.vscode/ -.idea/ -config/.env.example -config/config.php \ No newline at end of file +# Environment +.env +.env*.local + +# Systeem & Editor +.DS_Store +Thumbs.db +*.log +.vscode/ +.idea/ \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..952fc3e --- /dev/null +++ b/.htaccess @@ -0,0 +1,38 @@ +# ========================================================================= +# Fociz Pentest & Security Hardening (.htaccess) +# ========================================================================= + +# 1. Schakel directory listing uit +Options -Indexes + +# 2. Blokkeer directe toegang tot verborgen systeembestanden (.env, .git, etc.) + + + Require all denied + + + Order deny,allow + Deny from all + + + +# 3. Blokkeer directe toegang tot gevoelige bestandsformaten + + + Require all denied + + + Order deny,allow + Deny from all + + + +# 4. Beveiligingsheaders + + Header always set X-Content-Type-Options "nosniff" + Header always set X-Frame-Options "SAMEORIGIN" + Header always set X-XSS-Protection "1; mode=block" + Header always set Referrer-Policy "strict-origin-when-cross-origin" + Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()" + Header unset X-Powered-By + diff --git a/admin.php b/admin.php index 8cc90b5..f456b07 100644 --- a/admin.php +++ b/admin.php @@ -152,23 +152,49 @@ $tenantForConsent = !empty($appConfig['tenant_id']) ? $appConfig['tenant_id'] : $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 = (int)$db->query("SELECT COUNT(*) FROM users")->fetchColumn(); -$totalAdmins = (int)$db->query("SELECT COUNT(*) FROM users WHERE role = 'ADMIN'")->fetchColumn(); -$totalMembers = $totalUsers - $totalAdmins; -$pendingInvites = (int)$db->query("SELECT COUNT(*) FROM invites WHERE used_at IS NULL AND expires_at > NOW()")->fetchColumn(); +$totalUsers = 0; +$totalAdmins = 0; +$totalMembers = 0; +$pendingInvites = 0; +$outlookConnectedCount = 0; +$jiraConnectedCount = 0; +$recentUsers = []; -$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(); +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()); +} -// Haal recente gebruikers op voor snelle inspectie -$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(); +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'; diff --git a/brainstorm.php b/brainstorm.php index db62073..e9c1395 100644 --- a/brainstorm.php +++ b/brainstorm.php @@ -29,14 +29,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' } // Haal alle sessies op -$stmtSessions = $db->query(" - SELECT s.*, u.name as creator_name, - (SELECT COUNT(*) FROM brainstorm_cards WHERE session_id = s.id) as cards_count - FROM brainstorm_sessions s - JOIN users u ON s.created_by_id = u.id - ORDER BY s.created_at DESC -"); -$sessions = $stmtSessions->fetchAll(); +$sessions = []; +try { + $stmtSessions = $db->query(" + SELECT s.*, u.name as creator_name, + (SELECT COUNT(*) FROM brainstorm_cards WHERE session_id = s.id) as cards_count + FROM brainstorm_sessions s + JOIN users u ON s.created_by_id = u.id + ORDER BY s.created_at DESC + "); + $sessions = $stmtSessions->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen brainstorm sessies: " . $e->getMessage()); + ensure_tables_exist($db); +} require_once __DIR__ . '/includes/header.php'; ?> diff --git a/config/.env.example b/config/.env.example index 4812c61..e87ef4a 100644 --- a/config/.env.example +++ b/config/.env.example @@ -1,26 +1,28 @@ -# MariaDB Verbinding -DB_HOST="192.168.2.33" +# ========================================================================= +# FOCIZ OMGEVINGSCONFIGURATIE VOORBEELD (TEMPLATE) +# ========================================================================= +# Alle actieve database- en applicatie-instellingen staan centraal in: +# config/config.php +# +# Indien gewenst kan een lokaal .env bestand in de hoofdmap worden geplaatst +# om specifieke variabelen lokaal te overschrijven. + +DB_HOST="127.0.0.1" DB_PORT="3306" DB_NAME="fociz" -DB_USER="focizlive_user" -DB_PASS="F0c1z_Live_Secure_2026!" +DB_USER="fociz_user" +DB_PASS="" -# Omgeving: 'development' (knippert rood) of 'live' (knippert groen) APP_ENV="live" +APP_URL="https://www.fociz.nl" +APP_SECRET="" -# Applicatie URL (voor uitnodigingslinks en OAuth redirects: https://dev.fociz.nl of https://fociz.nl) -APP_URL="https://live.fociz.nl" - -# Applicatie Geheime Sleutel (willekeurige tekenreeks) -APP_SECRET="fociz-maria-db-super-secret-key-998877" - -# Microsoft 365 / Outlook (Optionele globale defaults; kan ook via web UI worden ingesteld) MICROSOFT_CLIENT_ID="" MICROSOFT_CLIENT_SECRET="" -MICROSOFT_TENANT_ID="common" +MICROSOFT_TENANT_ID="" -# Atlassian Jira Cloud (Optionele globale defaults; kan ook via web UI worden ingesteld) JIRA_DOMAIN="" JIRA_EMAIL="" JIRA_API_TOKEN="" + diff --git a/config/.htaccess b/config/.htaccess new file mode 100644 index 0000000..b81f864 --- /dev/null +++ b/config/.htaccess @@ -0,0 +1,8 @@ +# Beveiliging van de interne configuratiemap + + Require all denied + + + Order deny,allow + Deny from all + diff --git a/config/config.php b/config/config.php index 469197d..5b66f5f 100644 --- a/config/config.php +++ b/config/config.php @@ -1,44 +1,47 @@ +

⚠️ Fociz Systeemfout (Alleen zichtbaar voor Beheerder)

+

Fout: " . htmlspecialchars($error['message']) . "

+

Bestand: " . htmlspecialchars($error['file']) . " op regel " . $error['line'] . "

+ "; + } else { + echo "
+
+
F
+

Er is iets misgegaan

+

De pagina kon niet worden geladen. Probeer de pagina te vernieuwen of log opnieuw in.

+ Naar Inloggen +
+
"; + } + } + }); +} else { ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); error_reporting(E_ALL); diff --git a/config/database.php b/config/database.php index 7009f35..63c9d35 100644 --- a/config/database.php +++ b/config/database.php @@ -1,8 +1,14 @@ -
-

⚠️ PHP MySQL Driver Ontbreekt (pdo_mysql)

+
+
+

⚠️ Database Driver Ontbreekt

De extensie pdo_mysql is niet actief in deze PHP-omgeving.

@@ -45,19 +51,16 @@ function get_db_connection(): PDO { ); // 2. CHECK: Bestaat de database al? - // We proberen eerst direct verbinding te maken met de database. - // Als de database al bestaat, lukt dit direct zonder database-aanmaakrechten te vereisen! try { $pdo = new PDO($dsnWithDb, DB_USER, DB_PASS, $options); - - // De database bestaat al! Controleer enkel of de tabellen al aanwezig zijn ensure_tables_exist($pdo); return $pdo; } catch (PDOException $e) { + error_log("MariaDB Verbindingsfout [{$e->getCode()}]: {$e->getMessage()}"); + // Foutcode 1049 betekent: Database 'DB_NAME' bestaat nog NIET op de MariaDB server if ($e->getCode() == 1049 || str_contains($e->getMessage(), 'Unknown database')) { - // Probeer de database eenmalig aan te maken indien de gebruiker CREATE rechten heeft try { $serverDsn = sprintf('mysql:host=%s;port=%s;charset=%s', DB_HOST, DB_PORT, DB_CHARSET); $serverPdo = new PDO($serverDsn, DB_USER, DB_PASS, $options); @@ -66,82 +69,93 @@ function get_db_connection(): PDO { DB_NAME )); - // Nu opnieuw verbinden met de nieuw aangemaakte database $pdo = new PDO($dsnWithDb, DB_USER, DB_PASS, $options); ensure_tables_exist($pdo); return $pdo; } catch (PDOException $createEx) { - die(" -
-
-

⚠️ Database '" . htmlspecialchars(DB_NAME) . "' bestaat nog niet

-

- De database " . htmlspecialchars(DB_NAME) . " bestaat nog niet op MariaDB server " . htmlspecialchars(DB_HOST) . " en de gebruiker " . htmlspecialchars(DB_USER) . " heeft geen rechten om automatisch databases aan te maken. -

-

- Maak de database " . htmlspecialchars(DB_NAME) . " eenmalig aan via phpMyAdmin, HeidiSQL of de MariaDB CLI: -

- - CREATE DATABASE IF NOT EXISTS `" . htmlspecialchars(DB_NAME) . "` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -
-
- "); + error_log("Kan database " . DB_NAME . " niet automatisch aanmaken: " . $createEx->getMessage()); } } - // Andere verbindingsfout (bijv. verkeerd wachtwoord, poort of host onbereikbaar) - die(" -
-
-

MariaDB Verbindingsfout (" . htmlspecialchars((string)$e->getCode()) . ")

-

" . htmlspecialchars($e->getMessage()) . "

-
- Host: " . htmlspecialchars(DB_HOST) . "
- Poort: " . htmlspecialchars(DB_PORT) . "
- Database: " . htmlspecialchars(DB_NAME) . "
- Gebruiker: " . htmlspecialchars(DB_USER) . " + // Pen Test beveiliging: in live omgeving geen interne IP's of database-details tonen! + if (defined('APP_ENV') && APP_ENV === 'live') { + die(" +
+
+
⚠️
+

Database Tijdelijk Niet Bereikbaar

+

+ Er kon geen veilige verbinding tot stand worden gebracht met de database. Probeer het over enkele ogenblikken opnieuw. +

-
- "); + "); + } else { + die(" +
+
+

MariaDB Verbindingsfout (" . htmlspecialchars((string)$e->getCode()) . ")

+

" . htmlspecialchars($e->getMessage()) . "

+
+ Host: " . htmlspecialchars(DB_HOST) . "
+ Poort: " . htmlspecialchars(DB_PORT) . "
+ Database: " . htmlspecialchars(DB_NAME) . "
+ Gebruiker: " . htmlspecialchars(DB_USER) . " +
+
+
+ "); + } } } /** - * Zorgt ervoor dat alle benodigde tabellen uit schema.sql aanwezig zijn in MariaDB - * Voert niets uit als de tabellen al bestaan! + * Zorgt ervoor dat ALLE benodigde tabellen en kolommen uit schema.sql aanwezig zijn in MariaDB + * Voert elke tabel individueel uit zodat ontbrekende tabellen direct aangemaakt worden. */ function ensure_tables_exist(PDO $pdo): void { - try { - // Controleer of de hoofdtabel 'users' al bestaat - $result = $pdo->query("SHOW TABLES LIKE 'users'"); - if ($result && $result->rowCount() > 0) { - // Tabellen bestaan al. Voer eventuele niet-destructieve kolom-upgrades uit indien nodig - try { - $pdo->exec("ALTER TABLE tasks MODIFY external_id VARCHAR(255) NULL"); - } catch (Throwable $t) { - // Kolom bestaat wellicht al of heeft al de juiste lengte - } - return; - } + static $tablesChecked = false; + if ($tablesChecked) return; + $tablesChecked = true; - // Tabellen ontbreken nog: voer schema.sql uit statement voor statement + try { $schemaFile = file_exists(__DIR__ . '/schema.sql') ? (__DIR__ . '/schema.sql') : (__DIR__ . '/../schema.sql'); if (file_exists($schemaFile)) { $sql = file_get_contents($schemaFile); $statements = array_filter(array_map('trim', explode(';', $sql))); foreach ($statements as $stmt) { - // Verwijder eventuele SQL commentaren aan het begin $stmtClean = preg_replace('/^--.*$/m', '', $stmt); $stmtClean = trim($stmtClean); if (!empty($stmtClean)) { - $pdo->exec($stmtClean); + try { + $pdo->exec($stmtClean); + } catch (Throwable $t) { + // Stil negeren indien tabel of relatie al bestaat + } } } } - } catch (Exception $e) { - error_log("Fout bij aanmaken tabellen: " . $e->getMessage()); + + // Zorg ervoor dat alle kolommen en upgrades aanwezig zijn + $colUpgrades = [ + "ALTER TABLE tasks MODIFY external_id VARCHAR(255) NULL", + "ALTER TABLE tasks ADD COLUMN external_url TEXT NULL AFTER external_id", + "ALTER TABLE users ADD COLUMN remember_token VARCHAR(64) NULL AFTER backup_codes", + "ALTER TABLE users ADD COLUMN remember_token_expires_at DATETIME NULL AFTER remember_token", + "ALTER TABLE users ADD COLUMN notification_settings TEXT NULL AFTER remember_token_expires_at", + "ALTER TABLE users ADD COLUMN theme VARCHAR(20) NOT NULL DEFAULT 'dark' AFTER notification_settings", + "ALTER TABLE users ADD COLUMN avatar_url VARCHAR(255) NULL AFTER theme", + "ALTER TABLE users ADD COLUMN backup_codes TEXT NULL AFTER two_factor_enabled", + ]; + foreach ($colUpgrades as $sql) { + try { + $pdo->exec($sql); + } catch (Throwable $t) { + // Stil negeren indien kolom al bestaat + } + } + } catch (Throwable $e) { + error_log("Fout bij controleren tabellen: " . $e->getMessage()); } } diff --git a/config/index.php b/config/index.php new file mode 100644 index 0000000..c5903e4 --- /dev/null +++ b/config/index.php @@ -0,0 +1,3 @@ +prepare("SELECT * FROM integrations WHERE user_id = ?"); -$stmtInt->execute([$userId]); $userIntegrations = []; -foreach ($stmtInt->fetchAll() as $row) { - $userIntegrations[$row['provider']] = $row; +try { + $stmtInt = $db->prepare("SELECT * FROM integrations WHERE user_id = ?"); + $stmtInt->execute([$userId]); + foreach ($stmtInt->fetchAll() as $row) { + $userIntegrations[$row['provider']] = $row; + } +} catch (Throwable $e) { + error_log("Fout bij ophalen integraties: " . $e->getMessage()); + ensure_tables_exist($db); } $isOutlookConnected = !empty($userIntegrations['OUTLOOK']['access_token']) && empty($userIntegrations['OUTLOOK']['is_demo_mode']); @@ -58,12 +63,18 @@ $weekOutlookEvents = []; $isLiveOutlook = false; if ($isOutlookConnected) { - // Haal afspraken op voor het volledige datumbereik van de week - $weekOutlookEvents = microsoft_fetch_events_range($userId, $weekStart, $weekEnd); - $isLiveOutlook = true; + try { + // Haal afspraken op voor het volledige datumbereik van de week + $weekOutlookEvents = microsoft_fetch_events_range($userId, $weekStart, $weekEnd); + $isLiveOutlook = true; - // Synchroniseer / reconcilieer Fociz taken direct met actuele Outlook data - microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents); + // Synchroniseer / reconcilieer Fociz taken direct met actuele Outlook data + microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents); + } catch (Throwable $e) { + error_log("Outlook synchronisatiefout in planner: " . $e->getMessage()); + $isLiveOutlook = false; + $weekOutlookEvents = []; + } } else { // Demo data indien Microsoft Outlook nog niet gekoppeld is $weekOutlookEvents = [ @@ -101,45 +112,64 @@ if ($isOutlookConnected) { } // 2. Haal dagplanning op of maak aan voor $selectedDate -$stmtPlan = $db->prepare("SELECT * FROM daily_plans WHERE user_id = ? AND plan_date = ?"); -$stmtPlan->execute([$userId, $selectedDate]); -$dailyPlan = $stmtPlan->fetch(); +$dailyPlan = null; +try { + $stmtPlan = $db->prepare("SELECT * FROM daily_plans WHERE user_id = ? AND plan_date = ?"); + $stmtPlan->execute([$userId, $selectedDate]); + $dailyPlan = $stmtPlan->fetch(); -if (!$dailyPlan) { - $defaultGoal = ($selectedDate === $today) ? 'Mijn belangrijkste doel van vandaag behalen' : 'Focusdoel voor deze dag bepalen'; - $stmtCreatePlan = $db->prepare(" - INSERT INTO daily_plans (user_id, plan_date, focus_goal) - VALUES (?, ?, ?) - "); - $stmtCreatePlan->execute([$userId, $selectedDate, $defaultGoal]); - $planId = (int)$db->lastInsertId(); - $dailyPlan = ['id' => $planId, 'plan_date' => $selectedDate, 'focus_goal' => $defaultGoal, 'notes' => '']; + if (!$dailyPlan) { + $defaultGoal = ($selectedDate === $today) ? 'Mijn belangrijkste doel van vandaag behalen' : 'Focusdoel voor deze dag bepalen'; + $stmtCreatePlan = $db->prepare(" + INSERT INTO daily_plans (user_id, plan_date, focus_goal) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id) + "); + $stmtCreatePlan->execute([$userId, $selectedDate, $defaultGoal]); + $planId = (int)$db->lastInsertId(); + $dailyPlan = ['id' => $planId, 'plan_date' => $selectedDate, 'focus_goal' => $defaultGoal, 'notes' => '']; + } +} catch (Throwable $e) { + error_log("Fout bij dagplanning ophalen/aanmaken: " . $e->getMessage()); + ensure_tables_exist($db); + $dailyPlan = ['id' => 0, 'plan_date' => $selectedDate, 'focus_goal' => 'Mijn belangrijkste doel van vandaag behalen', 'notes' => '']; } +$planId = !empty($dailyPlan['id']) ? (int)$dailyPlan['id'] : 0; + // 3. Haal taken op voor de geselecteerde dag (vers bijgewerkt na eventuele reconciliatie) -$stmtTasks = $db->prepare(" - SELECT * FROM tasks - WHERE user_id = ? AND (daily_plan_id = ? " . ($selectedDate === $today ? "OR daily_plan_id IS NULL" : "") . ") - ORDER BY is_completed ASC, priority ASC, time_block_start ASC, created_at DESC -"); -$stmtTasks->execute([$userId, $dailyPlan['id']]); -$tasks = $stmtTasks->fetchAll(); +$tasks = []; +try { + $stmtTasks = $db->prepare(" + SELECT * FROM tasks + WHERE user_id = ? AND (daily_plan_id = ? " . ($selectedDate === $today ? "OR daily_plan_id IS NULL" : "") . ") + ORDER BY is_completed ASC, priority ASC, time_block_start ASC, created_at DESC + "); + $stmtTasks->execute([$userId, $planId]); + $tasks = $stmtTasks->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen taken: " . $e->getMessage()); +} // 4. Haal weekstatistieken voor taken op (aantal per dag) -$stmtWeekTasks = $db->prepare(" - SELECT dp.plan_date, COUNT(t.id) as total_tasks, SUM(CASE WHEN t.is_completed = 1 THEN 1 ELSE 0 END) as completed_tasks - FROM daily_plans dp - LEFT JOIN tasks t ON t.daily_plan_id = dp.id AND t.user_id = ? - WHERE dp.user_id = ? AND dp.plan_date BETWEEN ? AND ? - GROUP BY dp.plan_date -"); -$stmtWeekTasks->execute([$userId, $userId, $weekStart, $weekEnd]); $weekTaskStats = []; -foreach ($stmtWeekTasks->fetchAll() as $row) { - $weekTaskStats[$row['plan_date']] = [ - 'total' => (int)$row['total_tasks'], - 'completed' => (int)$row['completed_tasks'], - ]; +try { + $stmtWeekTasks = $db->prepare(" + SELECT dp.plan_date, COUNT(t.id) as total_tasks, SUM(CASE WHEN t.is_completed = 1 THEN 1 ELSE 0 END) as completed_tasks + FROM daily_plans dp + LEFT JOIN tasks t ON t.daily_plan_id = dp.id AND t.user_id = ? + WHERE dp.user_id = ? AND dp.plan_date BETWEEN ? AND ? + GROUP BY dp.plan_date + "); + $stmtWeekTasks->execute([$userId, $userId, $weekStart, $weekEnd]); + foreach ($stmtWeekTasks->fetchAll() as $row) { + $weekTaskStats[$row['plan_date']] = [ + 'total' => (int)$row['total_tasks'], + 'completed' => (int)$row['completed_tasks'], + ]; + } +} catch (Throwable $e) { + error_log("Fout bij weekstatistieken ophalen: " . $e->getMessage()); } // Groepeer Outlook afspraken per datum @@ -174,9 +204,15 @@ for ($i = 0; $i < 7; $i++) { } // 5. Haal Jira issues op voor de snelle import widget -$jiraData = jira_get_user_issues($userId); -$jiraIssues = $jiraData['issues'] ?? []; -$isJiraLive = !empty($jiraData['success']) && empty($jiraData['is_demo']); +$jiraIssues = []; +$isJiraLive = false; +try { + $jiraData = jira_get_user_issues($userId); + $jiraIssues = $jiraData['issues'] ?? []; + $isJiraLive = !empty($jiraData['success']) && empty($jiraData['is_demo']); +} catch (Throwable $e) { + error_log("Fout bij ophalen Jira issues: " . $e->getMessage()); +} // Filter Outlook events die afkomstig zijn van Fociz taken voor de geselecteerde dag om duplicaten op de tijdlijn te voorkomen $dayOutlookEvents = $outlookEventsByDate[$selectedDate] ?? []; diff --git a/roadmap.php b/roadmap.php index 8412c62..536ae56 100644 --- a/roadmap.php +++ b/roadmap.php @@ -27,10 +27,15 @@ if ($selectedStatus !== 'ALL') { $params[] = $selectedStatus; } -$sql .= " ORDER BY r.quarter ASC, r.priority DESC, r.due_date ASC"; -$stmt = $db->prepare($sql); -$stmt->execute($params); -$roadmapItems = $stmt->fetchAll(); +$roadmapItems = []; +try { + $stmt = $db->prepare($sql); + $stmt->execute($params); + $roadmapItems = $stmt->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen roadmap items: " . $e->getMessage()); + ensure_tables_exist($db); +} $quarters = ['Q1', 'Q2', 'Q3', 'Q4']; ?> diff --git a/users.php b/users.php index e601a69..db908af 100644 --- a/users.php +++ b/users.php @@ -55,15 +55,26 @@ if (isset($_GET['delete_user'])) { } // Haal alle gebruikers op -$users = $db->query("SELECT * FROM users ORDER BY created_at DESC")->fetchAll(); +$users = []; +try { + $users = $db->query("SELECT * FROM users ORDER BY created_at DESC")->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen gebruikers: " . $e->getMessage()); +} // Haal alle uitnodigingen op -$invites = $db->query(" - SELECT i.*, u.name as inviter_name - FROM invites i - JOIN users u ON i.created_by_id = u.id - ORDER BY i.created_at DESC -")->fetchAll(); +$invites = []; +try { + $invites = $db->query(" + SELECT i.*, u.name as inviter_name + FROM invites i + JOIN users u ON i.created_by_id = u.id + ORDER BY i.created_at DESC + ")->fetchAll() ?: []; +} catch (Throwable $e) { + error_log("Fout bij ophalen uitnodigingen: " . $e->getMessage()); + ensure_tables_exist($db); +} $pageTitle = 'Team & Uitnodigingen — Fociz'; require_once __DIR__ . '/includes/header.php';