asdas
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user