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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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="= htmlspecialchars($config['config_value']) ?>"
pattern="^#[0-9A-Fa-f]{6}$"
placeholder="#000000"
+ title="6-stelliger Hex-Farbcode (z.B. #ff0000)"
oninput="updateColorPicker('= htmlspecialchars($config['config_key']) ?>', 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('= htmlspecialchars($config['config_key']) ?>', 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:
+
 ?>)
+
+