| Server IP : 188.114.97.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /.polana_v1801_rollback/ |
Upload File : |
<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/includes/functions.php';
require_login();
/** Diagnostic for legacy databases where menu_type exists but its ENUM omits wine. */
function polana_admin_menu_kind_allowed(string $kind): bool
{
if (!polana_schema_ready()) return false;
try {
$row = db()->query("SHOW COLUMNS FROM `menus` LIKE 'menu_type'")->fetch(PDO::FETCH_ASSOC);
if (!$row) return false;
$type = (string)($row['Type'] ?? '');
if (!preg_match('/^enum\((.*)\)$/is', $type, $m)) return true;
preg_match_all("/'([^']+)'/", $m[1], $matches);
return in_array($kind, $matches[1], true);
} catch (Throwable $e) {
error_log('[POLANA_MENU_TYPES] ' . $e->getMessage());
return false;
}
}
function polana_admin_wine_ready(): bool
{
if (!polana_admin_menu_kind_allowed('wine')) return false;
try {
$required = [
'menu_categories' => ['name_en'],
'menu_items' => ['name_en', 'description_en', 'ingredients_en', 'allergens_en'],
];
foreach ($required as $table => $columns) {
foreach ($columns as $column) {
$stmt = db()->query("SHOW COLUMNS FROM `$table` LIKE " . db()->quote($column));
if (!$stmt->fetch(PDO::FETCH_ASSOC)) return false;
}
}
return true;
} catch (Throwable $e) {
error_log('[POLANA_WINE_READY] ' . $e->getMessage());
return false;
}
}
$wineAllowed = polana_admin_wine_ready();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
if (!polana_schema_ready()) {
flash('error', 'Brak kolumny menu_type. Uruchom migrację PL/EN i ponów tworzenie karty.');
redirect('/admin/polana-bilingual-install.php');
}
$action = (string)($_POST['action'] ?? '');
if ($action === 'new') {
$name = trim((string)($_POST['name'] ?? 'Nowe menu'));
$validFrom = (string)($_POST['valid_from'] ?? date('Y-m-01'));
$kind = polana_menu_type((string)($_POST['menu_type'] ?? 'food'));
if (!polana_admin_menu_kind_allowed($kind) || ($kind === 'wine' && !$wineAllowed)) {
flash('error', 'Baza nie obsługuje jeszcze typu „' . polana_type_label($kind, 'pl') . '”. Administrator musi uruchomić naprawę PL/EN.');
redirect('/admin/menus.php');
}
$slug = 'menu-' . date('Ymd-His') . '-' . bin2hex(random_bytes(3));
try {
$stmt = db()->prepare(
'INSERT INTO menus (name, slug, status, valid_from, intro, seo_title, seo_description, menu_type)
VALUES (?, ?, "draft", ?, ?, ?, ?, ?)'
);
$stmt->execute([
$name ?: 'Nowe menu',
$slug,
$validFrom ?: null,
'Poznaj nasze aktualne menu.',
'Menu | ' . setting('site_name', APP_NAME),
'Aktualne menu restauracji ' . setting('site_name', APP_NAME) . '.',
$kind,
]);
$id = (int)db()->lastInsertId();
admin_log('menu_create', 'Utworzono menu #' . $id . ' (' . $kind . ')');
flash('success', 'Utworzono kartę: ' . polana_type_label($kind, 'pl') . '. Dodaj kategorię i pozycje.');
redirect('/admin/menu.php?id=' . $id);
} catch (Throwable $e) {
error_log('[POLANA_MENU_CREATE] ' . $e->getMessage());
flash('error', 'Nie udało się utworzyć karty „' . polana_type_label($kind, 'pl') . '”. Sprawdź migrację PL/EN lub log PHP (kod ' . (string)$e->getCode() . ').');
redirect('/admin/menus.php');
}
}
if ($action === 'copy') {
$sourceId = (int)($_POST['source_id'] ?? 0);
$stmt = db()->prepare('SELECT * FROM menus WHERE id = ?');
$stmt->execute([$sourceId]);
$source = $stmt->fetch();
if (!$source) {
flash('error', 'Nie znaleziono menu do skopiowania.');
redirect('/admin/menus.php');
}
$sourceType = polana_menu_type((string)($source['menu_type'] ?? 'food'));
if (!polana_admin_menu_kind_allowed($sourceType) || ($sourceType === 'wine' && !$wineAllowed)) {
flash('error', 'Nie można skopiować tej karty — najpierw napraw obsługę typu ' . polana_type_label($sourceType, 'pl') . '.');
redirect('/admin/menus.php');
}
db()->beginTransaction();
try {
$newName = trim((string)($_POST['new_name'] ?? ('Kopia – ' . $source['name'])));
$validFrom = (string)($_POST['copy_valid_from'] ?? date('Y-m-01'));
$stmt = db()->prepare(
'INSERT INTO menus
(name, slug, status, valid_from, valid_to, intro, pdf_path, seo_title, seo_description, menu_type)
VALUES (?, ?, "draft", ?, NULL, ?, ?, ?, ?, ?)'
);
$stmt->execute([
$newName ?: ('Kopia – ' . $source['name']),
'menu-' . date('Ymd-His') . '-' . random_int(100, 999),
$validFrom ?: null,
$source['intro'],
$source['pdf_path'],
$source['seo_title'],
$source['seo_description'],
$source['menu_type'] ?? 'food',
]);
$newMenuId = (int)db()->lastInsertId();
$catStmt = db()->prepare(
'SELECT * FROM menu_categories WHERE menu_id = ? ORDER BY sort_order, id'
);
$catStmt->execute([$sourceId]);
foreach ($catStmt->fetchAll() as $cat) {
$insertCat = db()->prepare(
'INSERT INTO menu_categories (menu_id, name, description, sort_order, active, name_en, name_es, name_de)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
);
$insertCat->execute([
$newMenuId,
$cat['name'],
$cat['description'],
$cat['sort_order'],
$cat['active'],
$cat['name_en'] ?? null,
$cat['name_es'] ?? null,
$cat['name_de'] ?? null,
]);
$newCatId = (int)db()->lastInsertId();
$itemStmt = db()->prepare(
'SELECT * FROM menu_items WHERE category_id = ? ORDER BY sort_order, id'
);
$itemStmt->execute([(int)$cat['id']]);
foreach ($itemStmt->fetchAll() as $item) {
$insertItem = db()->prepare(
'INSERT INTO menu_items
(category_id, name, description, ingredients, ingredients_en, price, price_text, weight, allergens, allergens_en,
image_path, available, active, featured, sort_order, name_en, description_en, name_es, name_de, description_es, description_de, ingredients_es, ingredients_de, allergens_es, allergens_de)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
);
$insertItem->execute([
$newCatId,
$item['name'],
$item['description'],
$item['ingredients'] ?? null,
$item['ingredients_en'] ?? null,
$item['price'],
$item['price_text'],
$item['weight'],
$item['allergens'],
$item['allergens_en'] ?? null,
$item['image_path'],
$item['available'],
$item['active'],
$item['featured'],
$item['sort_order'],
$item['name_en'] ?? null,
$item['description_en'] ?? null,
$item['name_es'] ?? null,
$item['name_de'] ?? null,
$item['description_es'] ?? null,
$item['description_de'] ?? null,
$item['ingredients_es'] ?? null,
$item['ingredients_de'] ?? null,
$item['allergens_es'] ?? null,
$item['allergens_de'] ?? null,
]);
}
}
db()->commit();
admin_log('menu_copy', 'Skopiowano menu #' . $sourceId . ' do #' . $newMenuId);
flash('success', 'Menu zostało skopiowane. Możesz zmieniać ceny i pozycje.');
redirect('/admin/menu.php?id=' . $newMenuId);
} catch (Throwable $e) {
if (db()->inTransaction()) db()->rollBack();
error_log('[POLANA_MENU_COPY] ' . $e->getMessage());
flash('error', 'Nie udało się skopiować menu. Sprawdź log PHP (kod ' . (string)$e->getCode() . ').');
redirect('/admin/menus.php');
}
}
}
$menus = db()->query('SELECT * FROM menus ORDER BY id DESC')->fetchAll();
render_admin_header('Menu');
?>
<div class="admin-title-row">
<div>
<h1>Wersje menu</h1>
<p><a class="btn btn-light" href="/admin/translate-site.php">Strona EN – tłumaczenia</a>
<a class="btn btn-green" href="/admin/translate-site-multi.php?lang=es">Strona ES / DE – tłumaczenia</a></p>
<p><a href="/admin/polana-bilingual-install.php">Migracja PL/EN/ES/DE →</a></p>
<p>Twórz kolejne miesięczne karty bez zmiany adresu QR.</p>
</div>
</div>
<?php if (!$wineAllowed): ?>
<div class="flash flash-error">Baza danych nie obsługuje jeszcze kart win. <a href="/admin/polana-bilingual-install.php">Otwórz naprawę PL/EN →</a>. Wykonaj kopię bazy przed migracją.</div>
<?php endif; ?>
<div class="two-col">
<section class="form-card">
<h2>Nowe puste menu</h2>
<form method="post" class="form-grid">
<?= csrf_field() ?>
<input type="hidden" name="action" value="new">
<label class="full">
Nazwa
<input name="name" value="Menu <?= e(date('m/Y')) ?>" required>
</label>
<label>
Rodzaj karty
<select name="menu_type">
<?php foreach (polana_types() as $kind => $labels): ?>
<option value="<?= e($kind) ?>"><?= e($labels['pl']) ?></option>
<?php endforeach; ?>
</select>
</label>
<label>
Obowiązuje od
<input type="date" name="valid_from" value="<?= e(date('Y-m-01')) ?>">
</label>
<div style="align-self:end">
<button class="btn btn-primary" type="submit">Utwórz</button>
</div>
</form>
</section>
<section class="form-card">
<h2>Kopiuj istniejące</h2>
<?php if ($menus): ?>
<form method="post" class="form-grid">
<?= csrf_field() ?>
<input type="hidden" name="action" value="copy">
<label class="full">
Karta źródłowa
<select name="source_id" required>
<?php foreach ($menus as $m): ?>
<option value="<?= (int)$m['id'] ?>"><?= e($m['name']) ?> (<?= e(polana_type_label((string)($m['menu_type'] ?? 'food'), 'pl')) ?>)</option>
<?php endforeach; ?>
</select>
</label>
<label>
Nowa nazwa
<input name="new_name" value="Menu <?= e(date('m/Y', strtotime('+1 month'))) ?>">
</label>
<label>
Obowiązuje od
<input type="date" name="copy_valid_from" value="<?= e(date('Y-m-01', strtotime('+1 month'))) ?>">
</label>
<div class="full">
<button class="btn btn-green" type="submit">Skopiuj kartę</button>
</div>
</form>
<?php else: ?>
<p class="muted">Najpierw utwórz pierwsze menu.</p>
<?php endif; ?>
</section>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Nazwa</th><th>Rodzaj karty</th>
<th>Status</th>
<th>Od</th>
<th>Do</th>
<th>Aktualizacja</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($menus as $menu): ?>
<tr>
<td><strong><?= e($menu['name']) ?></strong></td>
<td><?= e(polana_type_label((string)($menu['menu_type'] ?? 'food'), 'pl')) ?></td>
<td><span class="badge badge-<?= e($menu['status']) ?>"><?= e($menu['status']) ?></span></td>
<td><?= e($menu['valid_from'] ?: '—') ?></td>
<td><?= e($menu['valid_to'] ?: '—') ?></td>
<td><?= e($menu['updated_at']) ?></td>
<td>
<a class="btn btn-small btn-primary" href="/admin/menu.php?id=<?= (int)$menu['id'] ?>">Edytuj</a>
<a class="btn btn-small btn-light" href="/admin/menu-export.php?id=<?= (int)$menu['id'] ?>&format=docx&lang=pl">Word PL ↓</a>
<?php /* TKLM_WORD_V5_LINKS */ require_once dirname(__DIR__).'/includes/tklm_word_v5.php'; echo tklm_v5_word_buttons((int)($menu['id'] ?? $m['id'] ?? $row['id'] ?? 0)); ?>
<a class="btn btn-small btn-light" href="/menu-druk.php?menu_id=<?= (int)$menu['id'] ?>&kind=<?= e(polana_menu_type((string)($menu['menu_type'] ?? 'food'))) ?>&lang=pl" target="_blank" rel="noopener">PDF PL ↗</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php render_admin_footer(); ?>