asdas
This commit is contained in:
@@ -515,7 +515,7 @@ $mm->registerFunction($moduleName, 'alpha_vantage_request', static function (
|
||||
});
|
||||
|
||||
$mm->registerFunction($moduleName, 'display_timezone', static function (): \DateTimeZone {
|
||||
return new \DateTimeZone('Europe/Berlin');
|
||||
return new \DateTimeZone(nexus_display_timezone_name());
|
||||
});
|
||||
|
||||
$mm->registerFunction($moduleName, 'normalize_market_timestamp_utc', static function (mixed $value): string {
|
||||
@@ -547,7 +547,7 @@ $mm->registerFunction($moduleName, 'format_datetime_for_display', static functio
|
||||
return '';
|
||||
}
|
||||
|
||||
$displayTimezone = new \DateTimeZone('Europe/Berlin');
|
||||
$displayTimezone = new \DateTimeZone(nexus_display_timezone_name());
|
||||
$source = trim((string) $source);
|
||||
|
||||
if (str_starts_with($source, 'bavest:') || str_starts_with($source, 'alphavantage:')) {
|
||||
|
||||
@@ -14,6 +14,7 @@ $modules = array_values(array_filter(
|
||||
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
||||
<a class="nav-link" href="/modules">Module verwalten</a>
|
||||
<?php if (auth_is_admin()): ?>
|
||||
<a class="nav-link" href="/settings">Nexus Einstellungen</a>
|
||||
<a class="nav-link" href="/exports/database.sql">SQL-Export</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -165,6 +165,7 @@ foreach ($fields as $field) {
|
||||
$generalSetupFields = [];
|
||||
$databaseSetupFields = [];
|
||||
$cronSetupFields = [];
|
||||
$cronTimezoneField = null;
|
||||
$customSetupFields = [];
|
||||
foreach ($generalFields as $field) {
|
||||
$fieldName = (string)($field['name'] ?? '');
|
||||
@@ -177,7 +178,7 @@ foreach ($generalFields as $field) {
|
||||
continue;
|
||||
}
|
||||
if ($fieldName === 'schedule_timezone') {
|
||||
$cronSetupFields[] = $field;
|
||||
$cronTimezoneField = $field;
|
||||
continue;
|
||||
}
|
||||
$customSetupFields[] = $field;
|
||||
@@ -190,6 +191,10 @@ $driverOptions = [
|
||||
];
|
||||
|
||||
$timezoneOptions = modules()->timezones();
|
||||
$globalCronTimezone = nexus_cron_timezone_name();
|
||||
$globalDisplayTimezone = nexus_display_timezone_name();
|
||||
$moduleCronTimezoneOverride = trim((string) ($current['schedule_timezone'] ?? ''));
|
||||
$effectiveModuleCronTimezone = $moduleCronTimezoneOverride !== '' ? $moduleCronTimezoneOverride : $globalCronTimezone;
|
||||
|
||||
$describeDbConfig = static function (array $dbConfig): string {
|
||||
$driver = (string)($dbConfig['driver'] ?? '');
|
||||
@@ -346,7 +351,7 @@ $formatRunTimestamp = static function (?string $value, ?string $timezone = null)
|
||||
|
||||
try {
|
||||
$dt = new DateTimeImmutable($value, new DateTimeZone('UTC'));
|
||||
$targetTz = trim((string) $timezone) !== '' ? new DateTimeZone((string) $timezone) : new DateTimeZone(date_default_timezone_get());
|
||||
$targetTz = trim((string) $timezone) !== '' ? new DateTimeZone((string) $timezone) : new DateTimeZone(nexus_display_timezone_name());
|
||||
return $dt->setTimezone($targetTz)->format('Y-m-d H:i:s');
|
||||
} catch (\Throwable) {
|
||||
$ts = strtotime($value);
|
||||
@@ -376,7 +381,7 @@ $extractSchedulerJobs = static function (array $postedSchedulerJobs, array $cron
|
||||
continue;
|
||||
}
|
||||
$cronExpression = trim((string) ($entryPayload['cron_expression'] ?? ''));
|
||||
$timezone = trim((string) ($entryPayload['timezone'] ?? ($current['schedule_timezone'] ?? 'UTC')));
|
||||
$timezone = trim((string) ($entryPayload['timezone'] ?? ($current['schedule_timezone'] ?? $globalCronTimezone)));
|
||||
if ($cronExpression === '' && $timezone === '') {
|
||||
continue;
|
||||
}
|
||||
@@ -523,7 +528,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$payload = [];
|
||||
$sectionFieldNames = match ($submittedSetupSection) {
|
||||
'general' => array_map(static fn (array $field): string => (string) ($field['name'] ?? ''), $generalSetupFields),
|
||||
'cron' => array_map(static fn (array $field): string => (string) ($field['name'] ?? ''), $cronSetupFields),
|
||||
'cron' => array_values(array_filter(array_merge(
|
||||
array_map(static fn (array $field): string => (string) ($field['name'] ?? ''), $cronSetupFields),
|
||||
[$cronTimezoneField !== null ? (string) ($cronTimezoneField['name'] ?? '') : '']
|
||||
), static fn (string $name): bool => $name !== '')),
|
||||
'custom' => array_map(static fn (array $field): string => (string) ($field['name'] ?? ''), $customSetupFields),
|
||||
'database' => array_values(array_filter(array_merge(
|
||||
array_map(static fn (array $field): string => (string) ($field['name'] ?? ''), $databaseSetupFields),
|
||||
@@ -583,6 +591,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$postKey = str_replace('.', '_', $name);
|
||||
$value = $_POST[$postKey] ?? null;
|
||||
|
||||
if ($submittedSetupSection === 'cron' && $name === 'schedule_timezone') {
|
||||
if (!isset($_POST['schedule_timezone_custom'])) {
|
||||
$payload[$name] = '';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === 'checkbox') {
|
||||
$value = isset($_POST[$postKey]) ? '1' : '0';
|
||||
}
|
||||
@@ -930,12 +945,31 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
<p class="muted">Hier liegen die zeitbezogenen Modul-Einstellungen, Intervall-Tasks und Cron-Jobs.</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($cronSetupFields !== []): ?>
|
||||
<?php if ($cronTimezoneField !== null || $cronSetupFields !== []): ?>
|
||||
<div class="setup-grid">
|
||||
<?php if ($cronTimezoneField !== null): ?>
|
||||
<div class="setup-field muted">
|
||||
<span>Standard-Zeitzone für dieses Modul</span>
|
||||
<label style="display:flex; align-items:center; gap:10px;">
|
||||
<input type="checkbox" name="schedule_timezone_custom" value="1" <?= $moduleCronTimezoneOverride !== '' ? 'checked' : '' ?> data-module-cron-tz-toggle>
|
||||
<span>Custom-Zeitzone verwenden</span>
|
||||
</label>
|
||||
<div class="setup-db-message setup-db-message--hint">Aktiv: <?= e($effectiveModuleCronTimezone) ?></div>
|
||||
<small class="muted">Wenn deaktiviert, wird die globale Nexus-Cron-Zeitzone verwendet: <?= e($globalCronTimezone) ?>.</small>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($cronSetupFields as $field): ?>
|
||||
<?php $renderField($field); ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if ($cronTimezoneField !== null): ?>
|
||||
<div class="setup-grid" data-module-cron-tz-panel <?= $moduleCronTimezoneOverride !== '' ? '' : 'hidden' ?>>
|
||||
<label class="setup-field muted">
|
||||
<span>Custom-Zeitzone für dieses Modul</span>
|
||||
<input type="text" name="schedule_timezone" value="<?= e($moduleCronTimezoneOverride !== '' ? $moduleCronTimezoneOverride : $globalCronTimezone) ?>" list="timezone-options" autocomplete="off">
|
||||
</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p class="muted">Dieses Modul hat keine eigenen Zeitzonenfelder. Intervall-Tasks und Cron-Jobs koennen trotzdem weiter unten verwaltet werden.</p>
|
||||
<?php endif; ?>
|
||||
@@ -987,7 +1021,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
$cronMode = (string) ($cronDefinition['mode'] ?? 'single');
|
||||
$cronEntries = $cronTaskStatusGroups[$cronName] ?? [];
|
||||
?>
|
||||
<div class="setup-field muted" data-scheduler-job data-job-name="<?= e($cronName) ?>" data-job-mode="<?= e($cronMode) ?>" data-job-label="<?= e((string) ($cronDefinition['label'] ?? $cronName)) ?>" data-job-callback="<?= e((string) ($cronDefinition['callback'] ?? '')) ?>">
|
||||
<div class="setup-field muted" data-scheduler-job data-job-name="<?= e($cronName) ?>" data-job-mode="<?= e($cronMode) ?>" data-job-label="<?= e((string) ($cronDefinition['label'] ?? $cronName)) ?>" data-job-callback="<?= e((string) ($cronDefinition['callback'] ?? '')) ?>" data-default-timezone="<?= e($effectiveModuleCronTimezone) ?>">
|
||||
<span><?= e((string) ($cronDefinition['label'] ?? $cronName)) ?></span>
|
||||
<?php if (trim((string) ($cronDefinition['help'] ?? '')) !== ''): ?>
|
||||
<small class="muted"><?= e((string) $cronDefinition['help']) ?></small>
|
||||
@@ -1002,7 +1036,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
<label><input type="checkbox" name="scheduler_jobs[<?= e($cronName) ?>][entries][<?= e((string) $entryIndex) ?>][enabled]" value="1" data-enabled <?= !empty($cronConfig['enabled']) ? 'checked' : '' ?>> Aktiv</label>
|
||||
<input type="text" name="scheduler_jobs[<?= e($cronName) ?>][entries][<?= e((string) $entryIndex) ?>][cron_expression]" value="<?= e((string) ($cronConfig['cron_expression'] ?? '')) ?>" data-cron-expression>
|
||||
<small class="muted">Cron-Syntax: Minute Stunde Tag Monat Wochentag</small>
|
||||
<input type="text" name="scheduler_jobs[<?= e($cronName) ?>][entries][<?= e((string) $entryIndex) ?>][timezone]" value="<?= e((string) ($cronConfig['timezone'] ?? 'UTC')) ?>" data-cron-timezone>
|
||||
<input type="text" name="scheduler_jobs[<?= e($cronName) ?>][entries][<?= e((string) $entryIndex) ?>][timezone]" value="<?= e((string) (($cronConfig['timezone'] ?? '') !== '' ? $cronConfig['timezone'] : '')) ?>" data-cron-timezone>
|
||||
<select name="scheduler_jobs[<?= e($cronName) ?>][entries][<?= e((string) $entryIndex) ?>][builder][mode]" data-cron-builder-mode>
|
||||
<option value="builder" <?= (string) ($builder['mode'] ?? 'builder') === 'builder' ? 'selected' : '' ?>>Builder</option>
|
||||
<option value="manual" <?= (string) ($builder['mode'] ?? 'builder') === 'manual' ? 'selected' : '' ?>>Cron-Syntax</option>
|
||||
@@ -1267,6 +1301,14 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
</label>
|
||||
<label class="setup-field muted">
|
||||
<span>Zeitzone</span>
|
||||
<label style="display:flex; align-items:center; gap:10px;">
|
||||
<input type="checkbox" value="1" data-modal-timezone-custom>
|
||||
<span>Custom-Zeitzone verwenden</span>
|
||||
</label>
|
||||
<div class="setup-db-message setup-db-message--hint" data-modal-timezone-default>Standard: UTC</div>
|
||||
</label>
|
||||
<label class="setup-field muted" data-modal-timezone-wrap hidden>
|
||||
<span>Custom-Zeitzone</span>
|
||||
<input type="text" value="UTC" data-modal-timezone list="timezone-options" autocomplete="off">
|
||||
</label>
|
||||
|
||||
@@ -1399,6 +1441,16 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const moduleCronToggle = document.querySelector('[data-module-cron-tz-toggle]');
|
||||
const moduleCronPanel = document.querySelector('[data-module-cron-tz-panel]');
|
||||
if (moduleCronToggle && moduleCronPanel) {
|
||||
const syncModuleCronTimezone = () => {
|
||||
moduleCronPanel.hidden = !moduleCronToggle.checked;
|
||||
};
|
||||
moduleCronToggle.addEventListener('change', syncModuleCronTimezone);
|
||||
syncModuleCronTimezone();
|
||||
}
|
||||
|
||||
const jobs = document.querySelectorAll('[data-scheduler-job]');
|
||||
const modal = document.querySelector('[data-scheduler-modal]');
|
||||
if (!jobs.length || !modal) return;
|
||||
@@ -1424,6 +1476,9 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
|
||||
const modalFields = {
|
||||
enabled: modal.querySelector('[data-modal-enabled]'),
|
||||
timezoneCustom: modal.querySelector('[data-modal-timezone-custom]'),
|
||||
timezoneDefault: modal.querySelector('[data-modal-timezone-default]'),
|
||||
timezoneWrap: modal.querySelector('[data-modal-timezone-wrap]'),
|
||||
timezone: modal.querySelector('[data-modal-timezone]'),
|
||||
intervalHours: modal.querySelector('[data-modal-interval-hours]'),
|
||||
monthDay: modal.querySelector('[data-modal-month-day]'),
|
||||
@@ -1497,20 +1552,22 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
|
||||
const buildSummary = (tab) => {
|
||||
const enabled = modalFields.enabled.value === '1' ? 'Aktiv' : 'Inaktiv';
|
||||
const timezone = modalFields.timezone.value.trim() || 'UTC';
|
||||
const timezoneLabel = modalFields.timezoneCustom.checked
|
||||
? `Custom ${modalFields.timezone.value.trim() || 'UTC'}`
|
||||
: `Standard ${modalFields.timezoneDefault.dataset.timezone || 'UTC'}`;
|
||||
const time = formatTime(modalState.hour, modalState.minute);
|
||||
switch (tab) {
|
||||
case 'hourly':
|
||||
return `${enabled}, alle ${modalFields.intervalHours.value || '1'} Stunden um Minute ${modalState.minute}, ${timezone}`;
|
||||
return `${enabled}, alle ${modalFields.intervalHours.value || '1'} Stunden um Minute ${modalState.minute}, ${timezoneLabel}`;
|
||||
case 'weekly':
|
||||
return `${enabled}, woechentlich ${weekdayMap[modalState.weekday || '1']} um ${time}, ${timezone}`;
|
||||
return `${enabled}, woechentlich ${weekdayMap[modalState.weekday || '1']} um ${time}, ${timezoneLabel}`;
|
||||
case 'monthly':
|
||||
return `${enabled}, monatlich am ${modalFields.monthDay.value || '1'}. um ${time}, ${timezone}`;
|
||||
return `${enabled}, monatlich am ${modalFields.monthDay.value || '1'}. um ${time}, ${timezoneLabel}`;
|
||||
case 'custom':
|
||||
return `${enabled}, benutzerdefinierte Cron-Syntax, ${timezone}`;
|
||||
return `${enabled}, benutzerdefinierte Cron-Syntax, ${timezoneLabel}`;
|
||||
case 'daily':
|
||||
default:
|
||||
return `${enabled}, taeglich um ${time}, ${timezone}`;
|
||||
return `${enabled}, taeglich um ${time}, ${timezoneLabel}`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1547,6 +1604,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
};
|
||||
|
||||
const getEntryField = (entry, selector) => entry.querySelector(selector);
|
||||
const entryDefaultTimezone = (entry) => entry.closest('[data-scheduler-job]')?.dataset.defaultTimezone || 'UTC';
|
||||
const findStatusNode = (entry, label) => Array.from(entry.querySelectorAll('small')).find((node) => node.textContent.includes(label));
|
||||
const setStatusText = (entry, label, value) => {
|
||||
const node = findStatusNode(entry, label);
|
||||
@@ -1696,7 +1754,8 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
const updateEntrySummary = (entry) => {
|
||||
const enabled = getEntryField(entry, '[data-enabled]')?.checked ?? false;
|
||||
const expression = getEntryField(entry, '[data-cron-expression]')?.value || '';
|
||||
const timezone = (getEntryField(entry, '[data-cron-timezone]')?.value || 'UTC').trim() || 'UTC';
|
||||
const timezone = (getEntryField(entry, '[data-cron-timezone]')?.value || '').trim();
|
||||
const timezoneLabel = timezone !== '' ? `Custom ${timezone}` : `Standard ${entryDefaultTimezone(entry)}`;
|
||||
const builderMode = getEntryField(entry, '[data-cron-builder-mode]')?.value || 'builder';
|
||||
const builderKind = getEntryField(entry, '[data-cron-builder-kind]')?.value || 'daily';
|
||||
const time = getEntryField(entry, '[data-cron-builder-time]')?.value || '18:00';
|
||||
@@ -1706,15 +1765,15 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
|
||||
let summary = enabled ? 'Aktiv' : 'Inaktiv';
|
||||
if (builderMode === 'manual') {
|
||||
summary += `, Custom, ${timezone}`;
|
||||
summary += `, Custom, ${timezoneLabel}`;
|
||||
} else if (builderKind === 'every_x_hours') {
|
||||
summary += `, alle ${intervalHours} Stunden, ${timezone}`;
|
||||
summary += `, alle ${intervalHours} Stunden, ${timezoneLabel}`;
|
||||
} else if (builderKind === 'weekly') {
|
||||
summary += `, ${weekdayMap[weekday] || weekday} ${time}, ${timezone}`;
|
||||
summary += `, ${weekdayMap[weekday] || weekday} ${time}, ${timezoneLabel}`;
|
||||
} else if (builderKind === 'monthly_day') {
|
||||
summary += `, monatlich am ${monthDay}. ${time}, ${timezone}`;
|
||||
summary += `, monatlich am ${monthDay}. ${time}, ${timezoneLabel}`;
|
||||
} else {
|
||||
summary += `, taeglich ${time}, ${timezone}`;
|
||||
summary += `, taeglich ${time}, ${timezoneLabel}`;
|
||||
}
|
||||
|
||||
let summaryNode = entry.querySelector('[data-entry-summary]');
|
||||
@@ -1784,7 +1843,13 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
const [hour = '18', minute = '00'] = time.split(':');
|
||||
|
||||
modalFields.enabled.value = getEntryField(entry, '[data-enabled]')?.checked ? '1' : '0';
|
||||
modalFields.timezone.value = getEntryField(entry, '[data-cron-timezone]')?.value || 'UTC';
|
||||
const savedTimezone = getEntryField(entry, '[data-cron-timezone]')?.value || '';
|
||||
const defaultTimezone = entryDefaultTimezone(entry);
|
||||
modalFields.timezoneCustom.checked = savedTimezone.trim() !== '';
|
||||
modalFields.timezone.value = savedTimezone || defaultTimezone;
|
||||
modalFields.timezoneDefault.dataset.timezone = defaultTimezone;
|
||||
modalFields.timezoneDefault.textContent = `Standard: ${defaultTimezone}`;
|
||||
modalFields.timezoneWrap.hidden = !modalFields.timezoneCustom.checked;
|
||||
modalFields.intervalHours.value = getEntryField(entry, '[data-cron-builder-interval-hours]')?.value || '6';
|
||||
modalFields.monthDay.value = getEntryField(entry, '[data-cron-builder-month-day]')?.value || '1';
|
||||
modalFields.expression.value = getEntryField(entry, '[data-cron-expression]')?.value || '';
|
||||
@@ -1813,7 +1878,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
tab: modalState.tab,
|
||||
entryIndex: entry.dataset.entryIndex || null,
|
||||
enabled: modalFields.enabled.value,
|
||||
timezone: modalFields.timezone.value,
|
||||
timezone: modalFields.timezoneCustom.checked ? modalFields.timezone.value : '',
|
||||
});
|
||||
|
||||
const modeNode = getEntryField(entry, '[data-cron-builder-mode]');
|
||||
@@ -1833,7 +1898,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
}
|
||||
|
||||
enabledNode.checked = modalFields.enabled.value === '1';
|
||||
timezoneNode.value = modalFields.timezone.value.trim() || 'UTC';
|
||||
timezoneNode.value = modalFields.timezoneCustom.checked ? (modalFields.timezone.value.trim() || entryDefaultTimezone(entry)) : '';
|
||||
timeNode.value = formatTime(modalState.hour, modalState.minute);
|
||||
weekdayNode.value = modalState.weekday;
|
||||
monthDayNode.value = String(Math.max(1, Math.min(31, Number(modalFields.monthDay.value || 1))));
|
||||
@@ -1913,7 +1978,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
|
||||
getEntryField(entry, '[data-enabled]').checked = Boolean(values.enabled);
|
||||
getEntryField(entry, '[data-cron-expression]').value = values.cron_expression || '0 18 * * *';
|
||||
getEntryField(entry, '[data-cron-timezone]').value = values.timezone || 'UTC';
|
||||
getEntryField(entry, '[data-cron-timezone]').value = values.timezone || '';
|
||||
getEntryField(entry, '[data-cron-builder-mode]').value = values.builderMode || 'builder';
|
||||
getEntryField(entry, '[data-cron-builder-kind]').value = values.builderKind || 'daily';
|
||||
getEntryField(entry, '[data-cron-builder-time]').value = values.time || '18:00';
|
||||
@@ -2017,11 +2082,16 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
});
|
||||
});
|
||||
|
||||
[modalFields.enabled, modalFields.timezone, modalFields.intervalHours, modalFields.monthDay, modalFields.expression].forEach((node) => {
|
||||
[modalFields.enabled, modalFields.timezone, modalFields.intervalHours, modalFields.monthDay, modalFields.expression, modalFields.timezoneCustom].forEach((node) => {
|
||||
node?.addEventListener('input', refreshPreview);
|
||||
node?.addEventListener('change', refreshPreview);
|
||||
});
|
||||
|
||||
modalFields.timezoneCustom?.addEventListener('change', () => {
|
||||
modalFields.timezoneWrap.hidden = !modalFields.timezoneCustom.checked;
|
||||
refreshPreview();
|
||||
});
|
||||
|
||||
modal.querySelectorAll('[data-scheduler-close]').forEach((button) => {
|
||||
button.addEventListener('click', closeModal);
|
||||
});
|
||||
@@ -2035,7 +2105,7 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection
|
||||
addButton?.addEventListener('click', () => {
|
||||
const container = job.querySelector('[data-scheduler-entries]');
|
||||
if (!container) return;
|
||||
const entry = createEntry(job, { timezone: container.querySelector('[data-cron-timezone]')?.value || 'UTC' });
|
||||
const entry = createEntry(job, { timezone: '' });
|
||||
container.appendChild(entry);
|
||||
bindEntry(job, entry);
|
||||
reindexJob(job);
|
||||
|
||||
@@ -8,9 +8,35 @@ $themes = [
|
||||
require_auth();
|
||||
|
||||
$current = user_theme();
|
||||
$timezoneOptions = modules()->timezones();
|
||||
$nexusSettings = nexus_settings();
|
||||
$isAdmin = auth_is_admin();
|
||||
$notice = null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$settingsSection = trim((string) ($_POST['settings_section'] ?? 'theme'));
|
||||
if ($settingsSection === 'nexus' && $isAdmin) {
|
||||
$displayTimezoneCustom = isset($_POST['display_timezone_custom']) ? '1' : '0';
|
||||
$displayTimezone = trim((string) ($_POST['display_timezone'] ?? ''));
|
||||
$cronTimezone = trim((string) ($_POST['cron_timezone'] ?? ''));
|
||||
|
||||
foreach (['displayTimezone' => $displayTimezone, 'cronTimezone' => $cronTimezone] as $key => $value) {
|
||||
if ($value !== '') {
|
||||
try {
|
||||
new DateTimeZone($value);
|
||||
} catch (\Throwable) {
|
||||
$$key = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nexusSettings['display_timezone_custom'] = $displayTimezoneCustom;
|
||||
$nexusSettings['display_timezone'] = $displayTimezoneCustom === '1' ? $displayTimezone : '';
|
||||
$nexusSettings['cron_timezone'] = $cronTimezone;
|
||||
nexus_save_settings($nexusSettings);
|
||||
$nexusSettings = nexus_settings();
|
||||
$notice = 'Nexus-Einstellungen gespeichert.';
|
||||
} else {
|
||||
$theme = (string)($_POST['theme'] ?? 'light');
|
||||
if (!isset($themes[$theme])) {
|
||||
$theme = 'light';
|
||||
@@ -18,21 +44,39 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
set_user_theme($theme);
|
||||
$current = $theme;
|
||||
$notice = 'Theme gespeichert.';
|
||||
}
|
||||
}
|
||||
|
||||
$systemTimezone = nexus_system_timezone_name();
|
||||
$effectiveDisplayTimezone = nexus_display_timezone_name();
|
||||
$effectiveCronTimezone = nexus_cron_timezone_name();
|
||||
$displayTimezoneCustom = !empty($nexusSettings['display_timezone_custom']);
|
||||
$savedDisplayTimezone = trim((string) ($nexusSettings['display_timezone'] ?? ''));
|
||||
$savedCronTimezone = trim((string) ($nexusSettings['cron_timezone'] ?? ''));
|
||||
?>
|
||||
<div class="card">
|
||||
<div class="pill">Einstellungen</div>
|
||||
<h1 style="margin-top:.75rem;">User-Design</h1>
|
||||
<p class="muted">Wähle deine persönliche Farbpalette.</p>
|
||||
<div class="module-shell"><div class="module-page-bg"><div class="module-page-stack">
|
||||
<section class="section-box">
|
||||
<h1>Nexus Einstellungen</h1>
|
||||
<p class="muted">Persönliche Anzeige und systemweite Standardwerte.</p>
|
||||
|
||||
<?php if ($notice): ?>
|
||||
<div class="card" style="margin-top:1rem; border-color:var(--accent-2);">
|
||||
<div class="section-box" style="margin-top:1rem; border-color:var(--accent-2);">
|
||||
<?= e($notice) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" style="margin-top:1rem; display:grid; gap:12px; max-width:360px;">
|
||||
<label class="muted" style="display:grid; gap:6px;">
|
||||
<div class="setup-form" style="margin-top:1rem;">
|
||||
<section class="section-box setup-panel">
|
||||
<div class="setup-panel__head">
|
||||
<div>
|
||||
<span class="pill">Benutzer</span>
|
||||
<h2>Persönliches Design</h2>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="setup-form">
|
||||
<input type="hidden" name="settings_section" value="theme">
|
||||
<div class="setup-grid">
|
||||
<label class="setup-field muted">
|
||||
<span>Farbpalette</span>
|
||||
<select name="theme">
|
||||
<?php foreach ($themes as $key => $label): ?>
|
||||
@@ -42,6 +86,77 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button class="cta-button" type="submit">Speichern</button>
|
||||
</div>
|
||||
<div class="setup-actions setup-actions--footer">
|
||||
<button class="cta-button" type="submit">Design speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($isAdmin): ?>
|
||||
<section class="section-box setup-panel">
|
||||
<div class="setup-panel__head">
|
||||
<div>
|
||||
<span class="pill">Nexus</span>
|
||||
<h2>Standard-Zeitzonen</h2>
|
||||
<p class="muted">Diese Werte werden von Modulen als Default übernommen, sofern dort kein eigener Override gesetzt ist.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="setup-form">
|
||||
<input type="hidden" name="settings_section" value="nexus">
|
||||
<datalist id="nexus-timezone-options">
|
||||
<?php foreach ($timezoneOptions as $timezoneOption): ?>
|
||||
<option value="<?= e((string) $timezoneOption['value']) ?>"><?= e((string) $timezoneOption['label']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<div class="setup-grid">
|
||||
<div class="setup-field muted">
|
||||
<span>System-Zeitzone</span>
|
||||
<div><?= e($systemTimezone) ?></div>
|
||||
<small class="muted">Diese Zeitzone wird genutzt, wenn keine globale Anzeige-Zeitzone gesetzt ist.</small>
|
||||
</div>
|
||||
<div class="setup-field muted">
|
||||
<span>Anzeige-Zeitzone</span>
|
||||
<label style="display:flex; align-items:center; gap:10px;">
|
||||
<input type="checkbox" name="display_timezone_custom" value="1" <?= $displayTimezoneCustom ? 'checked' : '' ?> data-global-display-tz-toggle>
|
||||
<span>Custom-Zeitzone verwenden</span>
|
||||
</label>
|
||||
<div class="setup-db-message setup-db-message--hint">Aktiv: <?= e($effectiveDisplayTimezone) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setup-grid" data-global-display-tz-panel <?= $displayTimezoneCustom ? '' : 'hidden' ?>>
|
||||
<label class="setup-field muted">
|
||||
<span>Custom Anzeige-Zeitzone</span>
|
||||
<input type="text" name="display_timezone" value="<?= e($savedDisplayTimezone !== '' ? $savedDisplayTimezone : $effectiveDisplayTimezone) ?>" list="nexus-timezone-options" autocomplete="off">
|
||||
</label>
|
||||
</div>
|
||||
<div class="setup-grid">
|
||||
<label class="setup-field muted">
|
||||
<span>Standard-Zeitzone für Crons</span>
|
||||
<input type="text" name="cron_timezone" value="<?= e($savedCronTimezone !== '' ? $savedCronTimezone : $effectiveCronTimezone) ?>" list="nexus-timezone-options" autocomplete="off">
|
||||
<small class="muted">Wird in Modul-Crons als Standard verwendet. Einzelne Module oder Einträge können das übersteuern.</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setup-actions setup-actions--footer">
|
||||
<button class="cta-button" type="submit">Nexus-Einstellungen speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
</div></div></div>
|
||||
<?php if ($isAdmin): ?>
|
||||
<script>
|
||||
(() => {
|
||||
const toggle = document.querySelector('[data-global-display-tz-toggle]');
|
||||
const panel = document.querySelector('[data-global-display-tz-panel]');
|
||||
if (!toggle || !panel) return;
|
||||
const sync = () => {
|
||||
panel.hidden = !toggle.checked;
|
||||
};
|
||||
toggle.addEventListener('change', sync);
|
||||
sync();
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -306,6 +306,9 @@ final class ModuleCronScheduler
|
||||
$job = $jobExists ? $jobs[$definition['name']] : [];
|
||||
$timezoneSetting = trim((string) ($definition['timezone_setting'] ?? ''));
|
||||
$fallbackTimezone = $timezoneSetting !== '' ? trim((string) ($settings[$timezoneSetting] ?? '')) : '';
|
||||
if ($fallbackTimezone === '' && function_exists('nexus_cron_timezone_name')) {
|
||||
$fallbackTimezone = trim((string) nexus_cron_timezone_name());
|
||||
}
|
||||
$defaultEntry = [
|
||||
'enabled' => (bool) $definition['default_enabled'],
|
||||
'cron_expression' => trim((string) ($definition['default_cron'] ?? '0 * * * *')),
|
||||
@@ -336,7 +339,7 @@ final class ModuleCronScheduler
|
||||
$result[] = [
|
||||
'enabled' => array_key_exists('enabled', $entry) ? $this->settingBool($entry['enabled'], (bool) $defaultEntry['enabled']) : (bool) $defaultEntry['enabled'],
|
||||
'cron_expression' => trim((string) ($entry['cron_expression'] ?? $defaultEntry['cron_expression'])),
|
||||
'timezone' => trim((string) ($entry['timezone'] ?? $defaultEntry['timezone'])),
|
||||
'timezone' => (($entryTimezone = trim((string) ($entry['timezone'] ?? ''))) !== '' ? $entryTimezone : $defaultEntry['timezone']),
|
||||
'builder' => is_array($entry['builder'] ?? null) ? $entry['builder'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -336,6 +336,66 @@ function nexus_debug_clear(?string $source = null): void
|
||||
}));
|
||||
}
|
||||
|
||||
function nexus_settings(): array
|
||||
{
|
||||
try {
|
||||
return modules()->settings('_nexus');
|
||||
} catch (\Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function nexus_save_settings(array $settings): void
|
||||
{
|
||||
modules()->saveSettings('_nexus', $settings);
|
||||
}
|
||||
|
||||
function nexus_system_timezone_name(): string
|
||||
{
|
||||
$timezone = trim((string) date_default_timezone_get());
|
||||
if ($timezone === '') {
|
||||
return 'UTC';
|
||||
}
|
||||
|
||||
try {
|
||||
new \DateTimeZone($timezone);
|
||||
return $timezone;
|
||||
} catch (\Throwable) {
|
||||
return 'UTC';
|
||||
}
|
||||
}
|
||||
|
||||
function nexus_display_timezone_name(): string
|
||||
{
|
||||
$settings = nexus_settings();
|
||||
$useCustom = !empty($settings['display_timezone_custom']);
|
||||
$timezone = trim((string) ($settings['display_timezone'] ?? ''));
|
||||
if ($useCustom && $timezone !== '') {
|
||||
try {
|
||||
new \DateTimeZone($timezone);
|
||||
return $timezone;
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
return nexus_system_timezone_name();
|
||||
}
|
||||
|
||||
function nexus_cron_timezone_name(): string
|
||||
{
|
||||
$settings = nexus_settings();
|
||||
$timezone = trim((string) ($settings['cron_timezone'] ?? ''));
|
||||
if ($timezone !== '') {
|
||||
try {
|
||||
new \DateTimeZone($timezone);
|
||||
return $timezone;
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
return nexus_display_timezone_name();
|
||||
}
|
||||
|
||||
function module_debug_enabled(string $module): bool
|
||||
{
|
||||
if (preg_match('/[^a-zA-Z0-9_\-]/', $module)) {
|
||||
|
||||
Reference in New Issue
Block a user