get($moduleName); $error = null; $notice = null; $testGroup = null; $dbTestMessages = []; $currentSection = trim((string) ($_GET['section'] ?? 'general')); require_admin(); if (!$module) { http_response_code(404); echo '
Modul nicht gefunden.
'; return; } $fields = (array)($module['setup']['fields'] ?? []); $setupSectionConfig = is_array($module['setup']['sections'] ?? null) ? $module['setup']['sections'] : []; $hasGlobalDebugField = false; foreach ($fields as $field) { if ((string)($field['name'] ?? '') === 'debug_enabled') { $hasGlobalDebugField = true; break; } } if (!$hasGlobalDebugField) { $fields[] = [ 'name' => 'debug_enabled', 'label' => 'Modul-Debug aktivieren', 'type' => 'checkbox', 'help' => 'Wenn aktiv, darf dieses Modul Debug-Daten in den globalen Nexus-Debugstream schreiben.', ]; } $fieldTypes = []; $fieldMeta = []; foreach ($fields as $field) { $fname = (string)($field['name'] ?? ''); if ($fname === '') { continue; } $fieldTypes[$fname] = (string)($field['type'] ?? 'text'); $fieldMeta[$fname] = $field; } $current = modules()->settings($moduleName); $runtimeSettingsEnabled = modules()->hasFunction($moduleName, 'runtime_settings'); if ($runtimeSettingsEnabled) { try { $runtimeSettings = module_fn($moduleName, 'runtime_settings'); if (is_array($runtimeSettings)) { $current = array_replace_recursive($current, $runtimeSettings); } } catch (\Throwable $e) { $error = $e->getMessage(); } } $intervalTaskStatuses = []; $cronTaskDefinitions = modules()->cronTasks($moduleName); $cronTaskStatuses = []; $cronTaskStatusGroups = []; $refreshSchedulerState = static function () use ($moduleName, &$intervalTaskStatuses, &$cronTaskStatuses, &$cronTaskStatusGroups): void { $intervalTaskStatuses = modules()->intervalTaskStatuses($moduleName); $cronTaskStatuses = modules()->cronTaskStatuses($moduleName); $cronTaskStatusGroups = []; foreach ($cronTaskStatuses as $cronTaskStatus) { $cronGroupName = trim((string) ($cronTaskStatus['job_name'] ?? $cronTaskStatus['name'] ?? '')); if ($cronGroupName === '') { continue; } $cronTaskStatusGroups[$cronGroupName][] = $cronTaskStatus; } }; $refreshSchedulerState(); $setupActions = modules()->hasFunction($moduleName, 'setup_actions') ? (array) module_fn($moduleName, 'setup_actions') : []; $databaseSectionActions = array_values(array_filter($setupActions, static function (mixed $action): bool { return is_array($action) && trim((string) ($action['section'] ?? '')) === 'database'; })); $customSectionActions = array_values(array_filter($setupActions, static function (mixed $action): bool { return !is_array($action) || trim((string) ($action['section'] ?? '')) !== 'database'; })); $defaults = $module['db_defaults'] ?? []; if (empty($current['db']) && is_array($defaults)) { $current['db'] = $defaults; } $metadataDefaults = $module['metadata_db_defaults'] ?? []; if (empty($current['metadata_db']) && is_array($metadataDefaults)) { $current['metadata_db'] = $metadataDefaults; } $dbDefaultsByGroup = [ 'db' => is_array($defaults) ? $defaults : [], 'metadata_db' => is_array($metadataDefaults) ? $metadataDefaults : [], ]; $authConfig = is_array($module['auth'] ?? null) ? $module['auth'] : ['required' => false, 'users' => [], 'groups' => []]; $allowedUsers = []; $allowedGroups = []; $knownUsers = []; $knownGroups = []; $manualUsers = []; $manualGroups = []; $setNested = function (array &$target, string $path, mixed $value): void { $parts = explode('.', $path); $last = array_pop($parts); $node = &$target; foreach ($parts as $part) { if (!isset($node[$part]) || !is_array($node[$part])) { $node[$part] = []; } $node = &$node[$part]; } if ($last !== null && $last !== '') { $node[$last] = $value; } }; $getNested = function (array $source, string $path): mixed { $node = $source; foreach (explode('.', $path) as $part) { if (!is_array($node) || !array_key_exists($part, $node)) { return null; } $node = $node[$part]; } return $node; }; $dbGroups = []; $fieldsByDbGroup = []; $generalFields = []; foreach ($fields as $field) { $name = (string)($field['name'] ?? ''); if (!str_contains($name, '.')) { continue; } [$group, $key] = explode('.', $name, 2); if ($key !== 'driver') { continue; } $label = (string)($field['label'] ?? $group); $label = trim(preg_replace('/\s+DB\s+Driver$/i', ' DB', $label) ?? $label); if ($group === 'db') { $label = 'Custom Datenbank'; } $label = $label !== '' ? $label : $group; $dbGroups[$group] = $label; } foreach ($fields as $field) { $name = (string)($field['name'] ?? ''); if (str_contains($name, '.')) { [$group] = explode('.', $name, 2); if (array_key_exists($group, $dbGroups)) { $fieldsByDbGroup[$group][] = $field; continue; } } $generalFields[] = $field; } $generalSetupFields = []; $databaseSetupFields = []; $cronSetupFields = []; $customSetupFields = []; foreach ($generalFields as $field) { $fieldName = (string)($field['name'] ?? ''); if ($fieldName === 'debug_enabled') { $generalSetupFields[] = $field; continue; } if ($fieldName === 'use_separate_db') { $databaseSetupFields[] = $field; continue; } if ($fieldName === 'schedule_timezone') { $cronSetupFields[] = $field; continue; } $customSetupFields[] = $field; } $driverOptions = [ 'pgsql' => 'PostgreSQL', 'mysql' => 'MySQL / MariaDB', 'sqlite' => 'SQLite', ]; $timezoneOptions = modules()->timezones(); $describeDbConfig = static function (array $dbConfig): string { $driver = (string)($dbConfig['driver'] ?? ''); $host = (string)($dbConfig['host'] ?? ''); $port = (string)($dbConfig['port'] ?? ''); return 'Aktive Einstellung: Treiber ' . ($driver !== '' ? $driver : 'nicht gesetzt') . ', Host ' . ($host !== '' ? $host : 'nicht gesetzt') . ', Port ' . ($port !== '' ? $port : 'nicht gesetzt') . '.'; }; $describeDbDefaults = static function (array $dbDefaults): string { $driver = (string)($dbDefaults['driver'] ?? ''); $host = (string)($dbDefaults['host'] ?? ''); $port = (string)($dbDefaults['port'] ?? ''); if ($driver === '' && $host === '' && $port === '') { return ''; } return 'Standard: Treiber ' . ($driver !== '' ? $driver : 'nicht gesetzt') . ', Host ' . ($host !== '' ? $host : 'nicht gesetzt') . ', Port ' . ($port !== '' ? $port : 'nicht gesetzt') . '.'; }; $dbConfigWarning = static function (array $dbConfig): ?string { $driver = (string)($dbConfig['driver'] ?? ''); $port = (string)($dbConfig['port'] ?? ''); if ($driver === 'pgsql' && $port === '3306') { return 'Port 3306 ist typisch fuer MySQL/MariaDB, aber als Treiber ist PostgreSQL ausgewaehlt. Bitte den Treiber auf MySQL / MariaDB stellen.'; } if ($driver === 'mysql' && $port === '5432') { return 'Port 5432 ist typisch fuer PostgreSQL, aber als Treiber ist MySQL / MariaDB ausgewaehlt. Bitte Port oder Treiber pruefen.'; } return null; }; $dbConfigHint = static function (string $group, array $dbConfig, array $dbDefaults): ?string { if ($group !== 'metadata_db') { return null; } $driver = (string)($dbConfig['driver'] ?? ''); $host = (string)($dbConfig['host'] ?? ''); $port = (string)($dbConfig['port'] ?? ''); $defaultDriver = (string)($dbDefaults['driver'] ?? ''); $defaultHost = (string)($dbDefaults['host'] ?? ''); $defaultPort = (string)($dbDefaults['port'] ?? ''); if ($driver !== $defaultDriver || $host !== $defaultHost || $port !== $defaultPort) { return 'Diese Verbindung ist fuer Nexus-eigene DHCP-Zusatzinfos gedacht, nicht fuer die Nexus-App/Base-DB. Erwartet wird normalerweise: ' . ($defaultDriver !== '' ? $defaultDriver : 'Treiber offen') . ' auf ' . ($defaultHost !== '' ? $defaultHost : 'Host offen') . ':' . ($defaultPort !== '' ? $defaultPort : 'Port offen') . '.'; } return null; }; $fetchJsonWithApiKey = static function (string $url, string $apiKey, int $timeout = 10): array { $headers = [ 'Accept: application/json', 'x-api-key: ' . $apiKey, ]; $responseBody = null; $httpCode = 0; $curlError = ''; if (function_exists('curl_init')) { $ch = curl_init($url); if ($ch !== false) { curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_CONNECTTIMEOUT => min(5, $timeout), CURLOPT_HTTPHEADER => $headers, ]); $responseBody = curl_exec($ch); $curlError = curl_error($ch); $httpCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch); } } if (!is_string($responseBody) || $responseBody === '') { $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'timeout' => $timeout, 'header' => implode("\r\n", $headers) . "\r\n", ], ]); $responseBody = @file_get_contents($url, false, $context); } if (!is_string($responseBody) || $responseBody === '') { return [ 'ok' => false, 'message' => 'Abruf fehlgeschlagen.' . ($curlError !== '' ? ' ' . $curlError : '') . ($httpCode > 0 ? ' HTTP ' . $httpCode : ''), ]; } $decoded = json_decode($responseBody, true); if (!is_array($decoded)) { return [ 'ok' => false, 'message' => 'Antwort ist kein gueltiges JSON.', ]; } foreach (['error', 'message', 'detail'] as $errorKey) { if (isset($decoded[$errorKey]) && is_string($decoded[$errorKey]) && trim($decoded[$errorKey]) !== '') { return [ 'ok' => false, 'message' => trim((string) $decoded[$errorKey]), ]; } } return [ 'ok' => true, 'data' => $decoded, ]; }; $normalizeDriver = static function (mixed $value): mixed { if (!is_string($value)) { return $value; } $normalized = strtolower(trim($value)); return match ($normalized) { 'postgres', 'postgresql' => 'pgsql', 'mariadb', 'mysql/mariadb', 'mysql / mariadb' => 'mysql', default => $normalized, }; }; $formatRunTimestamp = static function (?string $value, ?string $timezone = null): string { $value = trim((string) $value); if ($value === '') { return '-'; } try { $dt = new DateTimeImmutable($value, new DateTimeZone('UTC')); $targetTz = trim((string) $timezone) !== '' ? new DateTimeZone((string) $timezone) : new DateTimeZone(date_default_timezone_get()); return $dt->setTimezone($targetTz)->format('Y-m-d H:i:s'); } catch (\Throwable) { $ts = strtotime($value); if ($ts === false) { return $value; } return date('Y-m-d H:i:s', $ts); } }; $extractSchedulerJobs = static function (array $postedSchedulerJobs, array $cronTaskDefinitions, array $current): array { $schedulerJobs = []; foreach ($cronTaskDefinitions as $cronTask) { if (!is_array($cronTask)) { continue; } $cronName = trim((string) ($cronTask['name'] ?? '')); if ($cronName === '') { continue; } $jobPayload = is_array($postedSchedulerJobs[$cronName] ?? null) ? $postedSchedulerJobs[$cronName] : []; $entriesPayload = is_array($jobPayload['entries'] ?? null) ? $jobPayload['entries'] : []; $entries = []; foreach (array_values($entriesPayload) as $entryPayload) { if (!is_array($entryPayload)) { continue; } $cronExpression = trim((string) ($entryPayload['cron_expression'] ?? '')); $timezone = trim((string) ($entryPayload['timezone'] ?? ($current['schedule_timezone'] ?? 'UTC'))); if ($cronExpression === '' && $timezone === '') { continue; } $entries[] = [ 'enabled' => !empty($entryPayload['enabled']), 'cron_expression' => $cronExpression, 'timezone' => $timezone !== '' ? $timezone : 'UTC', 'builder' => [ 'mode' => trim((string) ($entryPayload['builder']['mode'] ?? 'builder')), 'kind' => trim((string) ($entryPayload['builder']['kind'] ?? 'daily')), 'time' => trim((string) ($entryPayload['builder']['time'] ?? '18:00')), 'interval_days' => max(1, (int) ($entryPayload['builder']['interval_days'] ?? 2)), 'weekday' => trim((string) ($entryPayload['builder']['weekday'] ?? '1')), 'month_day' => max(1, min(31, (int) ($entryPayload['builder']['month_day'] ?? 1))), 'interval_hours' => max(1, min(23, (int) ($entryPayload['builder']['interval_hours'] ?? 6))), ], ]; } if ((string) ($cronTask['mode'] ?? 'single') !== 'multi' && $entries !== []) { $entries = [array_values($entries)[0]]; } $schedulerJobs[$cronName] = ['entries' => $entries]; } return $schedulerJobs; }; $cronWeekdays = [ '0' => 'Sonntag', '1' => 'Montag', '2' => 'Dienstag', '3' => 'Mittwoch', '4' => 'Donnerstag', '5' => 'Freitag', '6' => 'Samstag', ]; $renderField = function (array $field) use (&$current, $getNested, $driverOptions): void { $name = (string)($field['name'] ?? ''); if ($name === '') { return; } $label = (string)($field['label'] ?? $name); $type = (string)($field['type'] ?? 'text'); $required = !empty($field['required']); $help = (string)($field['help'] ?? $field['description'] ?? ''); $options = is_array($field['options'] ?? null) ? $field['options'] : []; $postKey = str_replace('.', '_', $name); $value = ''; if ($name === 'kea_auto_init') { $value = !empty($current[$name]) ? '1' : '0'; } elseif (str_contains($name, '.')) { $value = $getNested($current, $name); } else { $value = $current[$name] ?? ''; } if (is_array($value)) { $value = $type === 'multiselect' ? array_values(array_map('strval', $value)) : implode(', ', array_values(array_map('strval', $value))); } else { $value = (string) $value; } ?> saveSettings($moduleName, $current); $current = modules()->settings($moduleName); $refreshSchedulerState(); $testResult = null; if ($isSchedulerTest) { $jobName = trim((string) ($_POST['scheduler_job_name'] ?? '')); $entryIndex = max(0, (int) ($_POST['scheduler_entry_index'] ?? 0)); $testResult = modules()->runCronTaskNow($moduleName, $jobName, $entryIndex); $refreshSchedulerState(); } while (ob_get_level() > 0) { ob_end_clean(); } header('Content-Type: application/json; charset=utf-8'); echo json_encode([ 'ok' => true, 'message' => $isSchedulerTest ? (string) ($testResult['message'] ?? 'Cron-Test ausgefuehrt.') : 'Scheduler gespeichert.', 'scheduler_jobs' => $current['scheduler_jobs'] ?? [], 'statuses' => $cronTaskStatuses, 'test_result' => $testResult, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } foreach ($fields as $field) { $name = (string)($field['name'] ?? ''); if ($name === '') { continue; } $type = (string)($field['type'] ?? 'text'); $postKey = str_replace('.', '_', $name); $value = $_POST[$postKey] ?? null; if ($type === 'checkbox') { $value = isset($_POST[$postKey]) ? '1' : '0'; } if (is_array($value) && $type === 'multiselect') { $value = array_values(array_filter(array_map( static fn (mixed $item): string => trim((string) $item), $value ), static fn (string $item): bool => $item !== '')); } elseif (is_array($value)) { continue; } $value = is_string($value) ? trim($value) : $value; if (str_ends_with($name, '.driver')) { $value = $normalizeDriver($value); } if ($name === 'kea_auto_init') { $payload[$name] = $value === '1'; continue; } if (str_contains($name, '.')) { $setNested($payload, $name, $value); continue; } $payload[$name] = $value; } $current = array_replace_recursive($current, $payload); if ($cronTaskDefinitions !== []) { $postedSchedulerJobs = is_array($_POST['scheduler_jobs'] ?? null) ? $_POST['scheduler_jobs'] : []; $schedulerJobs = $extractSchedulerJobs($postedSchedulerJobs, $cronTaskDefinitions, $current); $current['scheduler_jobs'] = $schedulerJobs; } $postedTestGroup = (string)($_POST['test_db'] ?? ''); $postedResetGroup = (string)($_POST['reset_db'] ?? ''); $postedSetupAction = trim((string)($_POST['module_setup_action'] ?? '')); if ($postedSetupAction !== '') { if (!modules()->hasFunction($moduleName, 'run_setup_action')) { $error = 'Diese Setup-Aktion wird vom Modul nicht unterstuetzt.'; } else { try { $actionResult = module_fn($moduleName, 'run_setup_action', $postedSetupAction); $current = modules()->settings($moduleName); $notice = 'Setup-Aktion ausgefuehrt.'; if (is_array($actionResult)) { if (isset($actionResult['message']) && is_string($actionResult['message']) && trim($actionResult['message']) !== '') { $notice = trim((string) $actionResult['message']); } elseif (isset($actionResult['synced_count'])) { $notice = 'Waehrungskatalog synchronisiert. ' . (int) $actionResult['synced_count'] . ' Waehrungen verarbeitet.'; } } } catch (\Throwable $e) { $error = $e->getMessage(); } } } elseif ($postedResetGroup !== '') { $testGroup = $postedResetGroup; if (!array_key_exists($postedResetGroup, $dbGroups)) { $error = 'Unbekannte Datenbank-Konfiguration.'; } elseif (($dbDefaultsByGroup[$postedResetGroup] ?? []) === []) { $dbTestMessages[$postedResetGroup] = [ 'type' => 'error', 'text' => 'Fuer diese Datenbank sind keine Standardwerte hinterlegt.', ]; } else { $current[$postedResetGroup] = $dbDefaultsByGroup[$postedResetGroup]; $dbTestMessages[$postedResetGroup] = [ 'type' => 'success', 'text' => 'Standardwerte geladen. Bitte pruefen und speichern.', ]; } } elseif ($postedTestGroup !== '') { $testGroup = $postedTestGroup; if (!array_key_exists($postedTestGroup, $dbGroups)) { $error = 'Unbekannte Datenbank-Konfiguration.'; } else { $dbConfig = $getNested($current, $postedTestGroup); if (!is_array($dbConfig)) { $dbTestMessages[$postedTestGroup] = [ 'type' => 'error', 'text' => 'Datenbank-Konfiguration ist unvollstaendig.', ]; } else { $warning = $dbConfigWarning($dbConfig); if ($warning !== null) { $dbTestMessages[$postedTestGroup] = [ 'type' => 'error', 'text' => $warning . ' ' . $describeDbConfig($dbConfig), ]; } else { try { $dbConfig['options'] = array_replace([ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, ], (array)($dbConfig['options'] ?? [])); $testPdo = \App\Database::createFromArray($dbConfig); $testPdo->query('SELECT 1')->fetchColumn(); $dbTestMessages[$postedTestGroup] = [ 'type' => 'success', 'text' => 'Verbindung erfolgreich. ' . $describeDbConfig($dbConfig), ]; } catch (\Throwable $e) { $dbTestMessages[$postedTestGroup] = [ 'type' => 'error', 'text' => 'Verbindung fehlgeschlagen. ' . $describeDbConfig($dbConfig) . ' ' . $e->getMessage(), ]; } } } } } else { modules()->saveSettings($moduleName, $current); if ($submittedSetupSection === 'access') { $selectedUsers = is_array($_POST['auth_user_values'] ?? null) ? $_POST['auth_user_values'] : []; $selectedGroups = is_array($_POST['auth_group_values'] ?? null) ? $_POST['auth_group_values'] : []; $manualUserValues = preg_split('/[,\\n]+/', (string) ($_POST['auth_users'] ?? '')) ?: []; $manualGroupValues = preg_split('/[,\\n]+/', (string) ($_POST['auth_groups'] ?? '')) ?: []; modules()->saveAuth($moduleName, [ 'required' => isset($_POST['auth_required']), 'users' => array_merge($selectedUsers, $manualUserValues), 'groups' => array_merge($selectedGroups, $manualGroupValues), ]); } if (modules()->hasFunction($moduleName, 'save_runtime_settings')) { module_fn($moduleName, 'save_runtime_settings', $payload); $current = modules()->settings($moduleName); if ($runtimeSettingsEnabled) { $runtimeSettings = module_fn($moduleName, 'runtime_settings'); if (is_array($runtimeSettings)) { $current = array_replace_recursive($current, $runtimeSettings); } } } $refreshSchedulerState(); if ($submittedSetupSection === 'general' && array_key_exists('debug_enabled', $payload) && empty($payload['debug_enabled'])) { module_debug_clear($moduleName); } $notice = 'Setup gespeichert.'; $module = modules()->get($moduleName) ?: $module; $authConfig = is_array($module['auth'] ?? null) ? $module['auth'] : $authConfig; } } $moduleStatusPanel = null; if (modules()->hasFunction($moduleName, 'setup_status')) { try { $statusPanelPayload = module_fn($moduleName, 'setup_status'); if (is_array($statusPanelPayload)) { $moduleStatusPanel = $statusPanelPayload; } } catch (\Throwable $e) { $moduleStatusPanel = [ 'title' => 'Status', 'type' => 'error', 'text' => $e->getMessage(), 'stats' => [], ]; } } $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups) ? $testGroup : (array_key_first($dbGroups) ?? ''); $authConfig = is_array($module['auth'] ?? null) ? $module['auth'] : ['required' => false, 'users' => [], 'groups' => []]; $allowedUsers = is_array($authConfig['users'] ?? null) ? array_values(array_filter(array_map('strval', $authConfig['users']))) : []; $allowedGroups = is_array($authConfig['groups'] ?? null) ? array_values(array_filter(array_map('strval', $authConfig['groups']))) : []; $knownUsers = modules()->knownAuthUsers(); $knownGroups = modules()->knownAuthGroups(); $currentUser = auth_user(); if (is_array($currentUser) && trim((string)($currentUser['sub'] ?? '')) !== '') { $currentSub = (string) $currentUser['sub']; $hasCurrentUser = false; foreach ($knownUsers as $knownUser) { if ((string) ($knownUser['sub'] ?? '') === $currentSub) { $hasCurrentUser = true; break; } } if (!$hasCurrentUser) { $knownUsers[] = [ 'sub' => $currentSub, 'username' => (string) ($currentUser['username'] ?? ''), 'email' => (string) ($currentUser['email'] ?? ''), 'name' => (string) ($currentUser['name'] ?? ''), 'groups' => is_array($currentUser['groups'] ?? null) ? $currentUser['groups'] : [], ]; } } $knownGroups = array_values(array_unique(array_merge($knownGroups, auth_groups()))); sort($knownGroups, SORT_NATURAL | SORT_FLAG_CASE); $knownUserValues = array_column($knownUsers, 'sub'); $manualUsers = array_values(array_filter($allowedUsers, fn (string $value): bool => !in_array($value, $knownUserValues, true))); $manualGroups = array_values(array_filter($allowedGroups, fn (string $value): bool => !in_array($value, $knownGroups, true))); $hasDatabaseSection = array_key_exists('database', $setupSectionConfig) ? !empty($setupSectionConfig['database']) : $dbGroups !== []; $hasCustomSection = $customSetupFields !== [] || $customSectionActions !== []; $showCustomDbConfig = !empty($current['use_separate_db']) && !in_array(strtolower(trim((string) ($current['use_separate_db'] ?? ''))), ['0', 'false', 'off', 'standard'], true); $allowedSetupSections = ['general', 'access', 'cron']; if ($hasDatabaseSection) { $allowedSetupSections[] = 'database'; } if ($hasCustomSection) { $allowedSetupSections[] = 'custom'; } if (!in_array($currentSection, $allowedSetupSections, true)) { $currentSection = 'general'; } $sectionUrls = [ 'general' => '/modules/setup/' . rawurlencode($moduleName) . '/general', 'database' => '/modules/setup/' . rawurlencode($moduleName) . '/database', 'access' => '/modules/setup/' . rawurlencode($moduleName) . '/access', 'cron' => '/modules/setup/' . rawurlencode($moduleName) . '/cron', 'custom' => '/modules/setup/' . rawurlencode($moduleName) . '/custom', ]; $sectionTitles = [ 'general' => 'Allgemein', 'database' => 'Datenbank', 'access' => 'Zugriffsrechte', 'cron' => 'Cron Einstellungen', 'custom' => 'Custom Settings', ]; if ($currentSection === 'custom' && !$hasCustomSection) { $currentSection = 'general'; } $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection] ?? 'Allgemein'); ?>
Setup

Trage die benötigten Informationen für das Modul ein.

Allgemein

Moduleinstellungen

Zugriffsrechte

Zugriff verwalten

Steuert, ob Login erforderlich ist und welche Benutzer oder Gruppen das Modul oeffnen duerfen.

Login erforderlich
Erlaubte Benutzer Noch keine bekannten Benutzer vorhanden. Nutzer erscheinen hier, sobald sie sich einmal angemeldet haben.
Erlaubte Gruppen Noch keine bekannten Gruppen vorhanden.
Wenn Login aktiv ist und Benutzer sowie Gruppen leer bleiben, darf jeder eingeloggte Benutzer das Modul oeffnen.
Cron Einstellungen

Scheduler und Zeitsteuerung

Hier liegen die zeitbezogenen Modul-Einstellungen, Intervall-Tasks und Cron-Jobs.

Dieses Modul hat keine eigenen Zeitzonenfelder. Intervall-Tasks und Cron-Jobs koennen trotzdem weiter unten verwaltet werden.

Automationen

Intervall-Aufgaben

Diese Aufgaben werden beim ersten gueltigen Modulaufruf nach Ablauf des Intervalls automatisch ausgefuehrt.

Intervall: Stunden Letzter Start: Letzter Erfolg: Naechster Lauf: Status: Meldung:
Automationen

Cron-Jobs

Diese Jobs werden ueber den zentralen Nexus-Scheduler ausgefuehrt. Der System-Cron sollte den CLI-Runner jede Minute starten.

$task): ?>
Cron-Syntax: Minute Stunde Tag Monat Wochentag
Letzter Start: Letzter Erfolg: Naechster Lauf lokal: Aktion: () Status: Meldung: Cron-Fehler:
Datenbank

Datenbankaktionen und Tabellenstatus

Tabellenbezogene Wartungsaktionen und der aktuelle Schema-Status werden hier gemeinsam angezeigt.

Status

Der Status wird beim Aufruf der Setup-Seite automatisch geprueft.

>
Aktionen

Modulaktionen

Seltene Wartungsaktionen koennen direkt hier aus dem Setup ausgefuehrt werden.

>
Datenbank

Verbindungen

Standard nutzt die Nexus-Datenbank. Custom blendet eigene Verbindungsdaten und den DB-Test ein.

1): ?>
> $label): ?>
> $label): ?>
>

konfigurieren

Custom Settings

Modulspezifische Einstellungen