From 28a71b2f785374ee4e9260add93b725ad53fff77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:46:15 +0000 Subject: [PATCH 01/10] Initial plan From e403454e8250ac0d86aa13465e7dd5b6d06ea29b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:58:02 +0000 Subject: [PATCH 02/10] Add database-driven configuration management system Co-authored-by: itshypax <33388336+itshypax@users.noreply.github.com> --- assets/config/config.php | 61 ++-- ...lter_intra_notifications_type_04112025.php | 12 + .../database/create_intra_config_04112025.php | 27 ++ .../insert_intra_config_defaults_04112025.php | 59 ++++ auth/callback.php | 17 + settings/system/config.php | 299 ++++++++++++++++++ setup/database-init.php | 3 + src/Config/ConfigManager.php | 221 +++++++++++++ src/Notifications/NotificationManager.php | 4 +- 9 files changed, 678 insertions(+), 25 deletions(-) create mode 100644 assets/database/alter_intra_notifications_type_04112025.php create mode 100644 assets/database/create_intra_config_04112025.php create mode 100644 assets/database/insert_intra_config_defaults_04112025.php create mode 100644 settings/system/config.php create mode 100644 src/Config/ConfigManager.php diff --git a/assets/config/config.php b/assets/config/config.php index 89c77bac..70eb2aa3 100644 --- a/assets/config/config.php +++ b/assets/config/config.php @@ -1,6 +1,7 @@ loadAndDefineConfig(); +} catch (Exception $e) { + // Fallback to default values if database is not available or table doesn't exist + error_log("Could not load config from database: " . $e->getMessage()); + + // BASIS DATEN - Fallback defaults + if (!defined('API_KEY')) define('API_KEY', 'CHANGE_ME'); + if (!defined('SYSTEM_NAME')) define('SYSTEM_NAME', 'intraRP'); + if (!defined('SYSTEM_COLOR')) define('SYSTEM_COLOR', '#d10000'); + if (!defined('SYSTEM_URL')) define('SYSTEM_URL', 'CHANGE_ME'); + if (!defined('SYSTEM_LOGO')) define('SYSTEM_LOGO', '/assets/img/defaultLogo.webp'); + if (!defined('META_IMAGE_URL')) define('META_IMAGE_URL', ''); + + // SERVER DATEN + if (!defined('SERVER_NAME')) define('SERVER_NAME', 'CHANGE_ME'); + if (!defined('SERVER_CITY')) define('SERVER_CITY', 'Musterstadt'); + + // RP DATEN + if (!defined('RP_ORGTYPE')) define('RP_ORGTYPE', 'Berufsfeuerwehr'); + if (!defined('RP_STREET')) define('RP_STREET', 'Musterweg 0815'); + if (!defined('RP_ZIP')) define('RP_ZIP', '1337'); + + // FUNKTIONEN + if (!defined('CHAR_ID')) define('CHAR_ID', true); + if (!defined('ENOTF_PREREG')) define('ENOTF_PREREG', true); + if (!defined('ENOTF_USE_PIN')) define('ENOTF_USE_PIN', true); + if (!defined('ENOTF_PIN')) define('ENOTF_PIN', '1234'); + if (!defined('ENOTF_REQUIRE_USER_AUTH')) define('ENOTF_REQUIRE_USER_AUTH', false); + if (!defined('REGISTRATION_MODE')) define('REGISTRATION_MODE', 'open'); + if (!defined('LANG')) define('LANG', 'de'); + if (!defined('BASE_PATH')) define('BASE_PATH', '/'); +} \ No newline at end of file diff --git a/assets/database/alter_intra_notifications_type_04112025.php b/assets/database/alter_intra_notifications_type_04112025.php new file mode 100644 index 00000000..fb6ea544 --- /dev/null +++ b/assets/database/alter_intra_notifications_type_04112025.php @@ -0,0 +1,12 @@ +exec($sql); +} catch (PDOException $e) { + $message = $e->getMessage(); + echo $message; +} diff --git a/assets/database/create_intra_config_04112025.php b/assets/database/create_intra_config_04112025.php new file mode 100644 index 00000000..60776ba5 --- /dev/null +++ b/assets/database/create_intra_config_04112025.php @@ -0,0 +1,27 @@ +exec($sql); +} catch (PDOException $e) { + $message = $e->getMessage(); + echo $message; +} diff --git a/assets/database/insert_intra_config_defaults_04112025.php b/assets/database/insert_intra_config_defaults_04112025.php new file mode 100644 index 00000000..e9076641 --- /dev/null +++ b/assets/database/insert_intra_config_defaults_04112025.php @@ -0,0 +1,59 @@ + 'API_KEY', 'value' => 'CHANGE_ME', 'type' => 'string', 'category' => 'basis', 'description' => 'API-Schlüssel für externe Schnittstellen', 'editable' => 0, 'order' => 1], + ['key' => 'SYSTEM_NAME', 'value' => 'intraRP', 'type' => 'string', 'category' => 'basis', 'description' => 'Eigenname des Intranets', 'editable' => 1, 'order' => 2], + ['key' => 'SYSTEM_COLOR', 'value' => '#d10000', 'type' => 'color', 'category' => 'basis', 'description' => 'Hauptfarbe des Systems', 'editable' => 1, 'order' => 3], + ['key' => 'SYSTEM_URL', 'value' => 'CHANGE_ME', 'type' => 'url', 'category' => 'basis', 'description' => 'Domain des Systems', 'editable' => 1, 'order' => 4], + ['key' => 'SYSTEM_LOGO', 'value' => '/assets/img/defaultLogo.webp', 'type' => 'url', 'category' => 'basis', 'description' => 'Ort des Logos (relativer Pfad oder Link)', 'editable' => 1, 'order' => 5], + ['key' => 'META_IMAGE_URL', 'value' => '', 'type' => 'url', 'category' => 'basis', 'description' => 'Bild für Link-Vorschau (als Link angeben)', 'editable' => 1, 'order' => 6], + + // SERVER DATEN + ['key' => 'SERVER_NAME', 'value' => 'CHANGE_ME', 'type' => 'string', 'category' => 'server', 'description' => 'Name des Servers', 'editable' => 1, 'order' => 10], + ['key' => 'SERVER_CITY', 'value' => 'Musterstadt', 'type' => 'string', 'category' => 'server', 'description' => 'Name der Stadt in welcher der Server spielt', 'editable' => 1, 'order' => 11], + + // RP DATEN + ['key' => 'RP_ORGTYPE', 'value' => 'Berufsfeuerwehr', 'type' => 'string', 'category' => 'rp', 'description' => 'Art/Name der Organisation', 'editable' => 1, 'order' => 20], + ['key' => 'RP_STREET', 'value' => 'Musterweg 0815', 'type' => 'string', 'category' => 'rp', 'description' => 'Straße der Organisation', 'editable' => 1, 'order' => 21], + ['key' => 'RP_ZIP', 'value' => '1337', 'type' => 'string', 'category' => 'rp', 'description' => 'PLZ der Organisation', 'editable' => 1, 'order' => 22], + + // FUNKTIONEN + ['key' => 'CHAR_ID', 'value' => 'true', 'type' => 'boolean', 'category' => 'funktionen', 'description' => 'Wird eine eindeutige Charakter-ID verwendet?', 'editable' => 1, 'order' => 30], + ['key' => 'ENOTF_PREREG', 'value' => 'true', 'type' => 'boolean', 'category' => 'funktionen', 'description' => 'Wird das Voranmeldungssystem des eNOTF verwendet?', 'editable' => 1, 'order' => 31], + ['key' => 'ENOTF_USE_PIN', 'value' => 'true', 'type' => 'boolean', 'category' => 'funktionen', 'description' => 'Wird die PIN-Funktion des eNOTF verwendet?', 'editable' => 1, 'order' => 32], + ['key' => 'ENOTF_PIN', 'value' => '1234', 'type' => 'string', 'category' => 'funktionen', 'description' => 'PIN für den Zugang zum eNOTF (4-6 Zahlen)', 'editable' => 1, 'order' => 33], + ['key' => 'ENOTF_REQUIRE_USER_AUTH', 'value' => 'false', 'type' => 'boolean', 'category' => 'funktionen', 'description' => 'Wird eine Registrierung/Anmeldung im Hauptsystem für den Zugang zum eNOTF vorausgesetzt?', 'editable' => 1, 'order' => 34], + ['key' => 'REGISTRATION_MODE', 'value' => 'open', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Registrierungsmodus: open = für jeden möglich, code = nur mit Code, closed = keine Registrierung', 'editable' => 1, 'order' => 35], + ['key' => 'LANG', 'value' => 'de', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Sprache des Systems (de = Deutsch, en = Englisch)', 'editable' => 1, 'order' => 36], + ['key' => 'BASE_PATH', 'value' => '/', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Basis-Pfad des Systems (z.B. /intraRP/)', 'editable' => 1, 'order' => 37], + ]; + + $stmt = $pdo->prepare(" + INSERT INTO intra_config (config_key, config_value, config_type, category, description, is_editable, display_order) + VALUES (:key, :value, :type, :category, :description, :editable, :order) + ON DUPLICATE KEY UPDATE + config_value = VALUES(config_value), + config_type = VALUES(config_type), + category = VALUES(category), + description = VALUES(description), + is_editable = VALUES(is_editable), + display_order = VALUES(display_order) + "); + + foreach ($configs as $config) { + $stmt->execute([ + 'key' => $config['key'], + 'value' => $config['value'], + 'type' => $config['type'], + 'category' => $config['category'], + 'description' => $config['description'], + 'editable' => $config['editable'], + 'order' => $config['order'] + ]); + } +} catch (PDOException $e) { + $message = $e->getMessage(); + echo $message; +} diff --git a/auth/callback.php b/auth/callback.php index f13800e2..73270d49 100644 --- a/auth/callback.php +++ b/auth/callback.php @@ -5,6 +5,7 @@ use League\OAuth2\Client\Provider\GenericProvider; use App\Helpers\ProtocolDetection; +use App\Notifications\NotificationManager; ini_set('display_errors', 1); ini_set('display_startup_errors', 1); @@ -114,6 +115,22 @@ 'role' => $adminRole['id'], 'full_admin' => 1 ]); + + $firstUserId = $pdo->lastInsertId(); + + // Send notification to first user about configuration + try { + $notificationManager = new NotificationManager($pdo); + $notificationManager->create( + $firstUserId, + 'system', + 'Willkommen bei intraRP!', + 'Als erster Benutzer haben Sie Administratorrechte. Bitte besuchen Sie die System-Konfiguration, um wichtige Einstellungen wie den Systemnamen, Logo und weitere Optionen anzupassen.', + BASE_PATH . 'settings/system/config.php' + ); + } catch (Exception $e) { + error_log("Failed to create first user notification: " . $e->getMessage()); + } } $stmt = $pdo->prepare("SELECT * FROM intra_users WHERE discord_id = :discord_id"); diff --git a/settings/system/config.php b/settings/system/config.php new file mode 100644 index 00000000..0ed3216e --- /dev/null +++ b/settings/system/config.php @@ -0,0 +1,299 @@ + $value) { + if ($key === 'save_config') continue; + + // Get old value for audit log + $oldValue = $configManager->get($key); + + // Handle boolean values + if (strpos($key, 'CHAR_ID') !== false || + strpos($key, 'ENOTF_') !== false && strpos($key, 'PIN') === false || + strpos($key, 'REQUIRE_USER_AUTH') !== false) { + $value = isset($_POST[$key]) && $_POST[$key] === 'on' ? 'true' : 'false'; + } + + // Only update if value changed + if ($oldValue != $value) { + $updates[$key] = $value; + $changes[] = [ + 'key' => $key, + 'old' => $oldValue, + 'new' => $value + ]; + } + } + + if (!empty($updates)) { + $result = $configManager->updateMultiple($updates, $_SESSION['userid']); + + if ($result['success']) { + // Log each change in audit log + foreach ($changes as $change) { + $auditLogger->log( + $_SESSION['userid'], + 'config_update', + json_encode([ + 'key' => $change['key'], + 'old_value' => $change['old'], + 'new_value' => $change['new'] + ]), + 'System' + ); + } + + Flash::set('success', 'Konfiguration erfolgreich aktualisiert.'); + } else { + Flash::set('error', 'Fehler beim Aktualisieren der Konfiguration.'); + } + } else { + Flash::set('info', 'Keine Änderungen vorgenommen.'); + } + + header("Location: " . $_SERVER['PHP_SELF']); + exit(); +} + +$configByCategory = $configManager->getConfigByCategory(); +?> + + + + + + + + + + + +
+
+
+
+
+
+

System-Konfiguration

+
+ + +
+ $configs): ?> +
+
+
+
getCategoryDisplayName($category)) ?>
+
+
+ + +
+ + + +
+ + > +
+ + +
+ + +
+
+
Wählen Sie eine Farbe aus oder geben Sie einen Hex-Farbcode ein.
+ + + +
Relativer Pfad oder vollständige URL zum Logo.
+
+ Vorschau:
+ Logo Preview +
+ + + +
+ + + + +
+ + +
+
+
+ + +
+ +
+
+
+
+
+
+ + + + + + diff --git a/setup/database-init.php b/setup/database-init.php index efe5fae1..23294451 100644 --- a/setup/database-init.php +++ b/setup/database-init.php @@ -220,6 +220,9 @@ function isTransactionActive(PDO $pdo): bool // 04.11.2025 ['file' => 'alter_intra_mitarbeiter_log_04112025.php', 'type' => 'alter'], + ['file' => 'create_intra_config_04112025.php', 'type' => 'create'], + ['file' => 'insert_intra_config_defaults_04112025.php', 'type' => 'insert'], + ['file' => 'alter_intra_notifications_type_04112025.php', 'type' => 'alter'], ]; $executed = 0; diff --git a/src/Config/ConfigManager.php b/src/Config/ConfigManager.php new file mode 100644 index 00000000..088c96ec --- /dev/null +++ b/src/Config/ConfigManager.php @@ -0,0 +1,221 @@ +pdo = $pdo; + } + + /** + * Load all configuration values from database and define them as constants + * This maintains backward compatibility with existing code using define() + */ + public function loadAndDefineConfig(): void + { + $configs = $this->getAllConfig(); + + foreach ($configs as $config) { + $key = $config['config_key']; + $value = $config['config_value']; + $type = $config['config_type']; + + // Convert value based on type + $definedValue = $this->convertValue($value, $type); + + // Define constant if not already defined + if (!defined($key)) { + define($key, $definedValue); + } + } + } + + /** + * Get all configuration values from database + * + * @return array Array of configuration records + */ + public function getAllConfig(): array + { + // Use cache if available + if (self::$configCache !== null) { + return self::$configCache; + } + + try { + $stmt = $this->pdo->query(" + SELECT * FROM intra_config + ORDER BY display_order ASC, config_key ASC + "); + self::$configCache = $stmt->fetchAll(PDO::FETCH_ASSOC); + return self::$configCache; + } catch (PDOException $e) { + error_log("Failed to load config: " . $e->getMessage()); + return []; + } + } + + /** + * Get configuration values grouped by category + * + * @return array Array grouped by category + */ + public function getConfigByCategory(): array + { + $configs = $this->getAllConfig(); + $grouped = []; + + foreach ($configs as $config) { + $category = $config['category']; + if (!isset($grouped[$category])) { + $grouped[$category] = []; + } + $grouped[$category][] = $config; + } + + return $grouped; + } + + /** + * Get a single configuration value + * + * @param string $key Configuration key + * @return mixed|null Configuration value or null if not found + */ + public function get(string $key) + { + try { + $stmt = $this->pdo->prepare(" + SELECT config_value, config_type FROM intra_config + WHERE config_key = ? + "); + $stmt->execute([$key]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result) { + return $this->convertValue($result['config_value'], $result['config_type']); + } + + return null; + } catch (PDOException $e) { + error_log("Failed to get config value: " . $e->getMessage()); + return null; + } + } + + /** + * Update a configuration value + * + * @param string $key Configuration key + * @param mixed $value New value + * @param int|null $userId User ID making the change + * @return bool Success status + */ + public function update(string $key, $value, ?int $userId = null): bool + { + try { + // Clear cache + self::$configCache = null; + + $stmt = $this->pdo->prepare(" + UPDATE intra_config + SET config_value = ?, updated_by = ?, updated_at = NOW() + WHERE config_key = ? AND is_editable = 1 + "); + + return $stmt->execute([$value, $userId, $key]); + } catch (PDOException $e) { + error_log("Failed to update config value: " . $e->getMessage()); + return false; + } + } + + /** + * Update multiple configuration values at once + * + * @param array $updates Array of key => value pairs + * @param int|null $userId User ID making the changes + * @return array Array with success status and list of failed keys + */ + public function updateMultiple(array $updates, ?int $userId = null): array + { + $failed = []; + $updated = []; + + $this->pdo->beginTransaction(); + + try { + foreach ($updates as $key => $value) { + if ($this->update($key, $value, $userId)) { + $updated[] = $key; + } else { + $failed[] = $key; + } + } + + if (empty($failed)) { + $this->pdo->commit(); + return ['success' => true, 'updated' => $updated, 'failed' => []]; + } else { + $this->pdo->rollBack(); + return ['success' => false, 'updated' => [], 'failed' => array_keys($updates)]; + } + } catch (PDOException $e) { + $this->pdo->rollBack(); + error_log("Failed to update multiple config values: " . $e->getMessage()); + return ['success' => false, 'updated' => [], 'failed' => array_keys($updates)]; + } + } + + /** + * Convert string value to appropriate type + * + * @param string $value String value from database + * @param string $type Type specification + * @return mixed Converted value + */ + private function convertValue(?string $value, string $type) + { + if ($value === null) { + return null; + } + + switch ($type) { + case 'boolean': + return $value === 'true' || $value === '1' || $value === 'yes'; + case 'integer': + return (int)$value; + case 'color': + case 'url': + case 'string': + default: + return $value; + } + } + + /** + * Get category display name + * + * @param string $category Category key + * @return string Display name + */ + public function getCategoryDisplayName(string $category): string + { + $names = [ + 'basis' => 'Basis Daten', + 'server' => 'Server Daten', + 'rp' => 'RP Daten', + 'funktionen' => 'Funktionen', + ]; + + return $names[$category] ?? ucfirst($category); + } +} diff --git a/src/Notifications/NotificationManager.php b/src/Notifications/NotificationManager.php index 875ea5f9..5c41f502 100644 --- a/src/Notifications/NotificationManager.php +++ b/src/Notifications/NotificationManager.php @@ -17,7 +17,7 @@ public function __construct(PDO $pdo) * Create a new notification for a user * * @param int $userId User ID to notify - * @param string $type Type of notification (antrag, protokoll, dokument) + * @param string $type Type of notification (antrag, protokoll, dokument, system) * @param string $title Notification title * @param string|null $message Optional notification message * @param string|null $link Optional link to related item @@ -26,7 +26,7 @@ public function __construct(PDO $pdo) public function create(int $userId, string $type, string $title, ?string $message = null, ?string $link = null): bool { // Validate notification type - $validTypes = ['antrag', 'protokoll', 'dokument']; + $validTypes = ['antrag', 'protokoll', 'dokument', 'system']; if (!in_array($type, $validTypes)) { error_log("Invalid notification type: {$type}"); return false; From 5e94a39c3336b4eb7befd3cb564b1c2aeb7800b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:00:06 +0000 Subject: [PATCH 03/10] Add navigation link and improve boolean handling in config UI Co-authored-by: itshypax <33388336+itshypax@users.noreply.github.com> --- assets/components/navbar.php | 1 + settings/system/config.php | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/assets/components/navbar.php b/assets/components/navbar.php index 55500518..7204d8b1 100644 --- a/assets/components/navbar.php +++ b/assets/components/navbar.php @@ -102,6 +102,7 @@
  • Dashboard
  • +
  • Konfiguration
  • Updater
  • diff --git a/settings/system/config.php b/settings/system/config.php index 0ed3216e..d3225562 100644 --- a/settings/system/config.php +++ b/settings/system/config.php @@ -29,17 +29,28 @@ $updates = []; $changes = []; - foreach ($_POST as $key => $value) { - if ($key === 'save_config') continue; + // Get all configs to check types + $allConfigs = $configManager->getAllConfig(); + $configTypes = []; + foreach ($allConfigs as $config) { + $configTypes[$config['config_key']] = $config['config_type']; + } + + // Process POST data + foreach ($allConfigs as $config) { + if (!$config['is_editable']) continue; - // Get old value for audit log + $key = $config['config_key']; $oldValue = $configManager->get($key); - // Handle boolean values - if (strpos($key, 'CHAR_ID') !== false || - strpos($key, 'ENOTF_') !== false && strpos($key, 'PIN') === false || - strpos($key, 'REQUIRE_USER_AUTH') !== false) { + // Handle different input types + if ($config['config_type'] === 'boolean') { + // Checkboxes are only in POST when checked $value = isset($_POST[$key]) && $_POST[$key] === 'on' ? 'true' : 'false'; + } else { + // Skip if not in POST + if (!isset($_POST[$key])) continue; + $value = $_POST[$key]; } // Only update if value changed From d4f4b724c98de42b21cc9cc2635b62bc7e54a8b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:02:25 +0000 Subject: [PATCH 04/10] Address code review feedback - improve cache handling and comparison logic Co-authored-by: itshypax <33388336+itshypax@users.noreply.github.com> --- .../database/insert_intra_config_defaults_04112025.php | 1 - settings/system/config.php | 7 ++++--- src/Config/ConfigManager.php | 10 +++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/assets/database/insert_intra_config_defaults_04112025.php b/assets/database/insert_intra_config_defaults_04112025.php index e9076641..f1a0415d 100644 --- a/assets/database/insert_intra_config_defaults_04112025.php +++ b/assets/database/insert_intra_config_defaults_04112025.php @@ -34,7 +34,6 @@ INSERT INTO intra_config (config_key, config_value, config_type, category, description, is_editable, display_order) VALUES (:key, :value, :type, :category, :description, :editable, :order) ON DUPLICATE KEY UPDATE - config_value = VALUES(config_value), config_type = VALUES(config_type), category = VALUES(category), description = VALUES(description), diff --git a/settings/system/config.php b/settings/system/config.php index d3225562..240d6227 100644 --- a/settings/system/config.php +++ b/settings/system/config.php @@ -45,7 +45,7 @@ // Handle different input types if ($config['config_type'] === 'boolean') { - // Checkboxes are only in POST when checked + // Checkboxes/switches send 'on' when checked, nothing when unchecked $value = isset($_POST[$key]) && $_POST[$key] === 'on' ? 'true' : 'false'; } else { // Skip if not in POST @@ -53,8 +53,8 @@ $value = $_POST[$key]; } - // Only update if value changed - if ($oldValue != $value) { + // Only update if value changed (strict comparison for type safety) + if ($oldValue !== $value) { $updates[$key] = $value; $changes[] = [ 'key' => $key, @@ -220,6 +220,7 @@ class="form-control" value="" pattern="^#[0-9A-Fa-f]{6}$" placeholder="#000000" + title="6-stelliger Hex-Farbcode (z.B. #ff0000)" oninput="updateColorPicker('', this.value)" >
    diff --git a/src/Config/ConfigManager.php b/src/Config/ConfigManager.php index 088c96ec..42dac174 100644 --- a/src/Config/ConfigManager.php +++ b/src/Config/ConfigManager.php @@ -153,8 +153,14 @@ public function updateMultiple(array $updates, ?int $userId = null): array $this->pdo->beginTransaction(); try { + $stmt = $this->pdo->prepare(" + UPDATE intra_config + SET config_value = ?, updated_by = ?, updated_at = NOW() + WHERE config_key = ? AND is_editable = 1 + "); + foreach ($updates as $key => $value) { - if ($this->update($key, $value, $userId)) { + if ($stmt->execute([$value, $userId, $key])) { $updated[] = $key; } else { $failed[] = $key; @@ -163,6 +169,8 @@ public function updateMultiple(array $updates, ?int $userId = null): array if (empty($failed)) { $this->pdo->commit(); + // Clear cache after successful commit + self::$configCache = null; return ['success' => true, 'updated' => $updated, 'failed' => []]; } else { $this->pdo->rollBack(); From 4ea6a686dc9f1122b37d203f295d42d0aceef894 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:21:35 +0000 Subject: [PATCH 05/10] Fix UI issues: add META_IMAGE preview, fix preview backgrounds, remove duplicate color preview, remove LANG field Co-authored-by: itshypax <33388336+itshypax@users.noreply.github.com> --- assets/config/config.php | 1 - .../insert_intra_config_defaults_04112025.php | 3 +- .../database/remove_lang_config_04112025.php | 12 +++++ settings/system/config.php | 46 ++++++++++++------- setup/database-init.php | 1 + 5 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 assets/database/remove_lang_config_04112025.php diff --git a/assets/config/config.php b/assets/config/config.php index 70eb2aa3..8391bcaa 100644 --- a/assets/config/config.php +++ b/assets/config/config.php @@ -44,6 +44,5 @@ if (!defined('ENOTF_PIN')) define('ENOTF_PIN', '1234'); if (!defined('ENOTF_REQUIRE_USER_AUTH')) define('ENOTF_REQUIRE_USER_AUTH', false); if (!defined('REGISTRATION_MODE')) define('REGISTRATION_MODE', 'open'); - if (!defined('LANG')) define('LANG', 'de'); if (!defined('BASE_PATH')) define('BASE_PATH', '/'); } \ No newline at end of file diff --git a/assets/database/insert_intra_config_defaults_04112025.php b/assets/database/insert_intra_config_defaults_04112025.php index f1a0415d..b867b318 100644 --- a/assets/database/insert_intra_config_defaults_04112025.php +++ b/assets/database/insert_intra_config_defaults_04112025.php @@ -26,8 +26,7 @@ ['key' => 'ENOTF_PIN', 'value' => '1234', 'type' => 'string', 'category' => 'funktionen', 'description' => 'PIN für den Zugang zum eNOTF (4-6 Zahlen)', 'editable' => 1, 'order' => 33], ['key' => 'ENOTF_REQUIRE_USER_AUTH', 'value' => 'false', 'type' => 'boolean', 'category' => 'funktionen', 'description' => 'Wird eine Registrierung/Anmeldung im Hauptsystem für den Zugang zum eNOTF vorausgesetzt?', 'editable' => 1, 'order' => 34], ['key' => 'REGISTRATION_MODE', 'value' => 'open', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Registrierungsmodus: open = für jeden möglich, code = nur mit Code, closed = keine Registrierung', 'editable' => 1, 'order' => 35], - ['key' => 'LANG', 'value' => 'de', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Sprache des Systems (de = Deutsch, en = Englisch)', 'editable' => 1, 'order' => 36], - ['key' => 'BASE_PATH', 'value' => '/', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Basis-Pfad des Systems (z.B. /intraRP/)', 'editable' => 1, 'order' => 37], + ['key' => 'BASE_PATH', 'value' => '/', 'type' => 'string', 'category' => 'funktionen', 'description' => 'Basis-Pfad des Systems (z.B. /intraRP/)', 'editable' => 1, 'order' => 36], ]; $stmt = $pdo->prepare(" diff --git a/assets/database/remove_lang_config_04112025.php b/assets/database/remove_lang_config_04112025.php new file mode 100644 index 00000000..1b9f979c --- /dev/null +++ b/assets/database/remove_lang_config_04112025.php @@ -0,0 +1,12 @@ +exec($sql); +} catch (PDOException $e) { + $message = $e->getMessage(); + echo $message; +} diff --git a/settings/system/config.php b/settings/system/config.php index 240d6227..635ac4fb 100644 --- a/settings/system/config.php +++ b/settings/system/config.php @@ -111,26 +111,17 @@ border-radius: 0.375rem; padding: 1rem; margin-top: 0.5rem; - background: #f8f9fa; + background: #212529; } - .color-preview { - display: inline-block; - width: 50px; - height: 50px; - border-radius: 0.375rem; - border: 2px solid #dee2e6; - vertical-align: middle; - margin-left: 10px; - } - - .logo-preview { + .logo-preview, + .meta-image-preview { max-width: 200px; max-height: 100px; border: 1px solid #dee2e6; border-radius: 0.375rem; padding: 0.5rem; - background: white; + background: #2b3035; } .color-input-wrapper { @@ -223,7 +214,6 @@ class="form-control" title="6-stelliger Hex-Farbcode (z.B. #ff0000)" oninput="updateColorPicker('', this.value)" > -
    Wählen Sie eine Farbe aus oder geben Sie einen Hex-Farbcode ein.
    @@ -248,6 +238,27 @@ class="logo-preview" > + + +
    Vollständige URL zum Bild für Link-Vorschau.
    +
    + Vorschau:
    + Meta Image Preview +
    +