diff --git a/.github/workflows/update-version.yml b/.github/workflows/update-version.yml index 5d6b23ea..95d39dff 100644 --- a/.github/workflows/update-version.yml +++ b/.github/workflows/update-version.yml @@ -30,7 +30,7 @@ jobs: TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S') # Erstelle version.json - cat > admin/system/updates/version.json << EOF + cat > system/updates/version.json << EOF { "version": "$VERSION", "updated_at": "$TIMESTAMP", @@ -40,14 +40,14 @@ jobs: EOF echo "Created version.json with version $VERSION" - cat admin/system/updates/version.json + cat system/updates/version.json - name: Commit and push version.json run: | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" - git add admin/system/updates/version.json + git add system/updates/version.json # Prüfen ob es Änderungen gibt if git diff --staged --quiet; then diff --git a/.gitignore b/.gitignore index 30b4d926..161de7aa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ storage/documents/**.pdf support_errors.log *.backup .vscode +.idea \ No newline at end of file diff --git a/.htaccess b/.htaccess index dcb154da..c47f6b15 100644 --- a/.htaccess +++ b/.htaccess @@ -23,8 +23,8 @@ RewriteRule ^dokumente/urkunden/entlassung/(\d+)$ dokumente/urkunden/entlassung. RewriteRule ^dokumente/zertifikate/ausbildung/(\d+)$ dokumente/zertifikate/ausbildung.php?dok=$1 [L] RewriteRule ^dokumente/zertifikate/lehrgang/(\d+)$ dokumente/zertifikate/lehrgang.php?dok=$1 [L] RewriteRule ^dokumente/zertifikate/fachlehrgang/(\d+)$ dokumente/zertifikate/fachlehrgang.php?dok=$1 [L] -# RewriteRule ^admin/antraege/antrag(\d+)$ admin/antraege/t-view.php?antrag=$1 [L] -# RewriteRule ^admin/enotf/divi(\d+)$ admin/enotf/view.php?id=$1 [L] -# RewriteRule ^admin/users/user(\d+)$ admin/users/edit.php?id=$1 [L] +# RewriteRule ^antraege/antrag(\d+)$ antraege/t-view.php?antrag=$1 [L] +# RewriteRule ^enotf/divi(\d+)$ enotf/view.php?id=$1 [L] +# RewriteRule ^users/user(\d+)$ users/edit.php?id=$1 [L] RewriteRule ^enotf/([\w-]+)$ enotf/protokoll.php?enr=$1 [L] -# RewriteRule ^admin/personal/(\d+)$ admin/personal/profile.php?id=$1 [L] \ No newline at end of file +# RewriteRule ^personal/(\d+)$ personal/profile.php?id=$1 [L] \ No newline at end of file diff --git a/README.md b/README.md index 0138c601..28426294 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ Admininistrative Ansicht ### Benutzte Assets -- [Line Awesome](https://icons8.com/line-awesome) von [Icons8](https://icons8.com/) +- [Font Awesome 7 (Free)](https://fontawesome.com/) - [CKEditor5](https://ckeditor.com/) - Beinhaltete Schriftarten von [Google Fonts](https://fonts.google.com/) - [Chart.js](https://www.chartjs.org/) diff --git a/admin/enotf/delete.php b/admin/enotf/delete.php deleted file mode 100644 index 1f7ad4a2..00000000 --- a/admin/enotf/delete.php +++ /dev/null @@ -1,33 +0,0 @@ -prepare("UPDATE intra_edivi SET hidden = 1, protokoll_status = 4 WHERE id = :id"); -$stmt->bindParam(':id', $id); -$stmt->execute(); - -Flash::set('edivi', 'deleted'); -$auditLogger = new AuditLogger($pdo); -$auditLogger->log($userid, 'Protokoll gelöscht [ID: ' . $id . ']', NULL, 'eNOTF', 1); -header("Location: " . BASE_PATH . "admin/enotf/list.php"); -exit; diff --git a/admin/index.php b/admin/index.php deleted file mode 100644 index b1d1efa9..00000000 --- a/admin/index.php +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - Administration › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - - - " /> - - - - - - - -
- - - -
-
-
-
-

Dashboard

- - -
-
- prepare(" - SELECT cirs_status, COUNT(*) as count - FROM intra_antraege - GROUP BY cirs_status - "); - $stmt->execute(); - $result3 = $stmt->fetchAll(PDO::FETCH_ASSOC); - - $data3 = []; - $labels3 = []; - - $antragStatus = [ - 0 => "in Bearbeitung", - 1 => "Abgelehnt", - 2 => "Aufgeschoben", - 3 => "Angenommen", - ]; - - if (!empty($result3)) { - foreach ($result3 as $row) { - $status = $row['cirs_status']; - $rankLabel = isset($antragStatus[$status]) ? $antragStatus[$status] : 'Unbekannt'; - - $data3[] = $row['count']; - $labels3[] = $rankLabel; - } - } - - ?> - - - -
-
- - 'Ungesehen', - 1 => 'in Prüfung', - 2 => 'Freigegeben', - 3 => 'Ungenügend', - ]; - - $stmt = $pdo->prepare("SELECT protokoll_status, COUNT(*) as count FROM intra_edivi WHERE protokoll_status <> 4 GROUP BY protokoll_status"); - $stmt->execute(); - $result2 = $stmt->fetchAll(PDO::FETCH_ASSOC); - - $data2 = []; - $labels2 = []; - - if (!empty($result2)) { - foreach ($result2 as $row) { - $status = $row['protokoll_status']; - $statusLabel = isset($statusMapping[$status]) ? $statusMapping[$status] : 'Unbekannt'; - - $data2[] = $row['count']; - $labels2[] = $statusLabel; - } - } - ?> - - -
-
-
-
-
-
-

Eigene Dokumente

- -
-
-
-
-
-
-

Eigene Anträge

-
- -
- -
-
-
-
-

Eigene eNOTF-Protokolle

- -
-
-
-
- - - - - \ No newline at end of file diff --git a/admin/login.php b/admin/login.php deleted file mode 100644 index d702721b..00000000 --- a/admin/login.php +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - Login › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - " /> - - - -
- -
- -
-
-
- intraRP Logo -
-

-

Das Intranet der Stadt !

- -
- Login -
-
-
-
-

Hintergrundvideo: Rosenbauer Group: "Alles für diesen Moment. - Rosenbauer" (YouTube)
- © intraRP | Alle Rechte vorbehalten

-
- - - \ No newline at end of file diff --git a/admin/logout.php b/admin/logout.php deleted file mode 100644 index bafc3753..00000000 --- a/admin/logout.php +++ /dev/null @@ -1,8 +0,0 @@ -pdo = $pdo; - $this->githubRepo = $githubRepo; - $this->currentVersion = $currentVersion ?: $this->getCurrentVersion(); - $this->githubToken = $githubToken; - - $this->updateDir = __DIR__ . '/temp_update'; - $this->backupDir = __DIR__ . '/backups'; - - $appRoot = dirname(__DIR__, 3); - $this->log('UpdateManager initialisiert:'); - $this->log('- UpdateManager Pfad: ' . __DIR__); - $this->log('- App Root: ' . $appRoot); - $this->log('- Update Dir: ' . $this->updateDir); - $this->log('- Backup Dir: ' . $this->backupDir); - $this->log('- Aktuelle Version: ' . $this->currentVersion); - - $this->ensureDirectories(); - } - - private function ensureDirectories() - { - $directories = [ - $this->updateDir, - $this->backupDir - ]; - - foreach ($directories as $dir) { - if (!is_dir($dir)) { - if (!mkdir($dir, 0755, true)) { - throw new Exception("Verzeichnis konnte nicht erstellt werden: $dir"); - } - $this->log("Verzeichnis erstellt: $dir"); - } - - if (!is_writable($dir)) { - throw new Exception("Verzeichnis ist nicht beschreibbar: $dir"); - } - } - } - - private function getCurrentVersion() - { - $versionFile = __DIR__ . '/version.json'; - if (file_exists($versionFile)) { - $version = json_decode(file_get_contents($versionFile), true); - return $version['version'] ?? '1.0.0'; - } - - try { - $stmt = $this->pdo->prepare("SELECT value FROM system_settings WHERE setting_key = 'system_version'"); - $stmt->execute(); - $result = $stmt->fetch(PDO::FETCH_ASSOC); - return $result['value'] ?? '1.0.0'; - } catch (Exception $e) { - return '1.0.0'; - } - } - - public function checkForUpdates() - { - try { - $headers = ['User-Agent: Update-Manager']; - if ($this->githubToken) { - $headers[] = 'Authorization: token ' . $this->githubToken; - } - - $context = stream_context_create([ - 'http' => [ - 'header' => implode("\r\n", $headers), - 'timeout' => 10 - ] - ]); - - $url = "https://api.github.com/repos/{$this->githubRepo}/releases/latest"; - $response = file_get_contents($url, false, $context); - - if ($response === false) { - throw new Exception('GitHub API nicht erreichbar'); - } - - $release = json_decode($response, true); - - if (!$release || !isset($release['tag_name'])) { - throw new Exception('Ungültige API-Antwort'); - } - - $currentVersion = ltrim($this->currentVersion, 'v'); - $latestVersion = ltrim($release['tag_name'], 'v'); - - return [ - 'has_update' => version_compare($latestVersion, $currentVersion, '>'), - 'latest_version' => $release['tag_name'], - 'current_version' => $this->currentVersion, - 'release_notes' => $release['body'] ?? '', - 'published_at' => $release['published_at'] ?? '', - 'download_url' => $release['zipball_url'] ?? null, - 'debug_comparison' => [ - 'current_normalized' => $currentVersion, - 'latest_normalized' => $latestVersion, - 'comparison_result' => version_compare($latestVersion, $currentVersion, '>') - ] - ]; - } catch (Exception $e) { - return [ - 'error' => true, - 'message' => $e->getMessage(), - 'has_update' => false, - 'current_version' => $this->currentVersion - ]; - } - } - - public function performUpdate() - { - try { - $updateInfo = $this->checkForUpdates(); - - if (!$updateInfo['has_update']) { - throw new Exception('Keine Updates verfügbar'); - } - - $this->log('=== UPDATE GESTARTET ==='); - $this->log('Von Version: ' . $this->currentVersion); - $this->log('Zu Version: ' . $updateInfo['latest_version']); - $this->log('Download URL: ' . $updateInfo['download_url']); - - $this->log('Schritt 1: Backup erstellen...'); - $this->createBackup(); - - $this->log('Schritt 2: Update herunterladen...'); - $updateFile = $this->downloadUpdate($updateInfo['download_url']); - - $this->log('Schritt 3: Update extrahieren...'); - $this->extractUpdate($updateFile); - - $this->log('Schritt 4: Dateien kopieren...'); - $this->copyFiles(); - - $this->log('Schritt 5: Composer Update...'); - try { - $this->runComposerUpdate(); - } catch (Exception $e) { - $this->log('Composer Update Warnung: ' . $e->getMessage()); - } - - $this->log('Schritt 6: Version aktualisieren...'); - $this->updateVersion($updateInfo['latest_version']); - - $this->log('Schritt 7: Aufräumen...'); - $this->cleanup(); - - $this->log('=== UPDATE ERFOLGREICH ABGESCHLOSSEN ==='); - - return [ - 'success' => true, - 'message' => 'Update erfolgreich installiert', - 'new_version' => $updateInfo['latest_version'], - 'previous_version' => $this->currentVersion - ]; - } catch (Exception $e) { - $this->log('=== UPDATE FEHLGESCHLAGEN ==='); - $this->log('Fehler: ' . $e->getMessage()); - $this->log('Stack Trace: ' . $e->getTraceAsString()); - - if (strpos($e->getMessage(), 'Composer') === false) { - $this->rollback(); - } - - return [ - 'success' => false, - 'message' => $e->getMessage(), - 'debug_info' => [ - 'current_version' => $this->currentVersion, - 'error_class' => get_class($e), - 'error_file' => $e->getFile(), - 'error_line' => $e->getLine() - ] - ]; - } - } - - private function checkSystemRequirements() - { - $errors = []; - - if (version_compare(PHP_VERSION, '7.4.0', '<')) { - $errors[] = 'PHP 7.4+ erforderlich, aktuell: ' . PHP_VERSION; - } - - $requiredExtensions = ['zip', 'curl', 'json']; - foreach ($requiredExtensions as $ext) { - if (!extension_loaded($ext)) { - $errors[] = "PHP Extension '$ext' nicht verfügbar"; - } - } - - $rootDir = dirname(__DIR__, 3); - if (!is_writable($rootDir)) { - $errors[] = 'Keine Schreibrechte im Root-Verzeichnis: ' . $rootDir; - } - - $freeSpace = disk_free_space($rootDir); - if ($freeSpace < 100 * 1024 * 1024) { - $errors[] = 'Nicht genügend Speicherplatz (< 100MB verfügbar)'; - } - - if (!empty($errors)) { - throw new Exception('Systemvoraussetzungen nicht erfüllt: ' . implode(', ', $errors)); - } - - $this->log('Systemvoraussetzungen erfüllt'); - } - - private function createBackup() - { - $appRoot = dirname(__DIR__, 3); - $backupName = 'backup_' . $this->currentVersion . '_' . date('Y-m-d_H-i-s'); - $backupPath = $this->backupDir . '/' . $backupName . '.tar.gz'; - - $this->log('Erstelle Backup: ' . $backupName); - $this->log('App Root für Backup: ' . $appRoot); - $this->log('Backup Pfad: ' . $backupPath); - - $excludePatterns = [ - '--exclude=node_modules', - '--exclude=vendor', - '--exclude=*.log', - '--exclude=admin/system/updates/temp_update', - '--exclude=admin/system/updates/backups', - '--exclude=.git', - '--exclude=.github', - '--exclude=storage/logs/*', - '--exclude=storage/cache/*' - ]; - - $command = sprintf( - 'tar %s -czf "%s" -C "%s" .', - implode(' ', $excludePatterns), - $backupPath, - $appRoot - ); - - $this->log('Backup-Befehl: ' . $command); - - $output = []; - $returnCode = 0; - exec($command, $output, $returnCode); - - if ($returnCode !== 0) { - $errorMsg = 'Backup konnte nicht erstellt werden. Return Code: ' . $returnCode; - if (!empty($output)) { - $errorMsg .= '. Output: ' . implode("\n", $output); - } - throw new Exception($errorMsg); - } - - if (!file_exists($backupPath)) { - throw new Exception('Backup-Datei wurde nicht erstellt: ' . $backupPath); - } - - $backupSize = filesize($backupPath); - $this->log('Backup erfolgreich erstellt: ' . $backupName . ' (' . $this->formatBytes($backupSize) . ')'); - - $this->cleanupOldBackups(); - } - - private function cleanupOldBackups() - { - $backupFiles = glob($this->backupDir . '/backup_*.tar.gz'); - - if (count($backupFiles) <= 5) { - return; - } - - usort($backupFiles, function ($a, $b) { - return filemtime($b) - filemtime($a); - }); - - $toDelete = array_slice($backupFiles, 5); - foreach ($toDelete as $file) { - if (unlink($file)) { - $this->log('Altes Backup gelöscht: ' . basename($file)); - } - } - } - - private function formatBytes($size, $precision = 2) - { - $units = ['B', 'KB', 'MB', 'GB']; - $base = log($size, 1024); - return round(pow(1024, $base - floor($base)), $precision) . ' ' . $units[floor($base)]; - } - - private function downloadUpdate($downloadUrl) - { - $headers = ['User-Agent: Update-Manager']; - if ($this->githubToken) { - $headers[] = 'Authorization: token ' . $this->githubToken; - } - - $context = stream_context_create([ - 'http' => [ - 'header' => implode("\r\n", $headers), - 'timeout' => 300 - ] - ]); - - $updateFile = $this->updateDir . '/update.zip'; - $data = file_get_contents($downloadUrl, false, $context); - - if ($data === false) { - throw new Exception('Update konnte nicht heruntergeladen werden'); - } - - file_put_contents($updateFile, $data); - $this->log('Update heruntergeladen: ' . filesize($updateFile) . ' Bytes'); - - return $updateFile; - } - - private function extractUpdate($updateFile) - { - $zip = new ZipArchive(); - $result = $zip->open($updateFile); - - if ($result !== TRUE) { - throw new Exception('Update-Archiv konnte nicht geöffnet werden'); - } - - $extractPath = $this->updateDir . '/extracted'; - $zip->extractTo($extractPath); - $zip->close(); - - $dirs = glob($extractPath . '/*', GLOB_ONLYDIR); - if (empty($dirs)) { - throw new Exception('Ungültiges Update-Archiv'); - } - - $this->updateSourceDir = $dirs[0]; - $this->log('Update extrahiert nach: ' . $this->updateSourceDir); - } - - private function copyFiles() - { - $appRoot = dirname(__DIR__, 3); - - $this->log('App Root: ' . $appRoot); - $this->log('Update Source: ' . $this->updateSourceDir); - - $excludeFiles = [ - 'assets/config/config.php', - 'assets/config/database.php', - 'admin/system/updates/version.json', - '.env', - '.htaccess' - ]; - - $excludeDirs = [ - 'node_modules', - 'vendor', - '.git', - '.github', - 'backups', - 'temp_update', - 'logs' - ]; - - $updatesDir = $appRoot . '/admin/system/updates'; - $excludeDirs[] = 'admin/system/updates/backups'; - $excludeDirs[] = 'admin/system/updates/temp_update'; - - $this->recursiveCopy($this->updateSourceDir, $appRoot, $excludeFiles, $excludeDirs); - $this->log('Dateien erfolgreich kopiert von ' . $this->updateSourceDir . ' nach ' . $appRoot); - } - - private function recursiveCopy($src, $dst, $excludeFiles = [], $excludeDirs = []) - { - if (!is_dir($src)) { - throw new Exception("Quellverzeichnis existiert nicht: $src"); - } - - if (!is_dir($dst)) { - if (!mkdir($dst, 0755, true)) { - throw new Exception("Zielverzeichnis konnte nicht erstellt werden: $dst"); - } - } - - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($src, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ); - - $copiedFiles = 0; - $skippedFiles = 0; - - foreach ($iterator as $item) { - $srcPath = $item->getPathname(); - $relativePath = substr($srcPath, strlen($src) + 1); - $dstPath = $dst . DIRECTORY_SEPARATOR . $relativePath; - - $normalizedPath = str_replace('\\', '/', $relativePath); - - $shouldExclude = false; - - foreach ($excludeFiles as $excludeFile) { - if ($normalizedPath === $excludeFile || basename($normalizedPath) === $excludeFile) { - $shouldExclude = true; - break; - } - } - - if (!$shouldExclude) { - $pathParts = explode('/', $normalizedPath); - foreach ($excludeDirs as $excludeDir) { - if (in_array($excludeDir, $pathParts) || strpos($normalizedPath, $excludeDir . '/') === 0) { - $shouldExclude = true; - break; - } - } - } - - if ($shouldExclude) { - $skippedFiles++; - $this->log("Übersprungen: $normalizedPath"); - continue; - } - - if ($item->isDir()) { - if (!is_dir($dstPath)) { - if (!mkdir($dstPath, 0755, true)) { - throw new Exception("Verzeichnis konnte nicht erstellt werden: $dstPath"); - } - } - } else { - $dstDir = dirname($dstPath); - if (!is_dir($dstDir)) { - if (!mkdir($dstDir, 0755, true)) { - throw new Exception("Zielverzeichnis konnte nicht erstellt werden: $dstDir"); - } - } - - if (!copy($srcPath, $dstPath)) { - throw new Exception("Datei konnte nicht kopiert werden: $srcPath -> $dstPath"); - } - - $copiedFiles++; - - chmod($dstPath, 0644); - - if ($copiedFiles % 100 === 0) { - $this->log("$copiedFiles Dateien kopiert..."); - } - } - } - - $this->log("Kopiervorgang abgeschlossen: $copiedFiles Dateien kopiert, $skippedFiles übersprungen"); - } - - private function runComposerUpdate() - { - $rootDir = dirname(__DIR__, 3); - - if (!file_exists($rootDir . '/composer.json')) { - $this->log('Kein composer.json gefunden - Composer Update übersprungen'); - return; - } - - $originalDir = getcwd(); - chdir($rootDir); - $this->log('Arbeitsverzeichnis geändert nach: ' . getcwd()); - - $envVars = [ - 'COMPOSER_ROOT_VERSION=' . ltrim($this->currentVersion, 'v'), - 'COMPOSER_ALLOW_SUPERUSER=1', - 'COMPOSER_NO_INTERACTION=1', - 'COMPOSER_DISABLE_XDEBUG_WARN=1' - ]; - - $composerCommands = [ - 'composer update --no-dev --optimize-autoloader', - 'php composer.phar update --no-dev --optimize-autoloader', - '/usr/local/bin/composer update --no-dev --optimize-autoloader' - ]; - - $fallbackCommands = [ - 'composer install --no-dev --optimize-autoloader', - 'php composer.phar install --no-dev --optimize-autoloader' - ]; - - if (file_exists($rootDir . '/composer.phar')) { - array_unshift($composerCommands, 'php composer.phar update --no-dev --optimize-autoloader'); - array_unshift($fallbackCommands, 'php composer.phar install --no-dev --optimize-autoloader'); - } - - $success = false; - $lastError = ''; - - try { - foreach ($composerCommands as $command) { - $fullCommand = sprintf( - '%s %s 2>&1', - implode(' ', $envVars), - $command - ); - - $this->log('Versuche Composer UPDATE: ' . $command); - - $output = []; - $returnCode = 0; - exec($fullCommand, $output, $returnCode); - - $outputString = implode("\n", $output); - - if ($this->isComposerSuccessful($outputString, $returnCode)) { - $success = true; - $this->log('✓ Composer UPDATE erfolgreich: ' . $command); - break; - } else { - $this->log('Composer UPDATE fehlgeschlagen: ' . $command); - $lastError = $outputString; - } - } - - if (!$success) { - $this->log('UPDATE fehlgeschlagen, versuche INSTALL als Fallback...'); - - foreach ($fallbackCommands as $command) { - $fullCommand = sprintf( - '%s %s 2>&1', - implode(' ', $envVars), - $command - ); - - $this->log('Versuche Composer INSTALL: ' . $command); - - $output = []; - $returnCode = 0; - exec($fullCommand, $output, $returnCode); - - $outputString = implode("\n", $output); - - if ($this->isComposerSuccessful($outputString, $returnCode)) { - $success = true; - $this->log('✓ Composer INSTALL erfolgreich: ' . $command); - break; - } else { - $lastError = $outputString; - } - } - } - } finally { - chdir($originalDir); - } - - if (!$success) { - if ($this->isVendorDirectoryFunctional($rootDir)) { - $this->log('Composer fehlgeschlagen, aber vendor-Verzeichnis ist funktional'); - return; - } - - throw new Exception('Composer update/install fehlgeschlagen: ' . $lastError); - } - } - - private function isVendorDirectoryFunctional($rootDir) - { - $vendorDir = $rootDir . '/vendor'; - - if (!is_dir($vendorDir)) { - return false; - } - - if (!file_exists($vendorDir . '/autoload.php')) { - return false; - } - - if (!is_dir($vendorDir . '/composer')) { - return false; - } - - return true; - } - - private function isComposerSuccessful($output, $returnCode) - { - $successIndicators = [ - 'Writing lock file', - 'Installing dependencies from lock file', - 'Generating optimized autoload files', - 'Nothing to install, update or remove', - 'Nothing to modify in lock file' - ]; - - $hasSuccess = false; - foreach ($successIndicators as $indicator) { - if (stripos($output, $indicator) !== false) { - $hasSuccess = true; - break; - } - } - - return ($returnCode === 0) || $hasSuccess; - } - - private function updateVersion($newVersion) - { - $versionData = [ - 'version' => $newVersion, - 'updated_at' => date('Y-m-d H:i:s') - ]; - - file_put_contents(__DIR__ . '/version.json', json_encode($versionData, JSON_PRETTY_PRINT)); - - try { - $stmt = $this->pdo->prepare(" - INSERT INTO system_settings (setting_key, value) - VALUES ('system_version', ?) - ON DUPLICATE KEY UPDATE value = ? - "); - $stmt->execute([$newVersion, $newVersion]); - } catch (Exception $e) { - $this->log('Warnung: Version konnte nicht in Datenbank gespeichert werden: ' . $e->getMessage()); - } - - $this->currentVersion = $newVersion; - } - - private function cleanup() - { - $this->deleteDirectory($this->updateDir); - mkdir($this->updateDir, 0755, true); - $this->log('Temporäre Dateien bereinigt'); - } - - private function rollback() - { - $this->log('Rollback würde hier ausgeführt'); - } - - private function deleteDirectory($dir) - { - if (!is_dir($dir)) return; - - $files = array_diff(scandir($dir), ['.', '..']); - foreach ($files as $file) { - $path = $dir . '/' . $file; - is_dir($path) ? $this->deleteDirectory($path) : unlink($path); - } - rmdir($dir); - } - - private function log($message) - { - $logFile = __DIR__ . '/update.log'; - $timestamp = date('Y-m-d H:i:s'); - file_put_contents($logFile, "[$timestamp] $message\n", FILE_APPEND | LOCK_EX); - - try { - $stmt = $this->pdo->prepare("INSERT INTO system_logs (level, message, created_at) VALUES ('info', ?, NOW())"); - $stmt->execute([$message]); - } catch (Exception $e) { - } - } - - public function getUpdateInfo() - { - $updateInfo = $this->checkForUpdates(); - - return [ - 'current_version' => $this->currentVersion, - 'has_update' => $updateInfo['has_update'] ?? false, - 'latest_version' => $updateInfo['latest_version'] ?? null, - 'release_notes' => $updateInfo['release_notes'] ?? '', - 'error' => $updateInfo['error'] ?? false, - 'error_message' => $updateInfo['message'] ?? null - ]; - } - - public function checkRequirements() - { - $requirements = [ - 'php_version' => version_compare(PHP_VERSION, '7.4.0', '>='), - 'zip_extension' => extension_loaded('zip'), - 'curl_extension' => extension_loaded('curl'), - 'writable_root' => is_writable(dirname(__DIR__)), - 'git_available' => $this->isCommandAvailable('git'), - 'composer_available' => $this->isCommandAvailable('composer') || file_exists(dirname(__DIR__) . '/composer.phar') - ]; - - return $requirements; - } - - private function isCommandAvailable($command) - { - $output = shell_exec("which $command 2>/dev/null"); - return !empty($output); - } -} diff --git a/admin/system/updates/index.php b/admin/system/updates/index.php deleted file mode 100644 index 0a6c2692..00000000 --- a/admin/system/updates/index.php +++ /dev/null @@ -1,593 +0,0 @@ - - - - - - - - - System-Updates › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - - - - " /> - - - - - - -
-
-
-
-
-
-

System-Updates

- -
-
-
- - -
-
-
-
-
- Update-Status -
-
- - Prüfe Updates... -
-
-
-
-
-
Aktuelle Version:
- Lädt... -
- -
- - - - - - -
-
Update-Fortschritt:
-
-
- 0% -
-
-
Vorbereitung...
-
-
-
-
-
- - -
-
-
-
-
- Systemvoraussetzungen - -
-
-
-
-
- Lade Voraussetzungen... -
-
-
-
-
-
- - -
-
-
-
-
- Update-Log - -
-
-
-
-
Update-Log wird hier angezeigt...
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - \ No newline at end of file diff --git a/admin/system/updates/update_handler.php b/admin/system/updates/update_handler.php deleted file mode 100644 index a6837842..00000000 --- a/admin/system/updates/update_handler.php +++ /dev/null @@ -1,155 +0,0 @@ - false, - 'message' => 'Session konnte nicht gestartet werden', - 'debug' => 'session_start() fehlgeschlagen' - ]); - exit; -} - -if (!isset($_SESSION['userid'])) { - echo json_encode([ - 'success' => false, - 'message' => 'Keine userid in Session', - 'debug' => [ - 'session_id' => session_id(), - 'all_session_keys' => array_keys($_SESSION), - 'session_data' => $_SESSION - ] - ]); - exit; -} - -if (!isset($_SESSION['permissions'])) { - echo json_encode([ - 'success' => false, - 'message' => 'Keine permissions in Session', - 'debug' => [ - 'userid' => $_SESSION['userid'], - 'session_keys' => array_keys($_SESSION) - ] - ]); - exit; -} - -$hasPermission = false; -if (is_array($_SESSION['permissions'])) { - $adminRoles = ['full_admin']; - foreach ($adminRoles as $role) { - if (in_array($role, $_SESSION['permissions'])) { - $hasPermission = true; - break; - } - } -} - -if (!$hasPermission) { - echo json_encode([ - 'success' => false, - 'message' => 'Keine Berechtigung gefunden', - 'debug' => [ - 'your_permissions' => $_SESSION['permissions'], - 'checked_roles' => ['full_admin'], - 'permissions_type' => gettype($_SESSION['permissions']) - ] - ]); - exit; -} - -$action = $_POST['action'] ?? ''; - -try { - require_once __DIR__ . '/../../../assets/config/config.php'; - require_once __DIR__ . '/../../../assets/config/database.php'; -} catch (Exception $e) { - echo json_encode([ - 'success' => false, - 'message' => 'Config-Dateien konnten nicht geladen werden: ' . $e->getMessage() - ]); - exit; -} - -$updateManager = null; -if (file_exists(__DIR__ . '/UpdateManager.php')) { - try { - require_once __DIR__ . '/UpdateManager.php'; - $githubRepo = 'intraRP/intraRP'; - $updateManager = new UpdateManager($pdo, $githubRepo); - } catch (Exception $e) { - error_log('UpdateManager Error: ' . $e->getMessage()); - } -} - -switch ($action) { - case 'check_updates': - if ($updateManager) { - try { - $result = $updateManager->getUpdateInfo(); - echo json_encode($result); - } catch (Exception $e) { - echo json_encode([ - 'error' => true, - 'message' => 'GitHub API Error: ' . $e->getMessage(), - 'current_version' => '1.0.0', - 'has_update' => false - ]); - } - } else { - echo json_encode([ - 'error' => true, - 'message' => 'UpdateManager nicht verfügbar', - 'current_version' => '1.0.0', - 'has_update' => false, - 'debug' => 'UpdateManager.php existiert: ' . (file_exists(__DIR__ . '/UpdateManager.php') ? 'ja' : 'nein') - ]); - } - break; - - case 'check_requirements': - echo json_encode([ - 'php_version' => version_compare(PHP_VERSION, '7.4.0', '>='), - 'zip_extension' => extension_loaded('zip'), - 'curl_extension' => extension_loaded('curl') || ini_get('allow_url_fopen'), - 'writable_root' => is_writable(__DIR__ . '/../../../'), - 'git_available' => !empty(shell_exec('git --version 2>/dev/null')), - 'composer_available' => ( - file_exists(__DIR__ . '/../../../composer.phar') || - !empty(shell_exec('composer --version 2>/dev/null')) - ) - ]); - break; - - case 'perform_update': - if ($updateManager) { - set_time_limit(600); - echo json_encode($updateManager->performUpdate()); - } else { - echo json_encode([ - 'success' => false, - 'message' => 'UpdateManager nicht verfügbar' - ]); - } - break; - - default: - echo json_encode([ - 'success' => true, - 'message' => 'Debug-Handler aktiv', - 'action' => $action, - 'permissions_ok' => true, - 'updatemanager_available' => $updateManager !== null, - 'github_repo' => 'intraRP/intraRP' - ]); -} diff --git a/admin/system/updates/version.json b/admin/system/updates/version.json deleted file mode 100644 index e590f6e6..00000000 --- a/admin/system/updates/version.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "v0.4.5.1", - "updated_at": "2025-11-01 15:26:18", - "build_number": "18", - "commit_hash": "fb4a69cff5b9aec77d26e1024d369f5b5bc66e59" -} diff --git a/admin/antraege/list.php b/antrag/admin/list.php similarity index 83% rename from admin/antraege/list.php rename to antrag/admin/list.php index 6accbb8b..6700967d 100644 --- a/admin/antraege/list.php +++ b/antrag/admin/list.php @@ -2,11 +2,11 @@ session_start(); require_once __DIR__ . '/../../assets/config/config.php'; require_once __DIR__ . '/../../vendor/autoload.php'; -require __DIR__ . '/../../assets/config/database.php'; +require_once __DIR__ . '/../../assets/config/database.php'; if (!isset($_SESSION['userid']) || !isset($_SESSION['permissions'])) { $_SESSION['redirect_url'] = $_SERVER['REQUEST_URI']; - header("Location: " . BASE_PATH . "admin/login.php"); + header("Location: " . BASE_PATH . "login.php"); exit(); } @@ -15,7 +15,7 @@ if (!Permissions::check(['admin', 'application.edit'])) { Flash::set('error', 'no-permissions'); - header("Location: " . BASE_PATH . "admin/index.php"); + header("Location: " . BASE_PATH . "index.php"); exit(); } @@ -39,22 +39,12 @@ - - - Antragsübersicht › <?php echo SYSTEM_NAME ?> - - - - - - - - - - + - +
@@ -68,8 +58,8 @@ Antragsübersicht - - Typen verwalten + + Antragstypen verwalten
@@ -122,7 +112,7 @@ } // URL zur Antragsansicht - $view_url = BASE_PATH . "admin/antraege/view.php?antrag={$row['uniqueid']}"; + $view_url = BASE_PATH . "antrag/view.php?antrag={$row['uniqueid']}"; echo "log($_SESSION['userid'], 'Bearbeiter geändert [ID: ' . $caseid . ']', $cirs_manager, 'Anträge', 1); @@ -81,8 +83,23 @@ "); $stmt->execute([$cirs_manager, $cirs_status, $cirs_text, $jetzt, $antrag['id']]); + // Create notification for the applicant + $statusText = ['In Bearbeitung', 'Abgelehnt', 'Aufgeschoben', 'Angenommen']; + $statusName = $statusText[$cirs_status] ?? 'Unbekannt'; + + $userId = $notificationManager->getUserIdByDiscordTag($antrag['discordid']); + if ($userId) { + $notificationManager->create( + $userId, + 'antrag', + "Ihr Antrag #{$caseid} wurde bearbeitet", + "Status: {$statusName}. Bearbeiter: {$cirs_manager}", + BASE_PATH . "antrag/view.php?antrag={$caseid}" + ); + } + Flash::set('success', 'Antrag erfolgreich aktualisiert'); - header("Location: " . BASE_PATH . "admin/antraege/view.php?antrag=" . $caseid); + header("Location: " . BASE_PATH . "antrag/view.php?antrag=" . $caseid); exit(); } @@ -102,15 +119,11 @@ - - <?= htmlspecialchars($antrag['typ_name']) ?> bearbeiten [#<?= htmlspecialchars($caseid) ?>] › <?php echo SYSTEM_NAME ?> - - - - - - - + + + @@ -79,13 +70,11 @@

-

Neuen Antrag stellen

+

Neuen Antrag stellen

-
-
@@ -95,12 +84,9 @@
- diff --git a/antraege/view.php b/antrag/view.php similarity index 84% rename from antraege/view.php rename to antrag/view.php index bda25f5e..10c3046b 100644 --- a/antraege/view.php +++ b/antrag/view.php @@ -2,17 +2,17 @@ session_start(); require_once __DIR__ . '/../assets/config/config.php'; require_once __DIR__ . '/../vendor/autoload.php'; -require __DIR__ . '/../assets/config/database.php'; +require_once __DIR__ . '/../assets/config/database.php'; if (!isset($_SESSION['userid']) || !isset($_SESSION['permissions'])) { $_SESSION['redirect_url'] = $_SERVER['REQUEST_URI']; - header("Location: " . BASE_PATH . "admin/login.php"); + header("Location: " . BASE_PATH . "login.php"); exit(); } if (!isset($_SESSION['cirs_user']) || empty($_SESSION['cirs_user'])) { - header("Location: " . BASE_PATH . "admin/users/editprofile.php"); - exit(); + header("Location: " . BASE_PATH . "profil.php"); + exit; } use App\Helpers\Flash; @@ -22,7 +22,7 @@ if (!$caseid) { Flash::set('error', 'Keine Antragsnummer angegeben.'); - header("Location: " . BASE_PATH . "admin/index.php"); + header("Location: " . BASE_PATH . "index.php"); exit(); } @@ -38,7 +38,7 @@ if (!$antrag) { Flash::set('error', 'Antrag nicht gefunden.'); - header("Location: " . BASE_PATH . "admin/index.php"); + header("Location: " . BASE_PATH . "index.php"); exit(); } @@ -46,7 +46,7 @@ if (!Permissions::check(['admin', 'application.view'])) { if ($antrag['discordid'] !== $_SESSION['discordtag']) { Flash::set('error', 'Sie haben keine Berechtigung, diesen Antrag anzusehen.'); - header("Location: " . BASE_PATH . "admin/index.php"); + header("Location: " . BASE_PATH . "index.php"); exit(); } } @@ -80,27 +80,11 @@ - - - - Antrag [#<?php echo $caseid ?>] › <?php echo SYSTEM_NAME ?> - - - - - - - - - + + + + + + +
+
+
+
+
+

+ Benachrichtigungen +

+ + + +
+ + 0): ?> +
+ + +
+ +
+ +
+ +
+ +
+ +

Keine Benachrichtigungen vorhanden

+
+ + setTimezone(new DateTimeZone(date_default_timezone_get())); // Convert to PHP timezone (UTC) + $now = new DateTime('now', new DateTimeZone(date_default_timezone_get())); + $diff = $now->diff($datetime); + + // Check if the notification is in the future (clock skew) + if ($diff->invert == 0) { + // Notification is in the future, show as "Gerade eben" + $timeAgo = 'Gerade eben'; + } elseif ($diff->days > 0) { + // more than 1 day ago = (en), also change formatting to Vor X Tagen + + $timeAgo = 'Vor ' . $diff->days . ' Tag' . ($diff->days > 1 ? 'en' : ''); + } elseif ($diff->h > 0) { + $timeAgo = 'Vor ' . $diff->h . ' Stunde' . ($diff->h > 1 ? 'n' : ''); + } elseif ($diff->i > 0) { + $timeAgo = 'Vor ' . $diff->i . ' Minute' . ($diff->i > 1 ? 'n' : ''); + } else { + $timeAgo = 'Gerade eben'; + } + + $iconClass = [ + 'antrag' => 'fa-file', + 'protokoll' => 'fa-truck-medical', + 'dokument' => 'fa-folder-open' + ]; + $icon = $iconClass[$notification['type']] ?? 'fa-bell'; + ?> +
+
+
+
+ +
+
+
+
+
+
+ +
+ +

+ +

+ + + + + +
+
+ + + + + + +
+ + + +
+ +
+ + + +
+
+
+
+
+
+ + +
+
+
+
+
+ + + + + diff --git a/benachrichtigungen/mark-read.php b/benachrichtigungen/mark-read.php new file mode 100644 index 00000000..db0d144a --- /dev/null +++ b/benachrichtigungen/mark-read.php @@ -0,0 +1,45 @@ + false, 'message' => 'Not authorized'])); +} + +use App\Notifications\NotificationManager; + +header('Content-Type: application/json'); + +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + exit(json_encode(['success' => false, 'message' => 'Method not allowed'])); +} + +$notificationManager = new NotificationManager($pdo); +$userId = $_SESSION['userid']; + +// Get the notification ID from POST data +$data = json_decode(file_get_contents('php://input'), true); +$notificationId = $data['id'] ?? null; + +if (!$notificationId || !is_numeric($notificationId)) { + http_response_code(400); + exit(json_encode(['success' => false, 'message' => 'Invalid notification ID'])); +} + +try { + $result = $notificationManager->markAsRead((int)$notificationId, $userId); + + if ($result) { + exit(json_encode(['success' => true, 'message' => 'Notification marked as read'])); + } else { + http_response_code(404); + exit(json_encode(['success' => false, 'message' => 'Notification not found'])); + } +} catch (Exception $e) { + http_response_code(500); + exit(json_encode(['success' => false, 'message' => 'Server error'])); +} diff --git a/admin/users/auditlog.php b/benutzer/auditlog.php similarity index 64% rename from admin/users/auditlog.php rename to benutzer/auditlog.php index f43c4fe8..65364676 100644 --- a/admin/users/auditlog.php +++ b/benutzer/auditlog.php @@ -1,12 +1,12 @@ prepare("SELECT id, username FROM intra_users"); @@ -28,39 +28,13 @@ - - - - Administration › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - - - - " /> - + - +
@@ -88,7 +62,7 @@ prepare("SELECT * FROM intra_audit_log WHERE global = 1"); $stmt->execute(); $result = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -104,7 +78,7 @@ echo "" . $row['module'] . ""; echo "" . $row['action'] . ""; echo "" . $row['details'] . ""; - echo "" . $uinfo2['username'] . " (ID: " . $row['user'] . ")"; + echo "" . $uinfo2['username'] . " (ID: " . $row['user'] . ")"; echo ""; } ?> diff --git a/admin/users/delete.php b/benutzer/delete.php similarity index 66% rename from admin/users/delete.php rename to benutzer/delete.php index be8ef4ec..d0ca8652 100644 --- a/admin/users/delete.php +++ b/benutzer/delete.php @@ -1,12 +1,12 @@ log($userid, 'Benutzer gelöscht [ID: ' . $id . ']', NULL, 'Benutzer', 1); -header('Location: ' . BASE_PATH . 'admin/users/list.php'); +header('Location: ' . BASE_PATH . 'benutzer/list.php'); exit; diff --git a/admin/users/edit.php b/benutzer/edit.php similarity index 79% rename from admin/users/edit.php rename to benutzer/edit.php index 4a109c99..645449b0 100644 --- a/admin/users/edit.php +++ b/benutzer/edit.php @@ -1,11 +1,11 @@ $id ]); + Flash::success('Benutzer wurde erfolgreich aktualisiert.'); $auditLogger = new AuditLogger($pdo); $auditLogger->log($userid, 'Benutzer aktualisiert [ID: ' . $id . ']', NULL, 'Benutzer', 1); - header("Refresh: 0"); + header('Location: ' . BASE_PATH . 'benutzer/list.php'); + exit; } ?> @@ -73,38 +77,14 @@ - - - - <?= $row['username'] ?> › Administration › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - - - " /> - + - +
@@ -113,8 +93,10 @@

-

Benutzer bearbeiten

- +

Benutzer bearbeiten

+
@@ -148,7 +130,7 @@ + +
+
+ +
+ + +
+ Aktueller Registrierungsmodus: + Offen - Registrierung für jeden möglich'; + break; + case 'code': + echo 'Code - Registrierung nur mit Code'; + break; + case 'closed': + echo 'Geschlossen - Keine Registrierung möglich'; + break; + } + ?> +
+ +
+
Alle Registrierungscodes
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeErstellt vonErstellt amStatusVerwendet vonVerwendet amAktionen
Keine Registrierungscodes vorhanden.
+ + Verwendet + + Verfügbar + + + +
+ + + +
+ + - + +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/admin/users/roles/create.php b/benutzer/rollen/create.php similarity index 71% rename from admin/users/roles/create.php rename to benutzer/rollen/create.php index 8631e01f..fb7bb1c6 100644 --- a/admin/users/roles/create.php +++ b/benutzer/rollen/create.php @@ -1,8 +1,8 @@ log($_SESSION['userid'], 'Rolle erstellt', 'Name: ' . $name, 'Rollen', 1); - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } catch (PDOException $e) { error_log("PDO Insert Error (roles): " . $e->getMessage()); Flash::set('error', 'exception'); - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } } else { - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } diff --git a/admin/users/roles/delete.php b/benutzer/rollen/delete.php similarity index 64% rename from admin/users/roles/delete.php rename to benutzer/rollen/delete.php index dc0257a1..db1d1213 100644 --- a/admin/users/roles/delete.php +++ b/benutzer/rollen/delete.php @@ -1,8 +1,8 @@ execute([':id' => $id]); if (!$checkStmt->fetch()) { Flash::set('role', 'not-found'); - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } @@ -37,15 +37,15 @@ Flash::set('role', 'deleted'); $auditLogger = new AuditLogger($pdo); $auditLogger->log($_SESSION['userid'], 'Rolle gelöscht [ID: ' . $id . ']', NULL, 'Rollen', 1); - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } catch (PDOException $e) { error_log("PDO Delete Error: " . $e->getMessage()); Flash::set('error', 'exception'); - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } } else { - header("Location: " . BASE_PATH . "admin/users/roles/index.php"); + header("Location: " . BASE_PATH . "benutzer/rollen/index.php"); exit; } diff --git a/admin/users/roles/index.php b/benutzer/rollen/index.php similarity index 84% rename from admin/users/roles/index.php rename to benutzer/rollen/index.php index 0e67faed..1b844b19 100644 --- a/admin/users/roles/index.php +++ b/benutzer/rollen/index.php @@ -1,11 +1,11 @@ @@ -21,39 +21,13 @@ - - - - Administration › <?php echo SYSTEM_NAME ?> - - - - - - - - - - - - - - - - - - - - - - - - " /> - + - +
@@ -66,7 +40,7 @@

Rollenverwaltung

@@ -85,14 +59,14 @@ prepare("SELECT * FROM intra_users_roles"); $stmt->execute(); $result = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($result as $row) { $actions = (Permissions::check('full_admin')) - ? "" + ? "" : ""; echo ""; @@ -116,7 +90,7 @@