!in_array($item, ['.', '..']) && is_dir("$dir/$item") ); } function extractTitle(string $filePath): ?string { $files = findAllContentFiles($filePath); if (empty($files)) return null; // Check the first content file for a title $file = $files[0]; $ext = pathinfo($file, PATHINFO_EXTENSION); $content = file_get_contents($file); if ($ext === 'md' && preg_match('/^#\s+(.+)$/m', $content, $matches)) { return trim($matches[1]); } if (in_array($ext, ['html', 'php']) && preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) { return strip_tags($matches[1]); } return null; } // Build sorted list items for a directory function buildListItems(string $dir, Context $ctx, ?array $parentMetadata): array { $subdirs = getSubdirectories($dir); $items = array_filter(array_map(function($item) use ($dir, $ctx) { $itemPath = "$dir/$item"; $metadata = loadMetadata($itemPath); $coverImage = findCoverImage($itemPath); $pdfFile = findPdfFile($itemPath); $title = $metadata['title'] ?? extractTitle($itemPath) ?? $item; $rawDate = null; $date = null; if (isset($metadata['date'])) { $rawDate = $metadata['date']; $date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format'); } else { $rawDate = extractRawDateFromFolder($item); if ($rawDate) { $date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format'); } else { $rawDate = date("Y-m-d", filemtime($itemPath)); $date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format'); } } $urlSlug = ($metadata && isset($metadata['slug'])) ? $metadata['slug'] : $item; $langPrefix = $ctx->get('langPrefix', ''); $baseUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($urlSlug); $assetUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($item); return [ 'title' => $title, 'url' => $baseUrl . '/', 'date' => $date, 'rawDate' => $rawDate, 'summary' => $metadata['summary'] ?? null, 'cover' => $coverImage ? "$assetUrl/$coverImage" : null, 'pdf' => $pdfFile ? "$assetUrl/$pdfFile" : null, 'redirect' => $metadata['redirect'] ?? null, 'dirPath' => $itemPath ]; }, $subdirs)); $sortOrder = strtolower($parentMetadata['order'] ?? 'descending'); if ($sortOrder === 'ascending') { usort($items, fn($a, $b) => strcmp($a['date'] ?? '', $b['date'] ?? '')); } else { usort($items, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? '')); } return $items; } function extractRawDateFromFolder(string $folderName): ?string { if (preg_match('/^(\d{4})-(\d{2})-(\d{2})-/', $folderName, $matches)) { return $matches[1] . '-' . $matches[2] . '-' . $matches[3]; } return null; } function extractDateFromFolder(string $folderName): ?string { $raw = extractRawDateFromFolder($folderName); if ($raw) { return Hooks::apply(Hook::PROCESS_CONTENT, $raw, 'date_format'); } return null; } function findCoverImage(string $dirPath): ?string { $found = array_find( COVER_IMAGE_EXTENSIONS, fn($ext) => file_exists("$dirPath/cover.$ext") ); return $found ? "cover.$found" : null; } function findPdfFile(string $dirPath): ?string { $pdfs = glob("$dirPath/*.pdf") ?: []; return $pdfs ? basename($pdfs[0]) : null; } function findPageCss(string $dirPath, string $contentDir): ?array { $cssFile = "$dirPath/styles.css"; if (!file_exists($cssFile) || !is_file($cssFile)) { return null; } $relativePath = str_replace($contentDir, '', $dirPath); $relativePath = trim($relativePath, '/'); $cssUrl = '/' . ($relativePath ? $relativePath . '/' : '') . 'styles.css'; return [ 'url' => $cssUrl, 'hash' => hash_file('md5', $cssFile) ]; } function extractMetaDescription(string $dirPath, ?array $metadata): ?string { // 1. Check for search_description in metadata if ($metadata && isset($metadata['search_description'])) { return $metadata['search_description']; } // 2. Fall back to summary in metadata if ($metadata && isset($metadata['summary'])) { return $metadata['summary']; } // 3. Fall back to first paragraph in content files $files = findAllContentFiles($dirPath); if (empty($files)) return null; foreach ($files as $file) { $ext = pathinfo($file, PATHINFO_EXTENSION); $content = file_get_contents($file); if ($ext === 'md') { $lines = explode("\n", $content); foreach ($lines as $line) { $line = trim($line); if (empty($line) || str_starts_with($line, '#')) continue; if (strlen($line) > 20) { return strip_tags($line); } } } elseif (in_array($ext, ['html', 'php'])) { if (preg_match('/]*>(.*?)<\/p>/is', $content, $matches)) { return strip_tags($matches[1]); } } } return null; }