Add configurable legal pages and admin settings

This commit is contained in:
2026-03-23 16:26:45 +01:00
parent eb3723c4a7
commit 1478aa932b
6 changed files with 645 additions and 120 deletions
+204 -6
View File
@@ -2,7 +2,7 @@
session_start(); session_start();
require __DIR__ . '/helpers.php'; require __DIR__ . '/helpers.php';
$ADMIN_KEY = getenv('FLIXCOOKS_ADMIN_KEY') ?: 'REWnqLYwQtZZDgXcNxnt'; $ADMIN_KEY = getenv('FLIXCOOKS_ADMIN_KEY') ?: 'vFDH.N_tVLEKNdR3fhLs';
$authed = isset($_SESSION['fc_admin']) && $_SESSION['fc_admin'] === true; $authed = isset($_SESSION['fc_admin']) && $_SESSION['fc_admin'] === true;
if (!$authed && isset($_POST['password'])) { if (!$authed && isset($_POST['password'])) {
@@ -50,11 +50,18 @@ if (!$authed) {
} }
$allRecipes = load_recipes(); $allRecipes = load_recipes();
$siteSettings = load_site_settings();
$message = null; $message = null;
$errors = [];
$editing = null; $editing = null;
$editIndex = null; $editIndex = null;
$editingEn = []; $editingEn = [];
$editingDe = []; $editingDe = [];
$view = (isset($_GET['view']) && $_GET['view'] === 'settings') ? 'settings' : 'recipes';
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
}
if (isset($_GET['edit'])) { if (isset($_GET['edit'])) {
$slugEdit = trim($_GET['edit']); $slugEdit = trim($_GET['edit']);
@@ -69,7 +76,94 @@ if (isset($_GET['edit'])) {
} }
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_settings') {
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['token'] ?? '')) {
$errors[] = 'Invalid form token, please retry.';
} else {
$imprint = $siteSettings['imprint'];
$privacy = $siteSettings['privacy'];
$imprint['de']['owner_name'] = trim($_POST['owner_name_de'] ?? $imprint['de']['owner_name']);
$imprint['en']['owner_name'] = trim($_POST['owner_name_en'] ?? $imprint['en']['owner_name']);
$imprint['de']['address'] = trim($_POST['address_de'] ?? $imprint['de']['address']);
$imprint['en']['address'] = trim($_POST['address_en'] ?? $imprint['en']['address']);
$imprint['de']['email'] = trim($_POST['email_de'] ?? $imprint['de']['email']);
$imprint['en']['email'] = trim($_POST['email_en'] ?? $imprint['en']['email']);
$imprint['de']['phone'] = trim($_POST['phone_de'] ?? $imprint['de']['phone']);
$imprint['en']['phone'] = trim($_POST['phone_en'] ?? $imprint['en']['phone']);
$imprint['de']['legal_form'] = trim($_POST['legal_form_de'] ?? $imprint['de']['legal_form']);
$imprint['en']['legal_form'] = trim($_POST['legal_form_en'] ?? $imprint['en']['legal_form']);
$imprint['de']['business_purpose'] = trim($_POST['business_purpose_de'] ?? $imprint['de']['business_purpose']);
$imprint['en']['business_purpose'] = trim($_POST['business_purpose_en'] ?? $imprint['en']['business_purpose']);
$imprint['de']['wko_membership'] = trim($_POST['wko_de'] ?? $imprint['de']['wko_membership']);
$imprint['en']['wko_membership'] = trim($_POST['wko_en'] ?? $imprint['en']['wko_membership']);
$imprint['de']['authority'] = trim($_POST['authority_de'] ?? $imprint['de']['authority']);
$imprint['en']['authority'] = trim($_POST['authority_en'] ?? $imprint['en']['authority']);
$imprint['de']['uid'] = trim($_POST['uid_de'] ?? $imprint['de']['uid']);
$imprint['en']['uid'] = trim($_POST['uid_en'] ?? $imprint['en']['uid']);
$imprint['de']['odr'] = trim($_POST['odr_link'] ?? $imprint['de']['odr']);
$imprint['en']['odr'] = $imprint['de']['odr'];
$imprint['de']['last_updated'] = date('Y-m-d');
$imprint['en']['last_updated'] = date('Y-m-d');
$privacy['de']['controller'] = trim($_POST['controller_de'] ?? $privacy['de']['controller']);
$privacy['en']['controller'] = trim($_POST['controller_en'] ?? $privacy['en']['controller']);
$privacy['de']['address'] = trim($_POST['privacy_address_de'] ?? $privacy['de']['address']);
$privacy['en']['address'] = trim($_POST['privacy_address_en'] ?? $privacy['en']['address']);
$privacy['de']['contact_email'] = trim($_POST['privacy_email_de'] ?? $privacy['de']['contact_email']);
$privacy['en']['contact_email'] = trim($_POST['privacy_email_en'] ?? $privacy['en']['contact_email']);
$privacy['de']['contact_phone'] = trim($_POST['privacy_phone_de'] ?? $privacy['de']['contact_phone']);
$privacy['en']['contact_phone'] = trim($_POST['privacy_phone_en'] ?? $privacy['en']['contact_phone']);
$privacy['de']['hosting_provider'] = trim($_POST['hosting_de'] ?? $privacy['de']['hosting_provider']);
$privacy['en']['hosting_provider'] = trim($_POST['hosting_en'] ?? $privacy['en']['hosting_provider']);
$privacy['de']['cookie_statement'] = trim($_POST['cookie_de'] ?? $privacy['de']['cookie_statement']);
$privacy['en']['cookie_statement'] = trim($_POST['cookie_en'] ?? $privacy['en']['cookie_statement']);
$privacy['de']['purposes'] = trim($_POST['purposes_de'] ?? $privacy['de']['purposes']);
$privacy['en']['purposes'] = trim($_POST['purposes_en'] ?? $privacy['en']['purposes']);
$privacy['de']['log_retention_days'] = (int) ($_POST['log_retention'] ?? $privacy['de']['log_retention_days']);
$privacy['en']['log_retention_days'] = $privacy['de']['log_retention_days'];
$privacy['de']['last_updated'] = date('Y-m-d');
$privacy['en']['last_updated'] = date('Y-m-d');
// Require core fields
$requiredPairs = [
['imprint', 'owner_name', 'Owner name'],
['imprint', 'address', 'Address'],
['imprint', 'email', 'Email'],
['imprint', 'phone', 'Phone'],
['imprint', 'business_purpose', 'Business purpose'],
['privacy', 'controller', 'Controller'],
['privacy', 'contact_email', 'Privacy email'],
['privacy', 'contact_phone', 'Privacy phone'],
['privacy', 'hosting_provider', 'Hosting provider'],
['privacy', 'cookie_statement', 'Cookie statement'],
];
foreach ($requiredPairs as [$section, $key, $label]) {
$deVal = ${$section}['de'][$key] ?? '';
$enVal = ${$section}['en'][$key] ?? '';
if ($deVal === '' && $enVal !== '') {
${$section}['de'][$key] = $enVal;
} elseif ($enVal === '' && $deVal !== '') {
${$section}['en'][$key] = $deVal;
} elseif ($deVal === '' && $enVal === '') {
$errors[] = $label . ' is required (DE or EN).';
}
}
if (empty($errors)) {
$siteSettings['imprint'] = $imprint;
$siteSettings['privacy'] = $privacy;
if (save_site_settings($siteSettings)) {
$message = 'Settings saved.';
} else {
$errors[] = 'Could not save settings. Check permissions on data/site.json.';
}
}
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') {
$slugDel = trim($_POST['slug'] ?? ''); $slugDel = trim($_POST['slug'] ?? '');
$before = count($allRecipes); $before = count($allRecipes);
$allRecipes = array_values(array_filter($allRecipes, fn($r) => ($r['slug'] ?? '') !== $slugDel)); $allRecipes = array_values(array_filter($allRecipes, fn($r) => ($r['slug'] ?? '') !== $slugDel));
@@ -80,9 +174,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
} }
$editing = null; $editing = null;
$editIndex = null; $editIndex = null;
} } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save') {
// helpers // helpers
$parse_csv = fn($text) => array_values(array_filter(array_map('trim', explode(',', $text ?? '')), 'strlen')); $parse_csv = fn($text) => array_values(array_filter(array_map('trim', explode(',', $text ?? '')), 'strlen'));
$parse_lines = fn($text) => array_values(array_filter(array_map('trim', preg_split('/\\r?\\n/', $text ?? '')), 'strlen')); $parse_lines = fn($text) => array_values(array_filter(array_map('trim', preg_split('/\\r?\\n/', $text ?? '')), 'strlen'));
@@ -217,6 +309,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
<style> <style>
body { max-width: 1100px; margin: 24px auto 64px; padding: 0 16px; color: var(--ink); } body { max-width: 1100px; margin: 24px auto 64px; padding: 0 16px; color: var(--ink); }
.admin-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; } .admin-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; }
.admin-tabs { display: flex; gap: 8px; }
.admin-tabs a { padding: 8px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.15); text-decoration: none; color: var(--ink); }
.admin-tabs a.active { background: var(--accent-soft); border-color: var(--accent); }
form.admin-form { form.admin-form {
display: grid; display: grid;
gap: 12px; gap: 12px;
@@ -252,7 +347,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
<body> <body>
<div class="admin-header"> <div class="admin-header">
<h1>FlixCooks Admin</h1> <h1>FlixCooks Admin</h1>
<div> <div style="display:flex; gap:8px; align-items:center;">
<div class="admin-tabs">
<a href="/admin.php" class="<?php echo $view === 'recipes' ? 'active' : ''; ?>">Recipes</a>
<a href="/admin.php?view=settings" class="<?php echo $view === 'settings' ? 'active' : ''; ?>">Site settings</a>
</div>
<a class="button" href="/index.php" style="padding:10px 14px;">View site</a> <a class="button" href="/index.php" style="padding:10px 14px;">View site</a>
<?php if ($editing): ?> <?php if ($editing): ?>
<a class="button" href="/admin.php" style="padding:10px 14px;">Back</a> <a class="button" href="/admin.php" style="padding:10px 14px;">Back</a>
@@ -263,7 +362,105 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
<?php if ($message): ?> <?php if ($message): ?>
<div class="message"><?php echo e($message); ?></div> <div class="message"><?php echo e($message); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($errors)): ?>
<div class="message" style="background:#b00020; color:#fff;">
<?php echo e(implode(' ', $errors)); ?>
</div>
<?php endif; ?>
<?php if ($view === 'settings'): ?>
<?php $im = $siteSettings['imprint']; $pv = $siteSettings['privacy']; ?>
<form class="admin-form" method="post">
<input type="hidden" name="action" value="save_settings">
<input type="hidden" name="token" value="<?php echo e($_SESSION['csrf_token']); ?>">
<h2>Imprint (DE)</h2>
<div class="row">
<div><label>Name *</label><input type="text" name="owner_name_de" value="<?php echo e($im['de']['owner_name']); ?>" required></div>
<div><label>Adresse *</label><input type="text" name="address_de" value="<?php echo e($im['de']['address']); ?>" required></div>
</div>
<div class="row">
<div><label>E-Mail *</label><input type="email" name="email_de" value="<?php echo e($im['de']['email']); ?>" required></div>
<div><label>Telefon *</label><input type="text" name="phone_de" value="<?php echo e($im['de']['phone']); ?>" required></div>
</div>
<label>Rechtsform / Hinweis</label>
<input type="text" name="legal_form_de" value="<?php echo e($im['de']['legal_form']); ?>">
<label>Unternehmensgegenstand / Blattlinie *</label>
<input type="text" name="business_purpose_de" value="<?php echo e($im['de']['business_purpose']); ?>" required>
<div class="row">
<div><label>WKO-Mitgliedschaft</label><input type="text" name="wko_de" value="<?php echo e($im['de']['wko_membership']); ?>"></div>
<div><label>Aufsichts-/Gewerbebehörde</label><input type="text" name="authority_de" value="<?php echo e($im['de']['authority']); ?>"></div>
</div>
<div class="row">
<div><label>UID</label><input type="text" name="uid_de" value="<?php echo e($im['de']['uid']); ?>"></div>
<div><label>ODR-Link</label><input type="url" name="odr_link" value="<?php echo e($im['de']['odr']); ?>"></div>
</div>
<hr>
<h2>Imprint (EN)</h2>
<div class="row">
<div><label>Name *</label><input type="text" name="owner_name_en" value="<?php echo e($im['en']['owner_name']); ?>" required></div>
<div><label>Address *</label><input type="text" name="address_en" value="<?php echo e($im['en']['address']); ?>" required></div>
</div>
<div class="row">
<div><label>Email *</label><input type="email" name="email_en" value="<?php echo e($im['en']['email']); ?>" required></div>
<div><label>Phone *</label><input type="text" name="phone_en" value="<?php echo e($im['en']['phone']); ?>" required></div>
</div>
<label>Legal form / note</label>
<input type="text" name="legal_form_en" value="<?php echo e($im['en']['legal_form']); ?>">
<label>Business purpose / editorial line *</label>
<input type="text" name="business_purpose_en" value="<?php echo e($im['en']['business_purpose']); ?>" required>
<div class="row">
<div><label>Chamber membership</label><input type="text" name="wko_en" value="<?php echo e($im['en']['wko_membership']); ?>"></div>
<div><label>Supervisory authority</label><input type="text" name="authority_en" value="<?php echo e($im['en']['authority']); ?>"></div>
</div>
<div class="row">
<div><label>VAT ID</label><input type="text" name="uid_en" value="<?php echo e($im['en']['uid']); ?>"></div>
</div>
<hr>
<h2>Privacy (DE)</h2>
<div class="row">
<div><label>Verantwortlicher *</label><input type="text" name="controller_de" value="<?php echo e($pv['de']['controller']); ?>" required></div>
<div><label>Adresse *</label><input type="text" name="privacy_address_de" value="<?php echo e($pv['de']['address']); ?>" required></div>
</div>
<div class="row">
<div><label>E-Mail *</label><input type="email" name="privacy_email_de" value="<?php echo e($pv['de']['contact_email']); ?>" required></div>
<div><label>Telefon *</label><input type="text" name="privacy_phone_de" value="<?php echo e($pv['de']['contact_phone']); ?>" required></div>
</div>
<label>Hosting *</label>
<input type="text" name="hosting_de" value="<?php echo e($pv['de']['hosting_provider']); ?>" required>
<label>Zwecke *</label>
<textarea name="purposes_de" required><?php echo e($pv['de']['purposes']); ?></textarea>
<label>Cookies *</label>
<textarea name="cookie_de" required><?php echo e($pv['de']['cookie_statement']); ?></textarea>
<hr>
<h2>Privacy (EN)</h2>
<div class="row">
<div><label>Controller *</label><input type="text" name="controller_en" value="<?php echo e($pv['en']['controller']); ?>" required></div>
<div><label>Address *</label><input type="text" name="privacy_address_en" value="<?php echo e($pv['en']['address']); ?>" required></div>
</div>
<div class="row">
<div><label>Email *</label><input type="email" name="privacy_email_en" value="<?php echo e($pv['en']['contact_email']); ?>" required></div>
<div><label>Phone *</label><input type="text" name="privacy_phone_en" value="<?php echo e($pv['en']['contact_phone']); ?>" required></div>
</div>
<label>Hosting *</label>
<input type="text" name="hosting_en" value="<?php echo e($pv['en']['hosting_provider']); ?>" required>
<label>Purposes *</label>
<textarea name="purposes_en" required><?php echo e($pv['en']['purposes']); ?></textarea>
<label>Cookies *</label>
<textarea name="cookie_en" required><?php echo e($pv['en']['cookie_statement']); ?></textarea>
<div class="row">
<div>
<label>Log retention (days)</label>
<input type="number" name="log_retention" min="0" value="<?php echo e((int) $pv['de']['log_retention_days']); ?>">
</div>
</div>
<button type="submit" class="button" style="width: fit-content;">Save settings</button>
</form>
<?php else: ?>
<form class="admin-form" method="post"> <form class="admin-form" method="post">
<input type="hidden" name="action" value="save"> <input type="hidden" name="action" value="save">
<?php if ($editing): ?> <?php if ($editing): ?>
@@ -389,5 +586,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endif; ?>
</body> </body>
</html> </html>
+54
View File
@@ -0,0 +1,54 @@
{
"imprint": {
"de": {
"owner_name": "FlixCooks (bitte anpassen)",
"address": "Stra\u00dfe Hausnummer, PLZ Ort, \u00d6sterreich",
"email": "contact@flixcooks.at",
"phone": "+43 660 0000000",
"legal_form": "Kleines Impressum; kein Firmenbucheintrag/UID hinterlegt.",
"business_purpose": "Foodblog & Rezeptmarketing.",
"wko_membership": "",
"authority": "",
"uid": "",
"odr": "https://ec.europa.eu/consumers/odr",
"last_updated": "2026-03-23"
},
"en": {
"owner_name": "FlixCooks (please update)",
"address": "Street number, ZIP City, Austria",
"email": "contact@flixcooks.at",
"phone": "+43 660 0000000",
"legal_form": "Small website notice; no commercial register/UID provided.",
"business_purpose": "Food blog & recipe marketing.",
"wko_membership": "",
"authority": "",
"uid": "",
"odr": "https://ec.europa.eu/consumers/odr",
"last_updated": "2026-03-23"
}
},
"privacy": {
"de": {
"controller": "FlixCooks (bitte anpassen)",
"contact_email": "contact@flixcooks.at",
"contact_phone": "+43 660 0000000",
"address": "Stra\u00dfe Hausnummer, PLZ Ort, \u00d6sterreich",
"hosting_provider": "Hetzner Online GmbH, Industriestr. 25, 91710 Gunzenhausen, Deutschland",
"log_retention_days": 30,
"cookie_statement": "Keine Tracking- oder Marketing-Cookies; nur technisch notwendige.",
"purposes": "Betrieb und Bereitstellung der Website, Beantwortung von Kontaktanfragen.",
"last_updated": "2026-03-23"
},
"en": {
"controller": "FlixCooks (please update)",
"contact_email": "contact@flixcooks.at",
"contact_phone": "+43 660 0000000",
"address": "Street number, ZIP City, Austria",
"hosting_provider": "Hetzner Online GmbH, Industriestr. 25, 91710 Gunzenhausen, Germany",
"log_retention_days": 30,
"cookie_statement": "No tracking or marketing cookies; only technically necessary cookies.",
"purposes": "Operating and providing the website, responding to contact requests.",
"last_updated": "2026-03-23"
}
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
require __DIR__ . '/helpers.php';
$lang = strtolower((string) (filter_input(INPUT_GET, 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?: 'en')) === 'de' ? 'de' : 'en';
$settings = load_site_settings();
$privacy = $settings['privacy'][$lang] ?? ($settings['privacy']['en'] ?? []);
$langSwitchLabel = $lang === 'de' ? 'DE' : 'EN';
$langSwitchHref = lang_url($lang === 'de' ? 'en' : 'de');
$navHome = $lang === 'de' ? 'Start' : 'Home';
$navLatest = $lang === 'de' ? 'Neueste' : 'Latest';
$navBasics = $lang === 'de' ? 'Basics' : 'Basics';
$t = [
'title' => $lang === 'de' ? 'Datenschutzerklärung' : 'Privacy Policy',
'controller' => $lang === 'de' ? 'Verantwortlicher' : 'Controller',
'address' => $lang === 'de' ? 'Adresse' : 'Address',
'contact' => $lang === 'de' ? 'Kontakt für Datenschutz' : 'Privacy contact',
'hosting' => $lang === 'de' ? 'Hosting' : 'Hosting',
'logs' => $lang === 'de' ? 'Server-Logs' : 'Server logs',
'cookies' => $lang === 'de' ? 'Cookies' : 'Cookies',
'purposes' => $lang === 'de' ? 'Zwecke der Datenverarbeitung' : 'Purposes of processing',
'rights' => $lang === 'de' ? 'Ihre Rechte' : 'Your rights',
'last_updated' => $lang === 'de' ? 'Stand' : 'Last updated',
];
function privacy_item(string $label, ?string $value): void {
$value = trim((string) $value);
if ($value === '') {
return;
}
echo '<div class="legal-item"><span class="legal-label">' . e($label) . '</span><span>' . nl2br(e($value)) . '</span></div>';
}
$rights = $lang === 'de'
? 'Auskunft, Berichtigung, Löschung, Einschränkung, Datenübertragbarkeit, Widerspruch, Beschwerde bei der Datenschutzbehörde.'
: 'Access, rectification, erasure, restriction, data portability, objection, complaint to the data protection authority.';
$logInfo = '';
if (!empty($privacy['log_retention_days'])) {
$logInfo = ($lang === 'de'
? 'Server-Logs (IP-Adresse, Zeitpunkt, URL, User-Agent) werden zur Betriebssicherheit gespeichert und nach '
: 'Server logs (IP address, timestamp, URL, user agent) are kept for security and deleted after ')
. intval($privacy['log_retention_days']) . ($lang === 'de' ? ' Tagen gelöscht.' : ' days.');
}
?>
<!doctype html>
<html lang="<?php echo e($lang); ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo e($t['title']); ?> | FlixCooks</title>
<link rel="stylesheet" href="/assets/style.css">
<style>
.legal-page { max-width: 900px; margin: 40px auto 80px; padding: 0 16px; }
.legal-card { background: var(--surface-2); border-radius: 16px; padding: 24px; box-shadow: var(--shadow); border: 1px solid rgba(255,255,255,0.08); }
.legal-item { display: grid; grid-template-columns: 220px 1fr; gap: 10px; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
.legal-item:last-child { border-bottom: none; }
.legal-label { font-weight: 700; color: var(--ink); }
.legal-section { margin-top: 22px; }
.legal-section h2 { margin: 0 0 8px; }
@media (max-width: 600px) {
.legal-item { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<?php include __DIR__ . '/partials/header.php'; ?>
<main class="legal-page">
<h1><?php echo e($t['title']); ?></h1>
<p><?php echo $lang === 'de'
? 'Informationen gemäß DSGVO und TKG über die Verarbeitung personenbezogener Daten.'
: 'Information per GDPR and TKG on how personal data is processed.'; ?></p>
<div class="legal-card legal-section">
<h2><?php echo e($t['controller']); ?></h2>
<?php
privacy_item($t['controller'], $privacy['controller'] ?? '');
privacy_item($t['address'], $privacy['address'] ?? '');
privacy_item($t['contact'], ($privacy['contact_email'] ?? '') . ($privacy['contact_phone'] ? ' | ' . $privacy['contact_phone'] : ''));
privacy_item($t['hosting'], $privacy['hosting_provider'] ?? '');
privacy_item($t['purposes'], $privacy['purposes'] ?? '');
if ($logInfo) { privacy_item($t['logs'], $logInfo); }
privacy_item($t['cookies'], $privacy['cookie_statement'] ?? '');
privacy_item($t['rights'], $rights);
privacy_item($t['last_updated'], $privacy['last_updated'] ?? '');
?>
</div>
</main>
<?php include __DIR__ . '/partials/footer.php'; ?>
</body>
</html>
+87
View File
@@ -28,6 +28,93 @@ function save_recipes(array $recipes): bool {
return (bool) file_put_contents($path, $json); return (bool) file_put_contents($path, $json);
} }
/**
* Default settings for imprint and privacy pages.
*/
function default_site_settings(): array {
$today = date('Y-m-d');
return [
'imprint' => [
'de' => [
'owner_name' => 'FlixCooks (bitte anpassen)',
'address' => 'Straße Hausnummer, PLZ Ort, Österreich',
'email' => 'contact@flixcooks.at',
'phone' => '+43 660 0000000',
'legal_form' => 'Kleines Impressum; kein Firmenbucheintrag/UID hinterlegt.',
'business_purpose' => 'Foodblog & Rezeptmarketing.',
'wko_membership' => '',
'authority' => '',
'uid' => '',
'odr' => 'https://ec.europa.eu/consumers/odr',
'last_updated' => $today,
],
'en' => [
'owner_name' => 'FlixCooks (please update)',
'address' => 'Street number, ZIP City, Austria',
'email' => 'contact@flixcooks.at',
'phone' => '+43 660 0000000',
'legal_form' => 'Small website notice; no commercial register/UID provided.',
'business_purpose' => 'Food blog & recipe marketing.',
'wko_membership' => '',
'authority' => '',
'uid' => '',
'odr' => 'https://ec.europa.eu/consumers/odr',
'last_updated' => $today,
],
],
'privacy' => [
'de' => [
'controller' => 'FlixCooks (bitte anpassen)',
'contact_email' => 'contact@flixcooks.at',
'contact_phone' => '+43 660 0000000',
'address' => 'Straße Hausnummer, PLZ Ort, Österreich',
'hosting_provider' => 'Hetzner Online GmbH, Industriestr. 25, 91710 Gunzenhausen, Deutschland',
'log_retention_days' => 30,
'cookie_statement' => 'Keine Tracking- oder Marketing-Cookies; nur technisch notwendige.',
'purposes' => 'Betrieb und Bereitstellung der Website, Beantwortung von Kontaktanfragen.',
'last_updated' => $today,
],
'en' => [
'controller' => 'FlixCooks (please update)',
'contact_email' => 'contact@flixcooks.at',
'contact_phone' => '+43 660 0000000',
'address' => 'Street number, ZIP City, Austria',
'hosting_provider' => 'Hetzner Online GmbH, Industriestr. 25, 91710 Gunzenhausen, Germany',
'log_retention_days' => 30,
'cookie_statement' => 'No tracking or marketing cookies; only technically necessary cookies.',
'purposes' => 'Operating and providing the website, responding to contact requests.',
'last_updated' => $today,
],
],
];
}
/**
* Load site-wide settings for legal pages, merging with defaults.
*/
function load_site_settings(): array {
$defaults = default_site_settings();
$path = __DIR__ . '/data/site.json';
if (!file_exists($path)) {
return $defaults;
}
$raw = file_get_contents($path);
$data = json_decode($raw, true);
if (!is_array($data)) {
return $defaults;
}
return array_replace_recursive($defaults, $data);
}
/**
* Persist site-wide settings for legal pages.
*/
function save_site_settings(array $settings): bool {
$path = __DIR__ . '/data/site.json';
$json = json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
return (bool) file_put_contents($path, $json);
}
function slugify(string $text): string { function slugify(string $text): string {
$text = strtolower($text); $text = strtolower($text);
$text = preg_replace('~[^a-z0-9]+~', '-', $text); $text = preg_replace('~[^a-z0-9]+~', '-', $text);
+84
View File
@@ -0,0 +1,84 @@
<?php
require __DIR__ . '/helpers.php';
$lang = strtolower((string) (filter_input(INPUT_GET, 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?: 'en')) === 'de' ? 'de' : 'en';
$settings = load_site_settings();
$imprint = $settings['imprint'][$lang] ?? ($settings['imprint']['en'] ?? []);
$langSwitchLabel = $lang === 'de' ? 'DE' : 'EN';
$langSwitchHref = lang_url($lang === 'de' ? 'en' : 'de');
$navHome = $lang === 'de' ? 'Start' : 'Home';
$navLatest = $lang === 'de' ? 'Neueste' : 'Latest';
$navBasics = $lang === 'de' ? 'Basics' : 'Basics';
$t = [
'title' => $lang === 'de' ? 'Impressum' : 'Imprint',
'owner' => $lang === 'de' ? 'Medieninhaber' : 'Media owner',
'address' => $lang === 'de' ? 'Adresse' : 'Address',
'contact' => $lang === 'de' ? 'Kontakt' : 'Contact',
'legal' => $lang === 'de' ? 'Rechtliches' : 'Legal',
'purpose' => $lang === 'de' ? 'Unternehmensgegenstand / Blattlinie' : 'Business purpose / editorial line',
'authority' => $lang === 'de' ? 'Aufsichts-/Gewerbebehörde' : 'Supervisory authority',
'membership' => $lang === 'de' ? 'WKO-Mitgliedschaft' : 'Chamber membership',
'uid' => $lang === 'de' ? 'UID' : 'VAT ID',
'odr' => $lang === 'de' ? 'Online-Streitbeilegung (ODR)' : 'Online dispute resolution (ODR)',
'last_updated' => $lang === 'de' ? 'Stand' : 'Last updated',
];
function legal_item(string $label, ?string $value): void {
$value = trim((string) $value);
if ($value === '') {
return;
}
echo '<div class="legal-item"><span class="legal-label">' . e($label) . '</span><span>' . nl2br(e($value)) . '</span></div>';
}
?>
<!doctype html>
<html lang="<?php echo e($lang); ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo e($t['title']); ?> | FlixCooks</title>
<link rel="stylesheet" href="/assets/style.css">
<style>
.legal-page { max-width: 900px; margin: 40px auto 80px; padding: 0 16px; }
.legal-card { background: var(--surface-2); border-radius: 16px; padding: 24px; box-shadow: var(--shadow); border: 1px solid rgba(255,255,255,0.08); }
.legal-item { display: grid; grid-template-columns: 220px 1fr; gap: 10px; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
.legal-item:last-child { border-bottom: none; }
.legal-label { font-weight: 700; color: var(--ink); }
.legal-section { margin-top: 22px; }
.legal-section h2 { margin: 0 0 8px; }
@media (max-width: 600px) {
.legal-item { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<?php include __DIR__ . '/partials/header.php'; ?>
<main class="legal-page">
<h1><?php echo e($t['title']); ?></h1>
<p><?php echo $lang === 'de'
? 'Pflichtangaben für diese Website gemäß ECG, MedienG und GewO.'
: 'Mandatory website information in line with Austrian ECG, Media Act and Trade Regulation.'; ?></p>
<div class="legal-card legal-section">
<h2><?php echo e($t['owner']); ?></h2>
<?php
legal_item($t['owner'], $imprint['owner_name'] ?? '');
legal_item($t['address'], $imprint['address'] ?? '');
legal_item($t['contact'], ($imprint['email'] ?? '') . ($imprint['phone'] ? ' | ' . $imprint['phone'] : ''));
legal_item($t['legal'], $imprint['legal_form'] ?? '');
legal_item($t['purpose'], $imprint['business_purpose'] ?? '');
legal_item($t['membership'], $imprint['wko_membership'] ?? '');
legal_item($t['authority'], $imprint['authority'] ?? '');
legal_item($t['uid'], $imprint['uid'] ?? '');
if (!empty($imprint['odr'])) {
echo '<div class="legal-item"><span class="legal-label">' . e($t['odr']) . '</span><span><a href="' . e($imprint['odr']) . '" target="_blank" rel="noopener">ODR-Link</a></span></div>';
}
legal_item($t['last_updated'], $imprint['last_updated'] ?? '');
?>
</div>
</main>
<?php include __DIR__ . '/partials/footer.php'; ?>
</body>
</html>
+11 -1
View File
@@ -1,9 +1,19 @@
<?php
$langCode = (isset($lang) && $lang === 'de') ? 'de' : 'en';
$langSuffix = $langCode === 'de' ? '?lang=de' : '';
$settings = function_exists('load_site_settings') ? load_site_settings() : null;
$contactEmail = $settings['imprint'][$langCode]['email'] ?? 'contact@flixcooks.at';
?>
<footer class="site-footer"> <footer class="site-footer">
<div class="footer-meta"> <div class="footer-meta">
<p>Cook or get cooked.</p> <p>Cook or get cooked.</p>
<a class="contact-link" href="mailto:contact@flixcooks.at">Contact me</a> <a class="contact-link" href="mailto:<?php echo e($contactEmail); ?>">Contact me</a>
<small>© <?php echo date('Y'); ?> FlixCooks</small> <small>© <?php echo date('Y'); ?> FlixCooks</small>
</div> </div>
<div class="footer-links">
<a href="/impressum.php<?php echo $langSuffix; ?>"><?php echo $langCode === 'de' ? 'Impressum' : 'Imprint'; ?></a>
<a href="/datenschutz.php<?php echo $langSuffix; ?>"><?php echo $langCode === 'de' ? 'Datenschutz' : 'Privacy'; ?></a>
</div>
</footer> </footer>
<button id="backToTop" aria-label="Back to top">↑ Back to top</button> <button id="backToTop" aria-label="Back to top">↑ Back to top</button>