View Source
The actual code and article source behind this site. No minification. No transpilation. What you see is what runs.
|
| require_once __DIR__ . '/includes/config.php'; |
| $CATS_DIR = __DIR__ . '/categories'; |
| // ── Helpers ─────────────────────────────────────────────────────────────────── |
| function publicArticleName(string $cat, string $article): string |
| { |
| static $renamed = [ |
| 'Code' => [ |
| 'Creating a Simple SOCKS5 Server and Client in C' => 'Creating a Simple TCP Client and Server in C', |
| ], |
| ]; |
| return $renamed[$cat][$article] ?? $article; |
| } |
| function articleStorageName(string $cat, string $article): string |
| { |
| static $stored = [ |
| 'Code' => [ |
| 'Creating a Simple TCP Client and Server in C' => 'Creating a Simple SOCKS5 Server and Client in C', |
| ], |
| ]; |
| return $stored[$cat][$article] ?? $article; |
| } |
| function parseArticleOrder(string $path): ?int |
| { |
| $fh = fopen($path, 'r'); |
| if ($fh === false) return null; |
| $first = fgets($fh); |
| fclose($fh); |
| $pos = strpos($first, 'order='); |
| if ($pos !== false) { |
| $rest = substr($first, $pos + 6); |
| $num = (int)$rest; |
| if ($num > 0) return $num; |
| } |
| return null; |
| } |
| function articleContent(string $path): string |
| { |
| $lines = file($path, FILE_IGNORE_NEW_LINES); |
| $out = []; |
| foreach ($lines as $line) { |
| if (str_starts_with(trim($line), '<!--date=')) continue; |
| if (str_starts_with(trim($line), '<!--order=')) continue; |
| $out[] = $line; |
| } |
| return trim(implode("\n", $out)); |
| } |
| /** @return array{tutorials: list<array{name:string,count:int}>, old_hands: list<array{name:string,count:int}>} */ |
| function listCategories(string $dir): array |
| { |
| $OLD_HANDS = ['Camping', 'Code Notes', 'Recipes']; |
| if (!is_dir($dir)) return ['tutorials' => [], 'old_hands' => []]; |
| $tutorials = []; |
| $old_hands = []; |
| foreach (array_diff(scandir($dir), ['.', '..']) as $name) { |
| if (!is_dir("$dir/$name")) continue; |
| $count = count(glob("$dir/$name/*.php") ?: []); |
| if ($count === 0) continue; |
| $entry = ['name' => $name, 'count' => $count]; |
| if (in_array($name, $OLD_HANDS, true)) { |
| $old_hands[] = $entry; |
| } else { |
| $tutorials[] = $entry; |
| } |
| } |
| usort($tutorials, fn($a, $b) => strcmp($a['name'], $b['name'])); |
| usort($old_hands, fn($a, $b) => strcmp($a['name'], $b['name'])); |
| return ['tutorials' => $tutorials, 'old_hands' => $old_hands]; |
| } |
| function listArticles(string $dir, string $cat): array |
| { |
| $files = glob("$dir/$cat/*.php") ?: []; |
| $arts = []; |
| foreach ($files as $path) { |
| $arts[] = [ |
| 'name' => publicArticleName($cat, basename($path, '.php')), |
| 'order' => parseArticleOrder($path), |
| ]; |
| } |
| $ordered = array_filter($arts, fn($a) => $a['order'] !== null); |
| $unordered = array_filter($arts, fn($a) => $a['order'] === null); |
| usort($ordered, fn($a, $b) => $a['order'] - $b['order']); |
| usort($unordered, fn($a, $b) => strcmp($a['name'], $b['name'])); |
| $arts = array_values(array_merge($ordered, $unordered)); |
| return $arts; |
| } |
| // ── Routing ─────────────────────────────────────────────────────────────────── |
| $cat = isset($_GET['cat']) ? basename(trim($_GET['cat'])) : ''; |
| $requestedArticle = isset($_GET['article']) ? basename(trim($_GET['article'])) : ''; |
| $article = publicArticleName($cat, $requestedArticle); |
| $articleStorage = articleStorageName($cat, $article); |
| if ($cat && !is_dir("$CATS_DIR/$cat")) $cat = $article = ''; |
| if ($cat && $article && !file_exists("$CATS_DIR/$cat/$articleStorage.php")) $article = ''; |
| if (!$cat) $article = ''; |
| if ($cat && $article && $requestedArticle !== $article) { |
| $canonicalUrl = BASE_PATH . '/categories.php?cat=' . rawurlencode($cat) |
| . '&article=' . rawurlencode($article); |
| header('Location: ' . $canonicalUrl, true, 301); |
| exit; |
| } |
| // ── Page meta ───────────────────────────────────────────────────────────────── |
| $pageTitle = $article ? "$article — dispelled.ca" |
| : ($cat ? "$cat — dispelled.ca" |
| : 'Articles — dispelled.ca'); |
| $currentPage = 'articles'; |
| require_once __DIR__ . '/includes/header.php'; |
| ?> |
| <?php if ($article): ?> |
| <?php $content = articleContent("$CATS_DIR/$cat/$articleStorage.php"); ?> |
| <nav class="breadcrumb" aria-label="Breadcrumb"> |
| <a href="<?= BASE_PATH ?>/categories.php">Articles</a> |
| <span>›</span> |
| <a href="<?= BASE_PATH ?>/categories.php?cat=<?= urlencode($cat) ?>"><?= htmlspecialchars($cat) ?></a> |
| <span>›</span> |
| <span><?= htmlspecialchars($article) ?></span> |
| </nav> |
| <div class="article-content prose-block"> |
| <?= $content ?> |
| </div> |
| <?php elseif ($cat): ?> |
| <?php $arts = listArticles($CATS_DIR, $cat); ?> |
| <nav class="breadcrumb" aria-label="Breadcrumb"> |
| <a href="<?= BASE_PATH ?>/categories.php">Articles</a> |
| <span>›</span> |
| <span><?= htmlspecialchars($cat) ?></span> |
| </nav> |
| <h1 class="page-title"><?= htmlspecialchars($cat) ?></h1> |
| <?php if (empty($arts)): ?> |
| <p style="color:var(--text-muted);font-family:var(--font-mono);font-size:.85rem;">No articles yet.</p> |
| <?php else: ?> |
| <ul class="article-list"> |
| <?php foreach ($arts as $a): ?> |
| <li class="article-row"> |
| <a href="<?= BASE_PATH ?>/categories.php?cat=<?= urlencode($cat) ?>&article=<?= urlencode($a['name']) ?>"> |
| <?= htmlspecialchars($a['name']) ?> |
| </a> |
| </li> |
| <?php endforeach ?> |
| </ul> |
| <?php endif ?> |
| <?php else: ?> |
| <?php $cats = listCategories($CATS_DIR); ?> |
| <h1 class="page-title">Articles</h1> |
| <?php if (!empty($cats['tutorials'])): ?> |
| <h2 class="cat-section-heading">Tutorials</h2> |
| <div class="cat-grid"> |
| <?php foreach ($cats['tutorials'] as $c): ?> |
| <a href="<?= BASE_PATH ?>/categories.php?cat=<?= urlencode($c['name']) ?>" class="cat-card"> |
| <p class="cat-name"><?= htmlspecialchars($c['name']) ?></p> |
| <p class="cat-count"><?= $c['count'] ?> <?= $c['count'] === 1 ? 'article' : 'articles' ?></p> |
| </a> |
| <?php endforeach ?> |
| </div> |
| <?php endif ?> |
| <?php if (!empty($cats['old_hands'])): ?> |
| <h2 class="cat-section-heading">Old Hands</h2> |
| <div class="cat-grid"> |
| <?php foreach ($cats['old_hands'] as $c): ?> |
| <a href="<?= BASE_PATH ?>/categories.php?cat=<?= urlencode($c['name']) ?>" class="cat-card"> |
| <p class="cat-name"><?= htmlspecialchars($c['name']) ?></p> |
| <p class="cat-count"><?= $c['count'] ?> <?= $c['count'] === 1 ? 'article' : 'articles' ?></p> |
| </a> |
| <?php endforeach ?> |
| </div> |
| <?php endif ?> |
| <?php endif ?> |
| <?php require_once __DIR__ . '/includes/footer.php'; ?> |
dispelled