108 lines
5.9 KiB
PHP
108 lines
5.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace FDMTech;
|
|
|
|
final class View
|
|
{
|
|
public function __construct(private string $root) {}
|
|
|
|
public static function escape(mixed $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
|
|
}
|
|
|
|
public function render(array $data, ?string $applicationHtml = null): string
|
|
{
|
|
$e = self::escape(...);
|
|
extract($data, EXTR_SKIP);
|
|
$cmsHtml = Content::html((string) ($page['published_html'] ?? ''));
|
|
$css = Content::css((string) ($page['published_css'] ?? ''));
|
|
$cmsStyleTag = $css === '' ? '' : '<style nonce="' . $e($nonce) . '">' . $css . '</style>';
|
|
if ($applicationHtml !== null) {
|
|
$inner = $applicationHtml;
|
|
} elseif ($isDemo && $page['slug'] === 'home' && is_file($this->root . '/templates/home.php')) {
|
|
ob_start();
|
|
try {
|
|
require $this->root . '/templates/home.php';
|
|
$inner = ob_get_contents();
|
|
} finally {
|
|
ob_end_clean();
|
|
}
|
|
} else {
|
|
$inner = $cmsHtml;
|
|
}
|
|
$content = '<main id="cms-content">' . $inner . '</main>';
|
|
if ($isDemo) {
|
|
$content = '<aside role="note">Tryb demonstracyjny — formularz nie wysyła zgłoszeń.</aside>' . $content;
|
|
}
|
|
if (is_file($this->root . '/templates/layout.php')) {
|
|
ob_start();
|
|
try {
|
|
require $this->root . '/templates/layout.php';
|
|
return ob_get_contents();
|
|
} finally {
|
|
ob_end_clean();
|
|
}
|
|
}
|
|
// Technical fallback while the independent designer supplies the layout.
|
|
$navigation = '';
|
|
foreach ($site['navigation'] as $item) {
|
|
$navigation .= '<a href="' . $e($item['url']) . '">' . $e($item['label']) . '</a> ';
|
|
}
|
|
return '<!doctype html><html lang="pl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
. '<title>' . $e($meta['title']) . '</title><meta name="description" content="' . $e($meta['description']) . '">'
|
|
. '<meta name="robots" content="' . $e($meta['robots']) . '">'
|
|
. ($meta['canonical'] !== '' ? '<link rel="canonical" href="' . $e($meta['canonical']) . '">' : '')
|
|
. '<link rel="stylesheet" href="/assets/css/site.css?v=4">' . $cmsStyleTag . '</head><body>'
|
|
. '<header><a href="/">' . $e($site['name']) . '</a><nav aria-label="Nawigacja główna">' . $navigation . '</nav></header>'
|
|
. $content . '<footer><p>' . $e($site['name']) . '</p>'
|
|
. ($site['contact_email'] !== '' ? '<a href="mailto:' . $e($site['contact_email']) . '">' . $e($site['contact_email']) . '</a>' : '')
|
|
. ($site['contact_phone'] !== '' ? '<p>' . $e($site['contact_phone']) . '</p>' : '')
|
|
. '</footer><script src="/assets/js/site.js?v=4" defer></script></body></html>';
|
|
}
|
|
|
|
public static function form(array $data): string
|
|
{
|
|
$e = self::escape(...);
|
|
$errors = $data['formErrors'];
|
|
$values = $data['formValues'];
|
|
$html = '<section class="inquiry"><h1>Porozmawiajmy o Twoim projekcie</h1><p>Opisz, czego potrzebujesz. Zgłoszenie trafi do zespołu FDMTech.</p>';
|
|
if ($data['formNotice']) {
|
|
$html .= '<p role="status">' . $e($data['formNotice']) . '</p>';
|
|
}
|
|
if ($errors) {
|
|
$html .= '<div role="alert"><h2>Sprawdź formularz</h2><ul>';
|
|
foreach ($errors as $field => $error) {
|
|
$html .= '<li>' . $e($error) . '</li>';
|
|
}
|
|
$html .= '</ul></div>';
|
|
}
|
|
$html .= '<form method="post" action="/inquiries"><input type="hidden" name="_csrf" value="' . $e($data['csrfToken']) . '">'
|
|
. '<input type="hidden" name="source" value="audit"><div hidden aria-hidden="true"><label>Twoja strona<input name="website" tabindex="-1" autocomplete="off"></label></div>';
|
|
foreach (['name' => ['Imię i nazwisko', 160, 'text', 'name'], 'email' => ['E-mail', 254, 'email', 'email'],
|
|
'company' => ['Firma (opcjonalnie)', 200, 'text', 'organization'], 'phone' => ['Telefon (opcjonalnie)', 60, 'tel', 'tel'],
|
|
'message' => ['Wiadomość', 10000, 'textarea', 'off']] as $field => [$label, $max, $type, $autocomplete]) {
|
|
$attributes = ' id="inquiry-' . $field . '" name="' . $field . '" maxlength="' . $max . '" autocomplete="' . $autocomplete . '"'
|
|
. (in_array($field, ['name', 'email', 'message'], true) ? ' required' : '')
|
|
. (isset($errors[$field]) ? ' aria-invalid="true" aria-describedby="error-' . $field . '"' : '');
|
|
$html .= '<div><label for="inquiry-' . $field . '">' . $label . '</label>';
|
|
$html .= $type === 'textarea' ? '<textarea rows="7"' . $attributes . '>' . $e($values[$field] ?? '') . '</textarea>'
|
|
: '<input type="' . $type . '"' . $attributes . ' value="' . $e($values[$field] ?? '') . '">';
|
|
if (isset($errors[$field])) {
|
|
$html .= '<p id="error-' . $field . '">' . $e($errors[$field]) . '</p>';
|
|
}
|
|
$html .= '</div>';
|
|
}
|
|
$html .= '<div><label><input type="checkbox" name="consent" value="1" required'
|
|
. (($values['consent'] ?? '') === '1' ? ' checked' : '')
|
|
. (isset($errors['consent']) ? ' aria-invalid="true" aria-describedby="error-consent"' : '')
|
|
. '> Zgadzam się na kontakt i przetwarzanie podanych danych w celu obsługi tego zapytania.</label>';
|
|
if (isset($errors['consent'])) {
|
|
$html .= '<p id="error-consent">' . $e($errors['consent']) . '</p>';
|
|
}
|
|
return $html . '</div><p>Zapisujemy Twoje zgłoszenie i oddzwaniamy. Potwierdzenia e-mail nie wysyłamy.</p>'
|
|
. '<button type="submit"' . ($data['isDemo'] ? ' disabled' : '') . '>Wyślij zapytanie</button></form></section>';
|
|
}
|
|
}
|