$filePath, 'name' => $file, 'ext' => $ext ]; } // Let plugins filter content files (e.g., by language) $contentFiles = Hooks::apply(Hook::PROCESS_CONTENT, $contentFiles, $dir); // Sort by filename usort($contentFiles, fn($a, $b) => strnatcmp($a['name'], $b['name'])); return array_column($contentFiles, 'path'); } function parseRequestPath(Context $ctx): array { $requestPath = $ctx->requestPath; if (empty($requestPath)) { return ['type' => 'frontpage', 'path' => $ctx->contentDir]; } $contentPath = $ctx->contentDir . '/' . $requestPath; // Check if it's a directory if (is_dir($contentPath)) { $items = scandir($contentPath) ?: []; $subdirs = array_filter($items, fn($item) => $item !== '.' && $item !== '..' && is_dir("$contentPath/$item") ); if (!empty($subdirs)) { return ['type' => 'list', 'path' => $contentPath]; } else { return ['type' => 'page', 'path' => $contentPath]; } } return ['type' => 'not_found', 'path' => $contentPath]; } function loadMetadata(string $dirPath): ?array { $metadataFile = "$dirPath/metadata.ini"; if (!file_exists($metadataFile)) return null; $metadata = parse_ini_file($metadataFile, true); if (!$metadata) return null; // Get base metadata (non-array values) $baseMetadata = array_filter($metadata, fn($key) => !is_array($metadata[$key]), ARRAY_FILTER_USE_KEY); // Store full metadata for plugins to access $baseMetadata['_raw'] = $metadata; // Let plugins modify metadata (e.g., merge language sections) return Hooks::apply(Hook::PROCESS_CONTENT, $baseMetadata, $dirPath, 'metadata'); } function buildNavigation(Context $ctx): array { $items = scandir($ctx->contentDir) ?: []; $navItems = []; foreach ($items as $item) { if ($item === '.' || $item === '..' || !is_dir($ctx->contentDir . "/$item")) continue; $itemPath = "{$ctx->contentDir}/$item"; $metadata = loadMetadata($itemPath); // Only include if explicitly marked as menu item // parse_ini_file returns boolean true as 1, false as empty string, and "true"/"false" as strings if (!$metadata || !isset($metadata['menu']) || !$metadata['menu']) { continue; } // Extract title $title = $metadata['title'] ?? extractTitle($itemPath) ?? ucfirst($item); // Use slug if available, otherwise use folder name $urlSlug = ($metadata && isset($metadata['slug'])) ? $metadata['slug'] : $item; $navItems[] = [ 'title' => $title, 'url' => '/' . urlencode($urlSlug) . '/', 'order' => (int)($metadata['menu_order'] ?? 999) ]; } // Sort by menu_order usort($navItems, fn($a, $b) => $a['order'] <=> $b['order']); return $navItems; }