$query */ public function url(string $page, array $query = []): string { $url = $this->baseUrl() . '/' . ltrim($page, '/'); if ($query !== []) { $url .= '?' . http_build_query($query); } return $url; } /** @param array $vars */ public function t(string $key, array $vars = []): string { return $this->lang->t($key, $vars); } public function post(string $key, string $default = ''): string { return Http::getString($_POST, $key, $default); } public function query(string $key, string $default = ''): string { return Http::getString($_GET, $key, $default); } public function requirePost(): void { if (!Http::isPost()) { throw new RuntimeException('Method not allowed'); } } public function csrfField(string $intent): string { $issued = $this->csrf->issue($intent); return '' . '' . ''; } public function requireCsrf(string $intent): void { if (!$this->csrf->validate($intent, $this->post('gar_csrf_ts'), $this->post('gar_csrf_token'), 3600, $this->post('gar_csrf_sid'))) { throw new RuntimeException('Nieprawidłowy lub wygasły token CSRF.'); } } public function syncDirectAdminVacations(): void { $service = new DirectAdminVacationSyncService( $this->rules, new DirectAdminSyncRepository(), new MailboxDirectory(), new DirectAdminVacationApi() ); $service->syncUser($this->daUser->username()); } public function render(string $title, string $body, array $ok = [], array $errors = []): void { header('Content-Type: text/html; charset=UTF-8'); $alerts = ''; foreach ($ok as $msg) { $alerts .= '
' . self::e((string)$msg) . '
'; } foreach ($errors as $msg) { $alerts .= '
' . self::e((string)$msg) . '
'; } echo ''; echo ''; echo '' . self::e($this->t($title)) . ''; echo '
'; echo '

' . self::e($this->t($title)) . '

'; echo ''; echo '
' . $alerts . $body . '
'; echo ''; } public static function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } private function styles(): string { $skin = $this->daUser->skin(); if ($skin === 'evolution') { $base = PLUGIN_ROOT . '/user/enhanced.css'; $override = PLUGIN_ROOT . '/user/evolution.css'; return (is_file($base) ? (string)file_get_contents($base) : '') . "\n" . (is_file($override) ? (string)file_get_contents($override) : ''); } $path = PLUGIN_ROOT . '/user/' . $skin . '.css'; if (!is_file($path)) { $path = PLUGIN_ROOT . '/user/enhanced.css'; } return is_file($path) ? (string)file_get_contents($path) : ''; } private function scripts(): string { $skin = json_encode($this->daUser->skin(), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); $selectedLabel = json_encode($this->t('Selected'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); $monthNames = json_encode($this->lang->code() === 'pl' ? ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'] : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); return << days) { html += ''; continue; } var value = year + '-' + pad(month) + '-' + pad(day); if (minDate && value < minDate) { html += '' + day + ''; day++; continue; } var cls = value === selected ? ' is-selected' : ''; html += ''; day++; } html += ''; if (day > days) { break; } } tbody.innerHTML = html; label.textContent = monthNames[month - 1] + ' ' + year; calendar.setAttribute('data-visible-month', year + '-' + pad(month)); tbody.querySelectorAll('[data-date-value]').forEach(bindDateButton); } document.querySelectorAll('[data-date-value]').forEach(function (btn) { bindDateButton(btn); }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', expandDirectAdminPluginContainers); } else { expandDirectAdminPluginContainers(); } document.querySelectorAll('[data-calendar-picker]').forEach(function (calendar) { var name = calendar.getAttribute('data-calendar-name'); calendar.querySelectorAll('[data-calendar-prev],[data-calendar-next]').forEach(function (btn) { btn.addEventListener('click', function () { var parts = (calendar.getAttribute('data-visible-month') || '').split('-'); var year = parseInt(parts[0], 10); var month = parseInt(parts[1], 10); if (!year || !month) { return; } month += btn.hasAttribute('data-calendar-next') ? 1 : -1; if (month < 1) { month = 12; year--; } if (month > 12) { month = 1; year++; } renderMonth(calendar, year, month); }); }); document.querySelectorAll('[data-time-for="' + name + '"],[data-period-for="' + name + '"]').forEach(function (control) { control.addEventListener('change', function () { updateCanonical(name); }); control.addEventListener('input', function () { updateCanonical(name); }); }); }); document.querySelectorAll('[data-modal-close]').forEach(function (btn) { btn.addEventListener('click', function () { var overlay = btn.closest('.br-overlay'); if (overlay) { overlay.style.display = 'none'; } document.body.classList.remove('modal-open'); }); }); if (document.querySelector('.br-overlay')) { document.body.classList.add('modal-open'); } document.addEventListener('keydown', function (event) { if (event.key !== 'Escape') { return; } document.querySelectorAll('.br-overlay').forEach(function (overlay) { overlay.style.display = 'none'; }); document.body.classList.remove('modal-open'); }); })(); JS; } }