204 lines
11 KiB
PHP
204 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
function render_rule_form(AppContext $ctx, string $mode, ?array $rule): string
|
|
{
|
|
$isUpdate = $mode === 'update';
|
|
$id = $isUpdate ? (string)$rule['id'] : '';
|
|
$subjectPrefix = $rule['subject_prefix'] ?? $rule['subject'] ?? '';
|
|
$replyFrequency = selected_reply_frequency($rule);
|
|
$body = $rule['body'] ?? '';
|
|
$enabled = $rule === null || !empty($rule['enabled']);
|
|
$startValue = (string)($rule['start_value'] ?? default_schedule_value($ctx, true));
|
|
$endValue = (string)($rule['end_value'] ?? default_schedule_value($ctx, false));
|
|
$action = $ctx->url($isUpdate ? 'update.html' : 'create.html');
|
|
$intent = $isUpdate ? 'update:' . $id : 'create';
|
|
$csrf = $ctx->csrfField($intent);
|
|
$hiddenId = $isUpdate ? '<input type="hidden" name="id" value="' . AppContext::e($id) . '">' : '';
|
|
|
|
return '<form method="post" action="' . AppContext::e($action) . '">' . $csrf . $hiddenId .
|
|
'<div class="panel"><h3>' . AppContext::e($ctx->t('Response content')) . '</h3><div class="row"><div><label>' . AppContext::e($ctx->t('Subject prefix')) . '</label><div class="subject-prefix-control"><input type="text" name="subject_prefix" value="' . AppContext::e((string)$subjectPrefix) . '"><span>' . AppContext::e($ctx->t('Original subject suffix')) . '</span></div></div>' .
|
|
'<div><label>' . AppContext::e($ctx->t('Response frequency')) . '</label>' . render_reply_frequency_select($ctx, $replyFrequency) . '</div>' .
|
|
'<div><label>' . AppContext::e($ctx->t('Status')) . '</label><label class="br-time-item"><input type="checkbox" name="enabled" value="1" ' . ($enabled ? 'checked' : '') . '> ' . AppContext::e($ctx->t('Enabled')) . '</label></div></div>' .
|
|
'<label>' . AppContext::e($ctx->t('Message body')) . '</label><textarea name="body">' . AppContext::e((string)$body) . '</textarea></div>' .
|
|
render_calendar_picker($ctx, 'start_value', $ctx->t('Start date'), $startValue) .
|
|
render_calendar_picker($ctx, 'end_value', $ctx->t('End date'), $endValue) .
|
|
'<div class="panel"><div class="actions"><button class="primary" type="submit">' . AppContext::e($ctx->t($isUpdate ? 'Save changes' : 'Save autoresponder')) . '</button><a class="btn" href="' . AppContext::e($ctx->url('index.html')) . '">' . AppContext::e($ctx->t('Cancel')) . '</a></div></div>' .
|
|
'</form>';
|
|
}
|
|
|
|
function default_schedule_value(AppContext $ctx, bool $start): string
|
|
{
|
|
$today = new DateTimeImmutable('today', new DateTimeZone($ctx->settings->timezone()));
|
|
if ($ctx->settings->scheduleMode() === 'directadmin_period') {
|
|
return $today->format('Y-m-d') . '|' . ($start ? 'morning' : 'evening');
|
|
}
|
|
return $today->format('Y-m-d') . 'T' . ($start ? '09:00' : '17:00');
|
|
}
|
|
|
|
/** @param array<string,mixed>|null $rule */
|
|
function selected_reply_frequency(?array $rule): string
|
|
{
|
|
if ($rule === null) {
|
|
return '1440';
|
|
}
|
|
if (!empty($rule['reply_every_message'])) {
|
|
return 'every';
|
|
}
|
|
$minutes = (string)(int)($rule['repeat_minutes'] ?? 1440);
|
|
return in_array($minutes, ['30', '60', '120', '360', '720', '1440', '2880', '10080'], true) ? $minutes : '1440';
|
|
}
|
|
|
|
function render_reply_frequency_select(AppContext $ctx, string $selected): string
|
|
{
|
|
$options = [
|
|
'every' => 'Send automatic reply for every message',
|
|
'30' => '30 minutes',
|
|
'60' => '1 hour',
|
|
'120' => '2 hours',
|
|
'360' => '6 hours',
|
|
'720' => '12 hours',
|
|
'1440' => '1 day',
|
|
'2880' => '2 days',
|
|
'10080' => '7 days',
|
|
];
|
|
$html = '<select name="reply_frequency">';
|
|
foreach ($options as $value => $label) {
|
|
$value = (string)$value;
|
|
$html .= '<option value="' . AppContext::e($value) . '"' . ($selected === $value ? ' selected' : '') . '>' . AppContext::e($ctx->t($label)) . '</option>';
|
|
}
|
|
return $html . '</select>';
|
|
}
|
|
|
|
/** @param array<string,mixed> $post @return array<string,mixed> */
|
|
function normalize_rule_post(array $post): array
|
|
{
|
|
foreach (['start_value', 'end_value'] as $key) {
|
|
$date = trim((string)($post[$key . '_date'] ?? ''));
|
|
$time = trim((string)($post[$key . '_time'] ?? ''));
|
|
$period = trim((string)($post[$key . '_period'] ?? ''));
|
|
if ($date !== '' && preg_match('/^[0-9]{2}:[0-9]{2}$/', $time)) {
|
|
$post[$key] = $date . 'T' . $time;
|
|
} elseif ($date !== '' && preg_match('/^(morning|afternoon|evening)$/', $period)) {
|
|
$post[$key] = $date . '|' . $period;
|
|
}
|
|
}
|
|
return $post;
|
|
}
|
|
|
|
/** @param array<string,mixed> $rule @return array<string,mixed> */
|
|
function attach_rule_scope(AppContext $ctx, array $rule): array
|
|
{
|
|
if (empty($rule['dynamic_scope'])) {
|
|
$rule['mailbox_snapshot'] = (new MailboxDirectory())->mailboxesForUser($ctx->daUser->username());
|
|
}
|
|
return $rule;
|
|
}
|
|
|
|
/** @param array<string,mixed> $rule */
|
|
function enforce_rule_backend_constraints(AppContext $ctx, ?string $currentId, array $rule): void
|
|
{
|
|
if ($ctx->settings->backendMode() !== 'da' || empty($rule['enabled'])) {
|
|
return;
|
|
}
|
|
foreach ($ctx->rules->all($ctx->daUser->username()) as $existing) {
|
|
if (empty($existing['enabled'])) {
|
|
continue;
|
|
}
|
|
if ($currentId !== null && (string)($existing['id'] ?? '') === $currentId) {
|
|
continue;
|
|
}
|
|
throw new RuntimeException($ctx->t('DirectAdmin backend supports only one enabled global rule.'));
|
|
}
|
|
}
|
|
|
|
function render_calendar_picker(AppContext $ctx, string $name, string $title, string $value): string
|
|
{
|
|
$mode = $ctx->settings->scheduleMode();
|
|
$selectedDate = substr($value, 0, 10);
|
|
if (!preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $selectedDate)) {
|
|
$selectedDate = date('Y-m-d');
|
|
}
|
|
$selectedTime = '09:00';
|
|
$selectedPeriod = 'morning';
|
|
if ($mode === 'exact_hour' && preg_match('/T([0-9]{2}:[0-9]{2})$/', $value, $m)) {
|
|
$selectedTime = $m[1];
|
|
} elseif ($mode === 'directadmin_period' && preg_match('/\|(morning|afternoon|evening)$/', $value, $m)) {
|
|
$selectedPeriod = $m[1];
|
|
}
|
|
$selectedDt = DateTimeImmutable::createFromFormat('!Y-m-d', $selectedDate) ?: new DateTimeImmutable('today');
|
|
$monthStart = $selectedDt->modify('first day of this month');
|
|
$minDate = (new DateTimeImmutable('today', new DateTimeZone($ctx->settings->timezone())))->format('Y-m-d');
|
|
$days = render_calendar_days($name, $selectedDate, $monthStart, $minDate);
|
|
$visibleMonth = $monthStart->format('Y-m');
|
|
$monthLabel = calendar_month_label($ctx, $monthStart);
|
|
if ($mode === 'exact_hour') {
|
|
$timeControl = '<label>' . AppContext::e($ctx->t('Time')) . '</label><input type="time" name="' . AppContext::e($name . '_time') . '" value="' . AppContext::e($selectedTime) . '" step="60" data-time-for="' . AppContext::e($name) . '">';
|
|
} else {
|
|
$timeControl = '<label>' . AppContext::e($ctx->t('Time of day')) . '</label><select name="' . AppContext::e($name . '_period') . '" data-period-for="' . AppContext::e($name) . '">' .
|
|
'<option value="morning"' . ($selectedPeriod === 'morning' ? ' selected' : '') . '>' . AppContext::e($ctx->t('Morning')) . '</option>' .
|
|
'<option value="afternoon"' . ($selectedPeriod === 'afternoon' ? ' selected' : '') . '>' . AppContext::e($ctx->t('Afternoon')) . '</option>' .
|
|
'<option value="evening"' . ($selectedPeriod === 'evening' ? ' selected' : '') . '>' . AppContext::e($ctx->t('Evening')) . '</option>' .
|
|
'</select>';
|
|
}
|
|
return '<div class="panel"><h3>' . AppContext::e($title) . '</h3><div class="br-restore-layout"><div class="br-restore-calendar" data-calendar-picker data-schedule-mode="' . AppContext::e($mode) . '" data-calendar-name="' . AppContext::e($name) . '" data-visible-month="' . AppContext::e($visibleMonth) . '" data-selected-date="' . AppContext::e($selectedDate) . '" data-min-date="' . AppContext::e($minDate) . '"><h4>' . AppContext::e($title) . '</h4><div class="br-month-nav"><button type="button" class="br-month-btn" data-calendar-prev>‹</button><span class="br-month-label" data-calendar-month-label>' . AppContext::e($monthLabel) . '</span><button type="button" class="br-month-btn" data-calendar-next>›</button></div>' .
|
|
'<table class="br-calendar-grid"><thead><tr>' . render_weekday_headers($ctx) . '</tr></thead><tbody data-calendar-days>' . $days . '</tbody></table>' .
|
|
'<div class="br-date-label" id="' . AppContext::e($name . '_label') . '">' . AppContext::e($ctx->t('Selected')) . ': ' . AppContext::e($selectedDate) . '</div><input type="hidden" id="' . AppContext::e($name . '_date') . '" name="' . AppContext::e($name . '_date') . '" value="' . AppContext::e($selectedDate) . '"></div>' .
|
|
'<div class="br-restore-calendar"><h4>' . AppContext::e($ctx->t('Time')) . '</h4>' . $timeControl . '</div></div>' .
|
|
'<input type="hidden" id="' . AppContext::e($name) . '" name="' . AppContext::e($name) . '" value="' . AppContext::e($value) . '" data-canonical-value="' . AppContext::e($name) . '"></div>';
|
|
}
|
|
|
|
function render_preview_box(AppContext $ctx, array $rule): string
|
|
{
|
|
$subjectPrefix = (string)($rule['subject_prefix'] ?? $rule['subject'] ?? '');
|
|
return '<div class="preview-box"><div><strong>' . AppContext::e($ctx->t('Subject prefix')) . ':</strong> ' . AppContext::e($subjectPrefix) . '</div><br><div>' . nl2br(AppContext::e((string)$rule['body'])) . '</div></div>';
|
|
}
|
|
|
|
function render_weekday_headers(AppContext $ctx): string
|
|
{
|
|
$html = '';
|
|
foreach (['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as $day) {
|
|
$html .= '<th>' . AppContext::e($ctx->t($day)) . '</th>';
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
function render_calendar_days(string $name, string $selectedDate, DateTimeImmutable $monthStart, string $minDate): string
|
|
{
|
|
$offset = ((int)$monthStart->format('N')) - 1;
|
|
$daysInMonth = (int)$monthStart->format('t');
|
|
$html = '';
|
|
$day = 1;
|
|
for ($week = 0; $week < 6; $week++) {
|
|
$html .= '<tr>';
|
|
for ($i = 0; $i < 7; $i++) {
|
|
if (($week === 0 && $i < $offset) || $day > $daysInMonth) {
|
|
$html .= '<td class="br-cal-empty"></td>';
|
|
continue;
|
|
}
|
|
$date = $monthStart->setDate((int)$monthStart->format('Y'), (int)$monthStart->format('m'), $day)->format('Y-m-d');
|
|
if ($date < $minDate) {
|
|
$html .= '<td><span class="br-cal-day-disabled">' . $day . '</span></td>';
|
|
$day++;
|
|
continue;
|
|
}
|
|
$class = $date === $selectedDate ? ' is-selected' : '';
|
|
$html .= '<td><button type="button" class="br-cal-day' . $class . '" data-date-target="' . AppContext::e($name . '_date') . '" data-date-label="' . AppContext::e($name . '_label') . '" data-date-value="' . AppContext::e($date) . '">' . $day . '</button></td>';
|
|
$day++;
|
|
}
|
|
$html .= '</tr>';
|
|
if ($day > $daysInMonth) {
|
|
break;
|
|
}
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
function calendar_month_label(AppContext $ctx, DateTimeImmutable $monthStart): string
|
|
{
|
|
$pl = ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'];
|
|
$en = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
$months = $ctx->lang->code() === 'pl' ? $pl : $en;
|
|
return $months[((int)$monthStart->format('n')) - 1] . ' ' . $monthStart->format('Y');
|
|
}
|