aboutsummaryrefslogtreecommitdiffstats
path: root/gaming/runescape/_lib.php
diff options
context:
space:
mode:
Diffstat (limited to 'gaming/runescape/_lib.php')
-rw-r--r--gaming/runescape/_lib.php182
1 files changed, 182 insertions, 0 deletions
diff --git a/gaming/runescape/_lib.php b/gaming/runescape/_lib.php
new file mode 100644
index 0000000..c8fbbab
--- /dev/null
+++ b/gaming/runescape/_lib.php
@@ -0,0 +1,182 @@
+<?php
+// Shared read-only helpers for the /gaming/runescape/ page.
+// Data is edited via /admin.php?section=runescape; this file just renders it.
+
+function runescape_valid_url_or_path(string $url): bool {
+ if ($url === '') return false;
+ if ($url[0] === '/') return !str_starts_with($url, '//') && !str_contains($url, "\0");
+ $scheme = strtolower(parse_url($url, PHP_URL_SCHEME) ?? '');
+ if (in_array($scheme, ['http', 'https', 'gemini', 'gopher'], true)) return filter_var($url, FILTER_VALIDATE_URL) !== false;
+ return false;
+}
+
+function runescape_data(?string $path = null): array {
+ $path = $path ?? (__DIR__ . '/data.json');
+ $empty = [
+ 'welcome' => [],
+ 'screenshots' => [],
+ 'eras' => [],
+ 'links_section' => ['title' => 'Server Lists', 'disclaimer' => '', 'items' => []],
+ ];
+ 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;
+ }
+ $welcome = [];
+ foreach (($data['welcome'] ?? []) as $line) {
+ $line = trim((string)$line);
+ if ($line !== '') $welcome[] = $line;
+ }
+ $screenshots = [];
+ foreach (($data['screenshots'] ?? []) as $shot) {
+ if (!is_array($shot)) continue;
+ $filename = trim((string)($shot['filename'] ?? ''));
+ if ($filename === '' || str_contains($filename, '..') || str_contains($filename, '/')) continue;
+ $screenshots[] = [
+ 'filename' => $filename,
+ 'caption' => (string)($shot['caption'] ?? ''),
+ ];
+ }
+ $eras = [];
+ foreach (($data['eras'] ?? []) as $era) {
+ if (!is_array($era)) continue;
+ $title = trim((string)($era['title'] ?? ''));
+ if ($title === '') continue;
+ $servers = [];
+ foreach (($era['servers'] ?? []) as $srv) {
+ if (!is_array($srv)) continue;
+ $name = trim((string)($srv['name'] ?? ''));
+ if ($name === '') continue;
+ $url = trim((string)($srv['url'] ?? ''));
+ if ($url !== '' && !runescape_valid_url_or_path($url)) $url = '';
+ $highlights = [];
+ foreach (($srv['highlights'] ?? []) as $hl) {
+ $hl = trim((string)$hl);
+ if ($hl !== '') $highlights[] = $hl;
+ }
+ $links = [];
+ foreach (($srv['links'] ?? []) as $lk) {
+ if (!is_array($lk)) continue;
+ $label = trim((string)($lk['label'] ?? ''));
+ $lurl = trim((string)($lk['url'] ?? ''));
+ if ($label === '' || $lurl === '' || !runescape_valid_url_or_path($lurl)) continue;
+ $links[] = ['label' => $label, 'url' => $lurl];
+ }
+ $srvImage = trim((string)($srv['image'] ?? ''));
+ if ($srvImage !== '' && str_contains($srvImage, '..')) $srvImage = '';
+ $servers[] = [
+ 'id' => (string)($srv['id'] ?? ''),
+ 'name' => $name,
+ 'url' => $url,
+ 'featured' => !empty($srv['featured']),
+ 'desc' => (string)($srv['desc'] ?? ''),
+ 'highlights' => $highlights,
+ 'links' => $links,
+ 'image' => $srvImage,
+ ];
+ }
+ $eraImage = trim((string)($era['image'] ?? ''));
+ if ($eraImage !== '' && str_contains($eraImage, '..')) $eraImage = '';
+ $eras[] = [
+ 'id' => (string)($era['id'] ?? ''),
+ 'title' => $title,
+ 'subtitle' => (string)($era['subtitle'] ?? ''),
+ 'image' => $eraImage,
+ 'servers' => $servers,
+ ];
+ }
+ $linksSection = ['title' => 'Server Lists', 'disclaimer' => '', 'items' => []];
+ if (is_array($data['links_section'] ?? null)) {
+ $ls = $data['links_section'];
+ $linksSection['title'] = trim((string)($ls['title'] ?? '')) ?: 'Server Lists';
+ $linksSection['disclaimer'] = (string)($ls['disclaimer'] ?? '');
+ foreach (($ls['items'] ?? []) as $item) {
+ if (!is_array($item)) continue;
+ $label = trim((string)($item['label'] ?? ''));
+ $lurl = trim((string)($item['url'] ?? ''));
+ if ($label === '' || $lurl === '' || !runescape_valid_url_or_path($lurl)) continue;
+ $linksSection['items'][] = ['label' => $label, 'url' => $lurl, 'desc' => (string)($item['desc'] ?? '')];
+ }
+ }
+ return ['welcome' => $welcome, 'screenshots' => $screenshots, 'eras' => $eras, 'links_section' => $linksSection];
+}
+
+function runescape_screenshot_src(string $filename): string {
+ $filename = trim($filename);
+ if ($filename === '' || str_contains($filename, '..') || str_contains($filename, '/')) return '';
+ return '/gaming/runescape/images/' . rawurlencode($filename);
+}
+
+/** Public URL for an era/server image (uploaded filename, site path, or absolute URL). */
+function runescape_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/runescape/images/' . rawurlencode(basename($image));
+}
+
+/** Render the Welcome list — lines like "::Label::" become bold headings, everything else is a bullet. */
+function runescape_render_welcome(array $lines): void {
+ $he = fn($s) => htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
+ $inList = false;
+ foreach ($lines as $line) {
+ if (preg_match('/^::(.+)::$/', trim($line), $m)) {
+ if ($inList) { echo '</ul>'; $inList = false; }
+ echo '<p class="machine-title machine-title--sub">' . $he(trim($m[1])) . '</p>';
+ continue;
+ }
+ if (!$inList) { echo '<ul class="contact-list">'; $inList = true; }
+ echo '<li>' . $he($line) . '</li>';
+ }
+ if ($inList) echo '</ul>';
+}
+
+/** Render one server card (used inside an era section). */
+function runescape_render_server(array $srv): void {
+ $he = fn($s) => htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
+ $cardClass = $srv['featured'] ? 'machine machine--notes' : 'machine';
+ $img = runescape_image_src((string)($srv['image'] ?? ''));
+ ?>
+ <div class="<?= $cardClass ?>">
+ <div class="<?= $img !== '' ? 'machine-row' : '' ?>">
+ <?php if ($img !== ''): ?>
+ <img class="machine-thumb" src="<?= $he($img) ?>" alt="<?= $he($srv['name']) ?>" loading="lazy" decoding="async">
+ <?php endif; ?>
+ <div class="machine-info<?= $img !== '' ? ' machine-facts' : '' ?>">
+ <div class="machine-title">
+ <?php if ($srv['url'] !== ''): ?>
+ <a href="<?= $he($srv['url']) ?>" target="_blank" rel="noopener noreferrer"><?= $he($srv['name']) ?></a>
+ <?php else: ?>
+ <?= $he($srv['name']) ?>
+ <?php endif; ?>
+ </div>
+ <?php if ($srv['desc'] !== ''): ?>
+ <?= nl2br($he($srv['desc'])) ?>
+ <?php endif; ?>
+ <?php if ($srv['highlights']): ?>
+ <ul class="contact-list">
+ <?php foreach ($srv['highlights'] as $hl): ?><li><?= $he($hl) ?></li><?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+ <?php if ($srv['links']): ?>
+ <p>
+ <?php foreach ($srv['links'] as $i => $lk): ?>
+ <?= $i > 0 ? ' &middot; ' : '' ?><a href="<?= $he($lk['url']) ?>" target="_blank" rel="noopener noreferrer"><?= $he($lk['label']) ?></a>
+ <?php endforeach; ?>
+ </p>
+ <?php endif; ?>
+ </div>
+ </div>
+ </div>
+ <?php
+}