diff options
| author | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
|---|---|---|
| committer | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
| commit | 898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch) | |
| tree | 85c6ee5ad58b860144551184d4cf86b560c62b91 /gaming/collection/_lib.php | |
| download | www-898b52edcb47bcb3e9d6106e74ca73e74ea01e70.tar.gz www-898b52edcb47bcb3e9d6106e74ca73e74ea01e70.zip | |
Diffstat (limited to '')
| -rw-r--r-- | gaming/collection/_lib.php | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/gaming/collection/_lib.php b/gaming/collection/_lib.php new file mode 100644 index 0000000..6208465 --- /dev/null +++ b/gaming/collection/_lib.php @@ -0,0 +1,366 @@ +<?php +// Shared helpers for /gaming/collection/ catalog pages. +// Structure inspired by small-web collection catalogs; content & style are SillyLaird's. + +require_once __DIR__ . '/../../partials/i18n.php'; + +function collection_lang(): string { + return _i18n_lang(); +} + +function collection_h(string $k): string { + return htmlspecialchars(t($k), ENT_QUOTES, 'UTF-8'); +} + +function collection_he(string $s): string { + return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); +} + +function collection_load(?string $path = null): array { + $path = $path ?? (__DIR__ . '/games.json'); + $empty = ['consoles' => [], 'hardware' => [], 'systems' => [], 'online' => []]; + if (!is_file($path)) { + return $empty; + } + $raw = file_get_contents($path); + if ($raw === false || trim($raw) === '') { + return $empty; + } + $data = json_decode($raw, true); + if (!is_array($data)) { + return $empty; + } + // Prefer consoles; fall back to legacy videogames key. + $consoles = $data['consoles'] ?? $data['videogames'] ?? []; + return [ + 'consoles' => is_array($consoles) ? $consoles : [], + 'hardware' => is_array($data['hardware'] ?? null) ? $data['hardware'] : [], + 'systems' => is_array($data['systems'] ?? null) ? $data['systems'] : [], + 'online' => is_array($data['online'] ?? null) ? $data['online'] : [], + ]; +} + +function collection_image_src(string $image): string { + $image = trim($image); + if ($image === '') { + return ''; + } + if (str_starts_with($image, 'http://') || str_starts_with($image, 'https://') || str_starts_with($image, '/')) { + return $image; + } + if (str_contains($image, '..') || str_contains($image, "\0")) { + return ''; + } + return '/gaming/collection/images/' . rawurlencode(basename($image)); +} + +function collection_status_class(string $status): string { + $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $status) ?? ''); + $slug = trim($slug, '-'); + return $slug !== '' ? 'status-' . $slug : 'status-other'; +} + +function collection_safe_href(string $url): string { + $url = trim($url); + if ($url === '') { + return ''; + } + if (str_starts_with($url, '/')) { + return str_starts_with($url, '//') ? '' : $url; + } + $scheme = strtolower((string)(parse_url($url, PHP_URL_SCHEME) ?? '')); + if (in_array($scheme, ['http', 'https', 'gemini', 'gopher'], true)) { + return $url; + } + return ''; +} + +/** + * Inventory rows (name :: model) from a flat list (consoles or hardware). + * @param list<array<string,mixed>> $items + * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}> + */ +function collection_inventory_rows(array $items): array { + $rows = []; + foreach ($items as $item) { + if (!is_array($item)) continue; + $name = trim((string)($item['name'] ?? '')); + $model = trim((string)($item['model'] ?? '')); + if ($name === '') continue; + if ($model === '') $model = $name; + $status = (string)($item['status'] ?? 'Owned'); + $rows[] = [ + 'left' => $name, + 'right' => $model, + 'status' => $status, + 'notes' => (string)($item['notes'] ?? ''), + 'image' => (string)($item['image'] ?? ''), + 'url' => '', + 'specs' => [ + ['label' => t('collection.spec_system'), 'value' => $name], + ['label' => t('collection.spec_status'), 'value' => $status], + ], + ]; + } + return $rows; +} + +/** Consoles inventory. */ +function collection_console_rows(array $data): array { + return collection_inventory_rows($data['consoles'] ?? []); +} + +/** Hardware inventory. */ +function collection_hardware_inventory_rows(array $data): array { + return collection_inventory_rows($data['hardware'] ?? []); +} + +/** @deprecated use collection_console_rows */ +function collection_videogame_rows(array $data): array { + return collection_console_rows($data); +} + +/** + * Video games library: titles under systems (system :: title). + * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}> + */ +function collection_games_library_rows(array $data): array { + $rows = []; + foreach (($data['systems'] ?? []) as $sys) { + if (!is_array($sys)) continue; + $left = (string)($sys['name'] ?? ''); + foreach (($sys['games'] ?? []) as $game) { + if (!is_array($game)) continue; + $title = trim((string)($game['title'] ?? '')); + if ($title === '') continue; + $status = (string)($game['status'] ?? 'Owned'); + $rows[] = [ + 'left' => $left, + 'right' => $title, + 'status' => $status, + 'notes' => (string)($game['notes'] ?? ''), + 'image' => (string)($game['image'] ?? ''), + 'url' => '', + 'specs' => [ + ['label' => t('collection.spec_system'), 'value' => $left], + ['label' => t('collection.spec_status'), 'value' => $status], + ], + ]; + } + } + return $rows; +} + +/** @deprecated */ +function collection_tech_rows(array $data): array { + return collection_games_library_rows($data); +} + +/** @deprecated */ +function collection_hardware_rows(array $data): array { + return collection_games_library_rows($data); +} + +/** + * Flatten online song packs into catalog rows. + * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}> + */ +function collection_pack_rows(array $data): array { + $rows = []; + foreach ($data['online'] as $cat) { + if (!is_array($cat)) continue; + $left = (string)($cat['name'] ?? ''); + foreach (($cat['entries'] ?? []) as $entry) { + if (!is_array($entry)) continue; + $title = trim((string)($entry['title'] ?? '')); + if ($title === '') continue; + $status = (string)($entry['status'] ?? 'Owned'); + $url = (string)($entry['url'] ?? ''); + $specs = [ + ['label' => t('collection.spec_game'), 'value' => $left], + ['label' => t('collection.spec_status'), 'value' => $status], + ]; + if ($url !== '') { + $specs[] = ['label' => t('collection.spec_link'), 'value' => t('collection.spec_link_yes')]; + } + $rows[] = [ + 'left' => $left, + 'right' => $title, + 'status' => $status, + 'notes' => (string)($entry['notes'] ?? ''), + 'image' => (string)($entry['image'] ?? ''), + 'url' => $url, + 'specs' => $specs, + ]; + } + } + return $rows; +} + +/** + * @param list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array}> $rows + * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array}> + */ +function collection_filter_wishlist(array $rows): array { + return array_values(array_filter( + $rows, + static fn(array $r) => strcasecmp((string)($r['status'] ?? ''), 'Wishlist') === 0 + )); +} + +/** + * Render one catalog row (photo + name + icon-style specs). + * @param array{left:string,right:string,status:string,notes:string,image:string,url:string,specs?:array} $row + */ +function collection_render_row(array $row): void { + $left = (string)($row['left'] ?? ''); + $right = (string)($row['right'] ?? ''); + $status = (string)($row['status'] ?? 'Owned'); + $notes = (string)($row['notes'] ?? ''); + $img = collection_image_src((string)($row['image'] ?? '')); + $href = collection_safe_href((string)($row['url'] ?? '')); + $specs = is_array($row['specs'] ?? null) ? $row['specs'] : []; + $he = 'collection_he'; + $h = 'collection_h'; + ?> + <div class="gc-row"> + <div class="gc-pic"> + <?php if ($img !== ''): ?> + <a href="<?= $he($img) ?>" target="_blank" rel="noopener noreferrer"> + <img src="<?= $he($img) ?>" alt="" loading="lazy" decoding="async" width="200" height="150" /> + </a> + <?php else: ?> + <div class="gc-pic-empty" aria-hidden="true">no photo</div> + <?php endif; ?> + </div> + <div class="gc-body"> + <p class="gc-name"> + <?php if ($href !== ''): ?> + <a href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $he($left) ?></a> + <span class="gc-colon">::</span> + <a href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $he($right) ?></a> + <?php else: ?> + <span class="gc-nick"><?= $he($left) ?></span> + <span class="gc-colon">::</span> + <span class="gc-model"><?= $he($right) ?></span> + <?php endif; ?> + </p> + <?php if ($notes !== ''): ?> + <p class="gc-note"><em><?= nl2br($he($notes)) ?></em></p> + <?php endif; ?> + <p class="gc-specs"> + <?php foreach ($specs as $spec): + $label = (string)($spec['label'] ?? ''); + $value = (string)($spec['value'] ?? ''); + if ($value === '') continue; + ?> + <span class="gc-spec"> + <span class="gc-spec-ico" title="<?= $he($label) ?>" aria-hidden="true"><?= $he(mb_substr($label, 0, 1)) ?></span> + <span class="gc-spec-val"><?= $he($value) ?></span> + </span> + <?php endforeach; ?> + <?php if ($href !== ''): ?> + <span class="gc-spec"> + <span class="gc-spec-ico" aria-hidden="true">↗</span> + <a class="gc-spec-val" href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $h('collection.visit_product') ?></a> + </span> + <?php endif; ?> + </p> + </div> + </div> + <?php +} + +/** + * Shared page shell open (head + header + title + menu). + * @param 'games'|'consoles'|'hardware'|'packs'|'wishlist' $active + */ +function collection_page_open(string $active, string $pageTitle, string $pageDesc, string $baseFile): void { + $lang = collection_lang(); + $langAttr = ['en' => 'en', 'ja' => 'ja', 'zh' => 'zh'][$lang] ?? 'en'; + $suffix = ['en' => '', 'ja' => '_jp', 'zh' => '_zh'][$lang] ?? ''; + $selfName = $baseFile . $suffix . '.php'; + if ($baseFile === 'index' && $lang === 'en') { + $selfUrl = 'https://www.sillylaird.ca/gaming/collection/'; + $enUrl = 'https://www.sillylaird.ca/gaming/collection/'; + } else { + $selfUrl = 'https://www.sillylaird.ca/gaming/collection/' . $selfName; + $enUrl = 'https://www.sillylaird.ca/gaming/collection/' . ($baseFile === 'index' ? '' : $baseFile . '.php'); + if ($baseFile === 'index') { + $enUrl = 'https://www.sillylaird.ca/gaming/collection/'; + } + } + $jaUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_jp.php'; + $zhUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_zh.php'; + if ($baseFile === 'index') { + $jaUrl = 'https://www.sillylaird.ca/gaming/collection/index_jp.php'; + $zhUrl = 'https://www.sillylaird.ca/gaming/collection/index_zh.php'; + } + $h = 'collection_h'; + // Video Games / Consoles / Hardware / Song Packs / Wishlist + if ($lang === 'ja') { + $hrefGames = '/gaming/collection/index_jp.php'; + $hrefCons = '/gaming/collection/consoles_jp.php'; + $hrefHw = '/gaming/collection/hardware_jp.php'; + $hrefPk = '/gaming/collection/packs_jp.php'; + $hrefWl = '/gaming/collection/wishlist_jp.php'; + } elseif ($lang === 'zh') { + $hrefGames = '/gaming/collection/index_zh.php'; + $hrefCons = '/gaming/collection/consoles_zh.php'; + $hrefHw = '/gaming/collection/hardware_zh.php'; + $hrefPk = '/gaming/collection/packs_zh.php'; + $hrefWl = '/gaming/collection/wishlist_zh.php'; + } else { + $hrefGames = '/gaming/collection/'; + $hrefCons = '/gaming/collection/consoles.php'; + $hrefHw = '/gaming/collection/hardware.php'; + $hrefPk = '/gaming/collection/packs.php'; + $hrefWl = '/gaming/collection/wishlist.php'; + } + $act = static fn(string $key) => $active === $key ? ' class="gc-menu-active" id="gc-main"' : ''; + ?> +<!doctype html> +<html lang="<?= $langAttr ?>"> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title><?= collection_he($pageTitle) ?></title> + <?php + $meta = [ + 'lang' => $lang, + 'title' => $pageTitle, + 'desc' => $pageDesc, + 'base' => 'gaming/collection/' . $baseFile, + 'self_url' => $selfUrl, + 'en_url' => $enUrl, + 'x_default' => $enUrl, + 'ja_url' => $jaUrl, + 'zh_url' => $zhUrl, + ]; + include __DIR__ . '/../../partials/head_meta.php'; + ?> + <link rel="stylesheet" href="/assets/css/fonts.css" /> + <link rel="stylesheet" href="/assets/css/site.css" /> + <link rel="stylesheet" href="/assets/css/skeleton.css" /> + <link rel="stylesheet" href="/assets/css/pages/collection.css" /> +</head> +<body class="gc-page"> + <a class="skip-link" href="#main"><?= $h('common.skip') ?></a> + <?php include __DIR__ . '/../../partials/header.php'; ?> + + <main id="main" class="wrap gc-wrap"> + <p class="gc-title"><?= $h('collection.h1') ?></p> + <nav class="gc-menu" aria-label="<?= $h('collection.jump_nav') ?>"> + <a href="<?= collection_he($hrefGames) ?>"<?= $act('games') ?>><?= $h('collection.menu_games') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefCons) ?>"<?= $act('consoles') ?>><?= $h('collection.menu_consoles') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefHw) ?>"<?= $act('hardware') ?>><?= $h('collection.menu_hardware') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefPk) ?>"<?= $act('packs') ?>><?= $h('collection.menu_packs') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefWl) ?>"<?= $act('wishlist') ?>><?= $h('collection.menu_wishlist') ?></a> + </nav> + <?php +} + +function collection_page_close(): void { + ?> + </main> + <?php include __DIR__ . '/../../partials/footer.php'; ?> +</body> +</html> + <?php +} |
