diff --git a/partials/landingpages/modules/setup.php b/partials/landingpages/modules/setup.php index d3341d9..6d5fe40 100644 --- a/partials/landingpages/modules/setup.php +++ b/partials/landingpages/modules/setup.php @@ -42,17 +42,23 @@ foreach ($fields as $field) { } $isFxRatesSetup = $moduleName === 'fx-rates'; $current = modules()->settings($moduleName); -$intervalTaskStatuses = modules()->intervalTaskStatuses($moduleName); +$intervalTaskStatuses = []; $cronTaskDefinitions = modules()->cronTasks($moduleName); -$cronTaskStatuses = modules()->cronTaskStatuses($moduleName); +$cronTaskStatuses = []; $cronTaskStatusGroups = []; -foreach ($cronTaskStatuses as $cronTaskStatus) { - $cronGroupName = trim((string) ($cronTaskStatus['job_name'] ?? $cronTaskStatus['name'] ?? '')); - if ($cronGroupName === '') { - continue; +$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; } - $cronTaskStatusGroups[$cronGroupName][] = $cronTaskStatus; -} +}; +$refreshSchedulerState(); $setupActions = modules()->hasFunction($moduleName, 'setup_actions') ? (array) module_fn($moduleName, 'setup_actions') : []; @@ -134,6 +140,8 @@ $driverOptions = [ 'sqlite' => 'SQLite', ]; +$timezoneOptions = modules()->timezones(); + $describeDbConfig = static function (array $dbConfig): string { $driver = (string)($dbConfig['driver'] ?? ''); $host = (string)($dbConfig['host'] ?? ''); @@ -281,18 +289,72 @@ $normalizeDriver = static function (mixed $value): mixed { }; }; -$formatRunTimestamp = static function (?string $value): string { +$formatRunTimestamp = static function (?string $value, ?string $timezone = null): string { $value = trim((string) $value); if ($value === '') { return '-'; } - $ts = strtotime($value); - if ($ts === false) { - return $value; + 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 date('Y-m-d H:i:s', $ts); + return $schedulerJobs; }; $cronWeekdays = [ @@ -338,6 +400,8 @@ $renderField = function (array $field) use (&$current, $getNested, $driverOption > + + > > @@ -349,6 +413,7 @@ $renderField = function (array $field) use (&$current, $getNested, $driverOption }; if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $isSchedulerAutosave = isset($_POST['scheduler_autosave']) && (string) $_POST['scheduler_autosave'] === '1'; $payload = []; foreach ($fields as $field) { @@ -393,53 +458,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($cronTaskDefinitions !== []) { $postedSchedulerJobs = is_array($_POST['scheduler_jobs'] ?? null) ? $_POST['scheduler_jobs'] : []; - $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]; - } + $schedulerJobs = $extractSchedulerJobs($postedSchedulerJobs, $cronTaskDefinitions, $current); $current['scheduler_jobs'] = $schedulerJobs; } + if ($isSchedulerAutosave) { + modules()->saveSettings($moduleName, $current); + $current = modules()->settings($moduleName); + $refreshSchedulerState(); + + header('Content-Type: application/json; charset=utf-8'); + echo json_encode([ + 'ok' => true, + 'message' => 'Scheduler gespeichert.', + 'scheduler_jobs' => $current['scheduler_jobs'] ?? [], + 'statuses' => $cronTaskStatuses, + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + return; + } + $postedTestGroup = (string)($_POST['test_db'] ?? ''); $postedResetGroup = (string)($_POST['reset_db'] ?? ''); $postedSetupAction = trim((string)($_POST['module_setup_action'] ?? '')); @@ -524,6 +561,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { module_fn($moduleName, 'save_runtime_settings', $payload); $current = modules()->settings($moduleName); } + $refreshSchedulerState(); if (empty($payload['debug_enabled'])) { module_debug_clear($moduleName); } @@ -554,6 +592,11 @@ $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups)