feat: implement global autoresponder plugin
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
final class AppContext
|
||||
{
|
||||
public function __construct(
|
||||
public DirectAdminUser $daUser,
|
||||
public Settings $settings,
|
||||
public RuleRepository $rules,
|
||||
public CsrfGuard $csrf,
|
||||
public Lang $lang,
|
||||
public AuditLog $audit,
|
||||
) {
|
||||
}
|
||||
|
||||
public function action(): string
|
||||
{
|
||||
return (string)PLUGIN_ACTION;
|
||||
}
|
||||
|
||||
public function baseUrl(): string
|
||||
{
|
||||
return '/CMD_PLUGINS/global-autoresponder';
|
||||
}
|
||||
|
||||
/** @param array<string,string|int> $query */
|
||||
public function url(string $page, array $query = []): string
|
||||
{
|
||||
$url = $this->baseUrl() . '/' . ltrim($page, '/');
|
||||
if ($query !== []) {
|
||||
$url .= '?' . http_build_query($query);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function t(string $key): string
|
||||
{
|
||||
return $this->lang->t($key);
|
||||
}
|
||||
|
||||
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 '<input type="hidden" name="csrf_ts" value="' . self::e((string)$issued['ts']) . '">' .
|
||||
'<input type="hidden" name="csrf_sid" value="' . self::e($issued['sid']) . '">' .
|
||||
'<input type="hidden" name="csrf_token" value="' . self::e($issued['token']) . '">';
|
||||
}
|
||||
|
||||
public function requireCsrf(string $intent): void
|
||||
{
|
||||
if (!$this->csrf->validate($intent, $this->post('csrf_ts'), $this->post('csrf_token'), 3600, $this->post('csrf_sid'))) {
|
||||
throw new RuntimeException('Nieprawidłowy lub wygasły token CSRF.');
|
||||
}
|
||||
}
|
||||
|
||||
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 .= '<div class="alert alert-ok">' . self::e((string)$msg) . '</div>';
|
||||
}
|
||||
foreach ($errors as $msg) {
|
||||
$alerts .= '<div class="alert alert-err">' . self::e((string)$msg) . '</div>';
|
||||
}
|
||||
echo '<!doctype html><html lang="' . self::e($this->lang->code()) . '"><head><meta charset="utf-8">';
|
||||
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
|
||||
echo '<title>' . self::e($this->t($title)) . '</title><style>' . $this->styles() . '</style></head><body>';
|
||||
echo '<div class="wrap"><div class="breadcrumbs">' . self::e($this->t('Dashboard')) . ' <span>></span> ' . self::e($this->t('Global autoresponders')) . '</div>';
|
||||
echo '<header><h1>' . self::e($this->t($title)) . '</h1><p>' . self::e($this->t('Manage automatic replies for account mailboxes')) . '</p></header>';
|
||||
echo '<div class="top-actions"><a class="btn amber' . ($this->action() === 'index' ? ' active' : '') . '" href="' . self::e($this->url('index.html')) . '">' . self::e($this->t('AUTORESPONDERS')) . '</a>';
|
||||
echo '<a class="btn amber' . ($this->action() === 'create' ? ' active' : '') . '" href="' . self::e($this->url('create.html')) . '">' . self::e($this->t('ADD AUTORESPONDER')) . '</a></div>';
|
||||
echo '<section class="main-section">' . $alerts . $this->lang->translateHtml($body) . '</section></div>';
|
||||
echo '<script>' . $this->scripts() . '</script></body></html>';
|
||||
}
|
||||
|
||||
public static function e(string $value): string
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
private function styles(): string
|
||||
{
|
||||
$skin = $this->daUser->skin();
|
||||
$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
|
||||
{
|
||||
return <<<'JS'
|
||||
(function () {
|
||||
document.querySelectorAll('[data-date-value]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var target = document.getElementById(btn.getAttribute('data-date-target'));
|
||||
var label = document.getElementById(btn.getAttribute('data-date-label'));
|
||||
if (target) { target.value = btn.getAttribute('data-date-value') || ''; }
|
||||
if (label) { label.textContent = 'Wybrano: ' + (btn.getAttribute('data-date-value') || ''); }
|
||||
btn.closest('.br-restore-calendar').querySelectorAll('.br-cal-day').forEach(function (d) { d.classList.remove('is-selected'); });
|
||||
btn.classList.add('is-selected');
|
||||
});
|
||||
});
|
||||
})();
|
||||
JS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user