Add configurable legal pages and admin settings
This commit is contained in:
@@ -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,131 +362,230 @@ 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)): ?>
|
||||||
<form class="admin-form" method="post">
|
<div class="message" style="background:#b00020; color:#fff;">
|
||||||
<input type="hidden" name="action" value="save">
|
<?php echo e(implode(' ', $errors)); ?>
|
||||||
<?php if ($editing): ?>
|
|
||||||
<input type="hidden" name="slug_original" value="<?php echo e($editing['slug']); ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<label>Hero Image URL</label>
|
|
||||||
<input type="text" name="hero" placeholder="https://... or /assets/images/hero.jpg" value="<?php echo e($editing['hero'] ?? ''); ?>">
|
|
||||||
<p style="color: var(--muted); margin: 4px 0 8px; font-size: 13px;">Accepts full URLs or relative paths (e.g. /assets/images/hero.jpg).</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div>
|
|
||||||
<label>Prep time (minutes)</label>
|
|
||||||
<input type="number" name="prep_time" min="0" value="<?php echo e($editing['prep_time'] ?? 0); ?>">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Cook time (minutes)</label>
|
|
||||||
<input type="number" name="cook_time" min="0" value="<?php echo e($editing['cook_time'] ?? 0); ?>">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Total time (minutes)</label>
|
|
||||||
<input type="number" name="total_time" min="0" value="<?php echo e($editing['total_time'] ?? 0); ?>" placeholder="0 to auto-sum">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Servings</label>
|
|
||||||
<input type="number" name="servings" min="1" value="<?php echo e($editing['servings'] ?? 2); ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<hr>
|
<?php if ($view === 'settings'): ?>
|
||||||
<h3>English</h3>
|
<?php $im = $siteSettings['imprint']; $pv = $siteSettings['privacy']; ?>
|
||||||
<div class="row">
|
<form class="admin-form" method="post">
|
||||||
<div>
|
<input type="hidden" name="action" value="save_settings">
|
||||||
<label>Title (EN) *</label>
|
<input type="hidden" name="token" value="<?php echo e($_SESSION['csrf_token']); ?>">
|
||||||
<input type="text" name="title_en" required value="<?php echo e($editingEn['title'] ?? ''); ?>">
|
<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>
|
||||||
<div>
|
<div class="row">
|
||||||
<label>Category (EN)</label>
|
<div><label>E-Mail *</label><input type="email" name="email_de" value="<?php echo e($im['de']['email']); ?>" required></div>
|
||||||
<input type="text" name="category_en" placeholder="Dinner, Dessert..." value="<?php echo e($editingEn['category'] ?? ''); ?>">
|
<div><label>Telefon *</label><input type="text" name="phone_de" value="<?php echo e($im['de']['phone']); ?>" required></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<label>Rechtsform / Hinweis</label>
|
||||||
<label>Difficulty (EN)</label>
|
<input type="text" name="legal_form_de" value="<?php echo e($im['de']['legal_form']); ?>">
|
||||||
<?php $diffEn = $editingEn['difficulty'] ?? 'Easy'; ?>
|
<label>Unternehmensgegenstand / Blattlinie *</label>
|
||||||
<select name="difficulty_en">
|
<input type="text" name="business_purpose_de" value="<?php echo e($im['de']['business_purpose']); ?>" required>
|
||||||
<option <?php echo $diffEn==='Easy'?'selected':''; ?>>Easy</option>
|
<div class="row">
|
||||||
<option <?php echo $diffEn==='Medium'?'selected':''; ?>>Medium</option>
|
<div><label>WKO-Mitgliedschaft</label><input type="text" name="wko_de" value="<?php echo e($im['de']['wko_membership']); ?>"></div>
|
||||||
<option <?php echo $diffEn==='Hard'?'selected':''; ?>>Hard</option>
|
<div><label>Aufsichts-/Gewerbebehörde</label><input type="text" name="authority_de" value="<?php echo e($im['de']['authority']); ?>"></div>
|
||||||
</select>
|
</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>
|
</div>
|
||||||
</div>
|
|
||||||
<label>Description (EN)</label>
|
|
||||||
<textarea name="description_en" placeholder="Short teaser"><?php echo e($editingEn['description'] ?? ''); ?></textarea>
|
|
||||||
<label>Tags (EN, comma separated)</label>
|
|
||||||
<input type="text" name="tags_en" placeholder="gluten-free, 30-minutes" value="<?php echo e(isset($editingEn['tags']) ? implode(', ', $editingEn['tags']) : ''); ?>">
|
|
||||||
<label>Ingredients (EN, one per line) *</label>
|
|
||||||
<textarea name="ingredients_en" placeholder="1 cup flour 2 eggs"><?php echo isset($editingEn['ingredients']) ? e(implode("\n", $editingEn['ingredients'])) : ''; ?></textarea>
|
|
||||||
<label>Utensils (EN, one per line)</label>
|
|
||||||
<textarea name="utensils_en" placeholder="12" skillet Sheet pan Rubber spatula"><?php echo isset($editingEn['utensils']) ? e(implode("\n", $editingEn['utensils'])) : ''; ?></textarea>
|
|
||||||
<label>Steps (EN, one per line) *</label>
|
|
||||||
<textarea name="steps_en" placeholder="Preheat oven... Mix dry ingredients..."><?php echo isset($editingEn['steps']) ? e(implode("\n", $editingEn['steps'])) : ''; ?></textarea>
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<h3>Deutsch</h3>
|
<h2>Imprint (EN)</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div>
|
<div><label>Name *</label><input type="text" name="owner_name_en" value="<?php echo e($im['en']['owner_name']); ?>" required></div>
|
||||||
<label>Titel (DE)</label>
|
<div><label>Address *</label><input type="text" name="address_en" value="<?php echo e($im['en']['address']); ?>" required></div>
|
||||||
<input type="text" name="title_de" value="<?php echo e($editingDe['title'] ?? ''); ?>">
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="row">
|
||||||
<label>Kategorie (DE)</label>
|
<div><label>Email *</label><input type="email" name="email_en" value="<?php echo e($im['en']['email']); ?>" required></div>
|
||||||
<input type="text" name="category_de" placeholder="Abendessen, Dessert..." value="<?php echo e($editingDe['category'] ?? ''); ?>">
|
<div><label>Phone *</label><input type="text" name="phone_en" value="<?php echo e($im['en']['phone']); ?>" required></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<label>Legal form / note</label>
|
||||||
<label>Schwierigkeitsgrad (DE)</label>
|
<input type="text" name="legal_form_en" value="<?php echo e($im['en']['legal_form']); ?>">
|
||||||
<?php $diffDe = $editingDe['difficulty'] ?? 'Einfach'; ?>
|
<label>Business purpose / editorial line *</label>
|
||||||
<select name="difficulty_de">
|
<input type="text" name="business_purpose_en" value="<?php echo e($im['en']['business_purpose']); ?>" required>
|
||||||
<option <?php echo strtolower($diffDe)==='einfach'?'selected':''; ?>>Einfach</option>
|
<div class="row">
|
||||||
<option <?php echo strtolower($diffDe)==='mittel'?'selected':''; ?>>Mittel</option>
|
<div><label>Chamber membership</label><input type="text" name="wko_en" value="<?php echo e($im['en']['wko_membership']); ?>"></div>
|
||||||
<option <?php echo strtolower($diffDe)==='schwer'?'selected':''; ?>>Schwer</option>
|
<div><label>Supervisory authority</label><input type="text" name="authority_en" value="<?php echo e($im['en']['authority']); ?>"></div>
|
||||||
</select>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div><label>VAT ID</label><input type="text" name="uid_en" value="<?php echo e($im['en']['uid']); ?>"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<label>Beschreibung (DE)</label>
|
|
||||||
<textarea name="description_de" placeholder="Kurzer Teaser"><?php echo e($editingDe['description'] ?? ''); ?></textarea>
|
|
||||||
<label>Tags (DE, komma-getrennt)</label>
|
|
||||||
<input type="text" name="tags_de" placeholder="schnell, vegetarisch" value="<?php echo e(isset($editingDe['tags']) ? implode(', ', $editingDe['tags']) : ''); ?>">
|
|
||||||
<label>Zutaten (DE, eine pro Zeile)</label>
|
|
||||||
<textarea name="ingredients_de" placeholder="300 g Mehl 2 Eier"><?php echo isset($editingDe['ingredients']) ? e(implode("\n", $editingDe['ingredients'])) : ''; ?></textarea>
|
|
||||||
<label>Werkzeug (DE, eine pro Zeile)</label>
|
|
||||||
<textarea name="utensils_de" placeholder="Pfanne Kochtopf"><?php echo isset($editingDe['utensils']) ? e(implode("\n", $editingDe['utensils'])) : ''; ?></textarea>
|
|
||||||
<label>Schritte (DE, eine pro Zeile)</label>
|
|
||||||
<textarea name="steps_de" placeholder="Backofen vorheizen... Teig kneten..."><?php echo isset($editingDe['steps']) ? e(implode("\n", $editingDe['steps'])) : ''; ?></textarea>
|
|
||||||
|
|
||||||
<label><input type="checkbox" name="featured" value="1" <?php echo !empty($editing['featured']) ? 'checked' : ''; ?>> Mark as featured</label>
|
<hr>
|
||||||
<label><input type="checkbox" name="coming_soon" value="1" <?php echo !empty($editing['coming_soon']) ? 'checked' : ''; ?>> Mark as coming soon (name & image only)</label>
|
<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>
|
||||||
|
|
||||||
<button type="submit" class="button" style="width: fit-content;"><?php echo $editing ? 'Save changes' : 'Save recipe'; ?></button>
|
<hr>
|
||||||
</form>
|
<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="list">
|
<div class="row">
|
||||||
<h2>Existing recipes (<?php echo count($allRecipes); ?>)</h2>
|
<div>
|
||||||
<?php if (empty($allRecipes)): ?>
|
<label>Log retention (days)</label>
|
||||||
<p>No recipes yet.</p>
|
<input type="number" name="log_retention" min="0" value="<?php echo e((int) $pv['de']['log_retention_days']); ?>">
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($allRecipes as $r): ?>
|
|
||||||
<?php $display = localize_recipe($r, 'en'); ?>
|
|
||||||
<div class="list-item">
|
|
||||||
<div>
|
|
||||||
<strong><?php echo e($display['title'] ?? $r['slug']); ?></strong>
|
|
||||||
<span class="pill"><?php echo e($display['category'] ?? ''); ?></span>
|
|
||||||
<?php if (!empty($r['featured'])): ?><span class="pill">Featured</span><?php endif; ?>
|
|
||||||
<?php if (!empty($r['coming_soon'])): ?><span class="pill">Coming soon</span><?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex; gap:8px; align-items:center;">
|
|
||||||
<small><?php echo e($r['slug']); ?></small>
|
|
||||||
<a class="button" href="/admin.php?edit=<?php echo urlencode($r['slug']); ?>" style="padding:8px 12px;">Edit</a>
|
|
||||||
<form method="post" style="margin:0;" onsubmit="return confirm('Delete this recipe?');">
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="slug" value="<?php echo e($r['slug']); ?>">
|
|
||||||
<button type="submit" class="button" style="padding:8px 12px;">Delete</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
</div>
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
<button type="submit" class="button" style="width: fit-content;">Save settings</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<form class="admin-form" method="post">
|
||||||
|
<input type="hidden" name="action" value="save">
|
||||||
|
<?php if ($editing): ?>
|
||||||
|
<input type="hidden" name="slug_original" value="<?php echo e($editing['slug']); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<label>Hero Image URL</label>
|
||||||
|
<input type="text" name="hero" placeholder="https://... or /assets/images/hero.jpg" value="<?php echo e($editing['hero'] ?? ''); ?>">
|
||||||
|
<p style="color: var(--muted); margin: 4px 0 8px; font-size: 13px;">Accepts full URLs or relative paths (e.g. /assets/images/hero.jpg).</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<label>Prep time (minutes)</label>
|
||||||
|
<input type="number" name="prep_time" min="0" value="<?php echo e($editing['prep_time'] ?? 0); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Cook time (minutes)</label>
|
||||||
|
<input type="number" name="cook_time" min="0" value="<?php echo e($editing['cook_time'] ?? 0); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Total time (minutes)</label>
|
||||||
|
<input type="number" name="total_time" min="0" value="<?php echo e($editing['total_time'] ?? 0); ?>" placeholder="0 to auto-sum">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Servings</label>
|
||||||
|
<input type="number" name="servings" min="1" value="<?php echo e($editing['servings'] ?? 2); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h3>English</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<label>Title (EN) *</label>
|
||||||
|
<input type="text" name="title_en" required value="<?php echo e($editingEn['title'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Category (EN)</label>
|
||||||
|
<input type="text" name="category_en" placeholder="Dinner, Dessert..." value="<?php echo e($editingEn['category'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Difficulty (EN)</label>
|
||||||
|
<?php $diffEn = $editingEn['difficulty'] ?? 'Easy'; ?>
|
||||||
|
<select name="difficulty_en">
|
||||||
|
<option <?php echo $diffEn==='Easy'?'selected':''; ?>>Easy</option>
|
||||||
|
<option <?php echo $diffEn==='Medium'?'selected':''; ?>>Medium</option>
|
||||||
|
<option <?php echo $diffEn==='Hard'?'selected':''; ?>>Hard</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label>Description (EN)</label>
|
||||||
|
<textarea name="description_en" placeholder="Short teaser"><?php echo e($editingEn['description'] ?? ''); ?></textarea>
|
||||||
|
<label>Tags (EN, comma separated)</label>
|
||||||
|
<input type="text" name="tags_en" placeholder="gluten-free, 30-minutes" value="<?php echo e(isset($editingEn['tags']) ? implode(', ', $editingEn['tags']) : ''); ?>">
|
||||||
|
<label>Ingredients (EN, one per line) *</label>
|
||||||
|
<textarea name="ingredients_en" placeholder="1 cup flour 2 eggs"><?php echo isset($editingEn['ingredients']) ? e(implode("\n", $editingEn['ingredients'])) : ''; ?></textarea>
|
||||||
|
<label>Utensils (EN, one per line)</label>
|
||||||
|
<textarea name="utensils_en" placeholder="12" skillet Sheet pan Rubber spatula"><?php echo isset($editingEn['utensils']) ? e(implode("\n", $editingEn['utensils'])) : ''; ?></textarea>
|
||||||
|
<label>Steps (EN, one per line) *</label>
|
||||||
|
<textarea name="steps_en" placeholder="Preheat oven... Mix dry ingredients..."><?php echo isset($editingEn['steps']) ? e(implode("\n", $editingEn['steps'])) : ''; ?></textarea>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h3>Deutsch</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<label>Titel (DE)</label>
|
||||||
|
<input type="text" name="title_de" value="<?php echo e($editingDe['title'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Kategorie (DE)</label>
|
||||||
|
<input type="text" name="category_de" placeholder="Abendessen, Dessert..." value="<?php echo e($editingDe['category'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Schwierigkeitsgrad (DE)</label>
|
||||||
|
<?php $diffDe = $editingDe['difficulty'] ?? 'Einfach'; ?>
|
||||||
|
<select name="difficulty_de">
|
||||||
|
<option <?php echo strtolower($diffDe)==='einfach'?'selected':''; ?>>Einfach</option>
|
||||||
|
<option <?php echo strtolower($diffDe)==='mittel'?'selected':''; ?>>Mittel</option>
|
||||||
|
<option <?php echo strtolower($diffDe)==='schwer'?'selected':''; ?>>Schwer</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label>Beschreibung (DE)</label>
|
||||||
|
<textarea name="description_de" placeholder="Kurzer Teaser"><?php echo e($editingDe['description'] ?? ''); ?></textarea>
|
||||||
|
<label>Tags (DE, komma-getrennt)</label>
|
||||||
|
<input type="text" name="tags_de" placeholder="schnell, vegetarisch" value="<?php echo e(isset($editingDe['tags']) ? implode(', ', $editingDe['tags']) : ''); ?>">
|
||||||
|
<label>Zutaten (DE, eine pro Zeile)</label>
|
||||||
|
<textarea name="ingredients_de" placeholder="300 g Mehl 2 Eier"><?php echo isset($editingDe['ingredients']) ? e(implode("\n", $editingDe['ingredients'])) : ''; ?></textarea>
|
||||||
|
<label>Werkzeug (DE, eine pro Zeile)</label>
|
||||||
|
<textarea name="utensils_de" placeholder="Pfanne Kochtopf"><?php echo isset($editingDe['utensils']) ? e(implode("\n", $editingDe['utensils'])) : ''; ?></textarea>
|
||||||
|
<label>Schritte (DE, eine pro Zeile)</label>
|
||||||
|
<textarea name="steps_de" placeholder="Backofen vorheizen... Teig kneten..."><?php echo isset($editingDe['steps']) ? e(implode("\n", $editingDe['steps'])) : ''; ?></textarea>
|
||||||
|
|
||||||
|
<label><input type="checkbox" name="featured" value="1" <?php echo !empty($editing['featured']) ? 'checked' : ''; ?>> Mark as featured</label>
|
||||||
|
<label><input type="checkbox" name="coming_soon" value="1" <?php echo !empty($editing['coming_soon']) ? 'checked' : ''; ?>> Mark as coming soon (name & image only)</label>
|
||||||
|
|
||||||
|
<button type="submit" class="button" style="width: fit-content;"><?php echo $editing ? 'Save changes' : 'Save recipe'; ?></button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="list">
|
||||||
|
<h2>Existing recipes (<?php echo count($allRecipes); ?>)</h2>
|
||||||
|
<?php if (empty($allRecipes)): ?>
|
||||||
|
<p>No recipes yet.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($allRecipes as $r): ?>
|
||||||
|
<?php $display = localize_recipe($r, 'en'); ?>
|
||||||
|
<div class="list-item">
|
||||||
|
<div>
|
||||||
|
<strong><?php echo e($display['title'] ?? $r['slug']); ?></strong>
|
||||||
|
<span class="pill"><?php echo e($display['category'] ?? ''); ?></span>
|
||||||
|
<?php if (!empty($r['featured'])): ?><span class="pill">Featured</span><?php endif; ?>
|
||||||
|
<?php if (!empty($r['coming_soon'])): ?><span class="pill">Coming soon</span><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; gap:8px; align-items:center;">
|
||||||
|
<small><?php echo e($r['slug']); ?></small>
|
||||||
|
<a class="button" href="/admin.php?edit=<?php echo urlencode($r['slug']); ?>" style="padding:8px 12px;">Edit</a>
|
||||||
|
<form method="post" style="margin:0;" onsubmit="return confirm('Delete this recipe?');">
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
<input type="hidden" name="slug" value="<?php echo e($r['slug']); ?>">
|
||||||
|
<button type="submit" class="button" style="padding:8px 12px;">Delete</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user