0) {
// Einen zufälligen auswählen
$randomFile = $files[array_rand($files)];
// In relative URL umwandeln
$relativePath = str_replace($_SERVER['DOCUMENT_ROOT'], '', $randomFile);
return $relativePath;
}
// Fallback: statisches no-image
return '/img/no-image-katie-1.png';
}
}
/**
* DB-Connector (keine Fehlerdetails nach außen)
*/
function lg_db(): mysqli
{
global $servername, $username, $password, $db;
$conn = new mysqli($servername, $username, $password, $db);
if ($conn->connect_error) {
http_response_code(500);
exit('Interner Fehler (DB)');
}
$conn->set_charset('utf8mb4');
return $conn;
}
/**
* Einzelne Karten-Kachel rendern
*/
function generateListItem(
int $ListItemID,
?string $ItemImage,
string $ItemTitle,
?string $ItemLink,
int $ItemPriceCents,
?string $ItemComment,
int $ItemReserved,
?string $ItemDate,
int $ItemQty,
): void {
global $loggedin, $imagedir;
// Preis formatieren (de_DE)
$priceText = '';
if ($ItemPriceCents > 0) {
if (class_exists('NumberFormatter')) {
$fmt = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
$priceText = $fmt->formatCurrency($ItemPriceCents / 100, 'EUR');
} else {
$priceText = number_format($ItemPriceCents / 100, 2, ',', '.') . ' €';
}
}
// Kommentar (2 Zeilen max., CSS macht den Clamp)
$commentPlain = trim((string) $ItemComment);
$commentHtml = ($commentPlain === '') ? ' ' : e($commentPlain);
// Bild (lokal oder Placeholder)
$srcPath = '';
if (!empty($ItemImage)) {
$local = rtrim($imagedir, '/') . '/' . $ItemImage;
$srcPath = @is_file($local) ? $local : lg_default_image();
} else {
$srcPath = lg_default_image();
}
$imageTag = '
 . ')
';
// Titelzeile: Qty-Pill (nur wenn >1) + einzeiliger Titel
$qtyPill = ($ItemQty > 1) ? '×' . (int) $ItemQty . '' : '';
$titleHtml = ''
. $qtyPill
. '
' . e($ItemTitle) . '
'
. '';
// Status-Badge links
$statusLeft = '';
if ($ItemReserved >= $ItemQty) {
$statusLeft = 'alle reserviert';
} elseif ($ItemReserved > 0) {
$statusLeft = ''
. (int) $ItemReserved . ' / ' . (int) $ItemQty . ' reserviert';
}
// Datum (lokal lesbar)
$dateHtml = '';
if (!empty($ItemDate)) {
$ts = strtotime($ItemDate);
$dateHtml = $ts ? date('d.m.Y', $ts) : e($ItemDate);
}
// User-Buttons (Reserve / Cancel / Vendor)
// --- unten: Aktionsleiste (Icons, wachsen bei Hover) ---
// (Reserve nur wenn möglich; Cancel immer; Vendor nur wenn Link)
$userBtns = [];
if ($ItemReserved < $ItemQty) {
$userBtns[] = sprintf(
'',
$ListItemID
);
}
if ($ItemReserved > 0) {
$userBtns[] = sprintf(
'',
$ListItemID
);
}
if (!empty($ItemLink)) {
$safeLink = e($ItemLink);
$userBtns[] =
'
Zum Anbieter
';
}
$userBtnRow = implode("\n", $userBtns);
// ... in deinem Echo-Heredoc:
//
// {$userBtnRow}
//
// Admin-Icons (nur eingeloggt)
$adminFloating = '';
if (!empty($loggedin)) {
$adminFloating = '
';
}
// Preis-Badge rechts
$priceHtml = $priceText !== '' ? '' . e($priceText) . '' : '';
echo <<
{$adminFloating}
{$imageTag}
{$titleHtml}
{$commentHtml}
{$userBtnRow}
HTML;
}
/**
* Haupt-Builder für die Listenansicht
* $ListID: aktive Liste (int, intern)
* $sortby: einer aus der Whitelist unten
*/
function wishlistMainBuilder(int $ListID, string $sortby = 'priority'): void
{
global $loggedin;
$conn = lg_db();
// 1) Listen-Metadaten holen
$stmt = $conn->prepare('SELECT title, description FROM lists WHERE ID = ?');
$stmt->bind_param('i', $ListID);
$stmt->execute();
$res = $stmt->get_result();
$listTitle = 'Unbekannte Liste';
$listDesc = '';
if ($res && $row = $res->fetch_assoc()) {
$listTitle = e((string) $row['title']);
$listDesc = e((string) $row['description']);
}
$stmt->close();
// 2) ORDER BY Whitelist
$orderWhitelist = [
'priority' => 'priority DESC',
'price_asc' => 'price ASC',
'price_desc' => 'price DESC',
'date_desc' => 'date DESC',
'date_asc' => 'date ASC',
'random' => 'RAND()',
];
$orderSql = $orderWhitelist[$sortby] ?? $orderWhitelist['priority'];
// 3) Wünsche laden
$sql = "
SELECT
w.ID,
w.image,
w.title,
w.link,
w.price,
w.description,
w.date,
w.qty,
COALESCE(rc.reserved_count, 0) AS reserved_count
FROM wishes w
LEFT JOIN v_wish_reserved_counts rc
ON rc.wish_id = w.ID
WHERE w.wishlist = ?
ORDER BY {$orderSql}";
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $ListID);
$stmt->execute();
$items = $stmt->get_result();
// 4) Header-Text (vorher berechnen, kein Ternary im Heredoc)
$loginMsg = $loggedin
? 'Eingeloggt: Du kannst Einträge bearbeiten, löschen und priorisieren.'
: 'Tipp: Du kannst Einträge reservieren. Nur mit deinem Reservierungs-Passwort lässt sich die Reservierung wieder lösen.';
echo <<
{$listTitle}
{$listDesc}
{$loginMsg}
HTML;
if ($items !== false && $items->num_rows > 0) {
while ($row = $items->fetch_assoc()) {
generateListItem(
(int) $row['ID'],
$row['image'] !== null ? (string) $row['image'] : null,
(string) $row['title'],
$row['link'] !== null ? (string) $row['link'] : null,
(int) $row['price'],
$row['description'] !== null ? (string) $row['description'] : null,
(int) $row['reserved_count'],
$row['date'] !== null ? (string) $row['date'] : null,
isset($row['qty']) ? (int) $row['qty'] : 1
);
}
} else {
echo '
Keine Einträge vorhanden.
';
}
echo <<
HTML;
$stmt->close();
$conn->close();
}