config = $config; $this->buildMenu(); if (isset($_GET['search'])) { $this->performSearch($_GET['search']); } } /** * Build menu structure from content directory * * @return void */ private function buildMenu() { $this->menu = $this->scanDirectory($this->config['content_dir'], ''); } /** * Recursively scan directory for content files and folders * * @param string $dir Directory path to scan * @param string $prefix Relative path prefix * @return array Array of menu items */ private function scanDirectory($dir, $prefix) { if (!is_dir($dir)) return []; $items = scandir($dir); sort($items); $result = []; foreach ($items as $item) { if ($item[0] === '.') continue; $path = $dir . '/' . $item; $relativePath = $prefix ? $prefix . '/' . $item : $item; if (is_dir($path)) { $result[] = [ 'type' => 'folder', 'title' => $this->formatDisplayName($item), 'path' => $relativePath, 'children' => $this->scanDirectory($path, $relativePath) ]; } elseif (preg_match('/\.(md|php|html)$/', $item)) { // Always use filename for navigation (not H1 titles from content) $filename = pathinfo($item, PATHINFO_FILENAME); $title = $this->formatDisplayName($filename); $pathWithoutExt = preg_replace('/\.[^.]+$/', '', $relativePath); $result[] = [ 'type' => 'file', 'title' => $title, 'path' => $pathWithoutExt, 'url' => '?page=' . $pathWithoutExt ]; } } return $result; } /** * Perform search across all content files * * @param string $query Search query string * @return void */ private function performSearch($query) { $this->searchResults = []; $this->searchInDirectory($this->config['content_dir'], '', $query); } /** * Recursively search for query in directory files * * @param string $dir Directory to search in * @param string $prefix Relative path prefix * @param string $query Search query * @return void */ private function searchInDirectory($dir, $prefix, $query) { if (!is_dir($dir)) return; $items = scandir($dir); foreach ($items as $item) { if ($item[0] === '.') continue; $path = $dir . '/' . $item; $relativePath = $prefix ? $prefix . '/' . $item : $item; if (is_dir($path)) { $this->searchInDirectory($path, $relativePath, $query); } elseif (preg_match('/\.(md|php|html)$/', $item)) { $content = file_get_contents($path); if (stripos($content, $query) !== false || stripos($item, $query) !== false) { $title = ucfirst(pathinfo($item, PATHINFO_FILENAME)); $this->searchResults[] = [ 'title' => $title, 'path' => $relativePath, 'url' => '?page=' . $relativePath, 'snippet' => $this->createSnippet($content, $query) ]; } } } } /** * Create search snippet with highlighted query * * @param string $content Full content to create snippet from * @param string $query Search query to highlight * @return string Formatted snippet */ private function createSnippet($content, $query) { $content = strip_tags($content); $pos = stripos($content, $query); if ($pos === false) return substr($content, 0, 100) . '...'; $start = max(0, $pos - 50); $snippet = substr($content, $start, 150); return '...' . $snippet . '...'; } /** * Get current page content based on request * * @return array Page data with title and content */ public function getPage() { if (isset($_GET['search'])) { return $this->getSearchResults(); } // Check if guide is requested if (isset($_GET['guide'])) { return $this->getGuidePage(); } // Check if content directory is empty if ($this->isContentDirEmpty()) { return $this->getGuidePage(); } $page = $_GET['page'] ?? $this->config['default_page']; $page = preg_replace('/\.[^.]+$/', '', $page); $filePath = $this->config['content_dir'] . '/' . $page; $actualFilePath = null; // Check for exact file matches first if (file_exists($filePath . '.md')) { $actualFilePath = $filePath . '.md'; $result = $this->parseMarkdown(file_get_contents($actualFilePath)); } elseif (file_exists($filePath . '.php')) { $actualFilePath = $filePath . '.php'; $result = $this->parsePHP($actualFilePath); } elseif (file_exists($filePath . '.html')) { $actualFilePath = $filePath . '.html'; $result = $this->parseHTML(file_get_contents($actualFilePath)); } elseif (file_exists($filePath)) { $actualFilePath = $filePath; $extension = pathinfo($filePath, PATHINFO_EXTENSION); if ($extension === 'md') { $result = $this->parseMarkdown(file_get_contents($actualFilePath)); } elseif ($extension === 'php') { $result = $this->parsePHP($actualFilePath); } elseif ($extension === 'html') { $result = $this->parseHTML(file_get_contents($actualFilePath)); } } // If no file found, check if it's a directory if (!isset($result) && is_dir($filePath)) { return $this->getDirectoryListing($page, $filePath); } if (isset($result) && $actualFilePath) { $result['file_info'] = $this->getFileInfo($actualFilePath); return $result; } return $this->getError404(); } /** * Get file information including creation and modification dates * * @param string $filePath Path to the file * @return array|null File information or null if file doesn't exist */ private function getFileInfo($filePath) { if (!file_exists($filePath)) { return null; } $stats = stat($filePath); $created = date('d-m-Y H:i', $stats['ctime']); $modified = date('d-m-Y H:i', $stats['mtime']); return [ 'created' => $created, 'modified' => $modified, 'size' => $this->formatFileSize($stats['size']) ]; } /** * Format file size in human readable format * * @param int $bytes File size in bytes * @return string Formatted file size */ private function formatFileSize($bytes) { $units = ['B', 'KB', 'MB', 'GB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, 2) . ' ' . $units[$pow]; } /** * Get search results page content * * @return array Search results page data */ private function getSearchResults() { $query = $_GET['search']; $content = '
No results found.
'; } else { $content .= 'Found ' . count($this->searchResults) . ' results:
'; foreach ($this->searchResults as $result) { $content .= '' . htmlspecialchars($result['path']) . '
'; $content .= '' . htmlspecialchars($result['snippet']) . '
'; $content .= 'Directory not found.
' ]; } $items = scandir($dirPath); sort($items); $hasContent = false; $content .= 'Deze map is leeg.
'; } return [ 'title' => $title, 'content' => $content ]; } /** * Get 404 error page content * * @return array 404 page data */ private function getError404() { return [ 'title' => 'Page Not Found', 'content' => 'The page you are looking for does not exist.
' ]; } /** * Get menu structure * * @return array Menu structure for navigation */ public function getMenu() { return $this->menu; } /** * Render the complete page with template * * @return void */ public function render() { $page = $this->getPage(); $menu = $this->getMenu(); $breadcrumb = $this->getBreadcrumb(); // Get homepage title $homepageTitle = $this->getHomepageTitle(); // Prepare template data $templateData = [ 'site_title' => $this->config['site_title'], 'page_title' => htmlspecialchars($page['title']), 'content' => $page['content'], 'search_query' => isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '', 'menu' => $this->renderMenu($menu), 'breadcrumb' => $breadcrumb, 'default_page' => $this->config['default_page'], 'homepage' => $this->config['default_page'], 'homepage_title' => $homepageTitle, 'author_name' => $this->config['author']['name'] ?? 'CodePress Developer', 'author_website' => $this->config['author']['website'] ?? '#', 'author_git' => $this->config['author']['git'] ?? '#', 'seo_description' => $this->config['seo']['description'] ?? 'CodePress CMS - Lightweight file-based content management system', 'seo_keywords' => $this->config['seo']['keywords'] ?? 'cms, php, content management, file-based' ]; // File info for footer if (isset($page['file_info'])) { $templateData['file_info'] = 'Created: ' . htmlspecialchars($page['file_info']['created']) . ' | Modified: ' . htmlspecialchars($page['file_info']['modified']); $templateData['file_info_block'] = ' | ' . $templateData['file_info'] . ''; } else { $templateData['file_info'] = ''; $templateData['file_info_block'] = ''; } // File info for footer if (isset($page['file_info'])) { $templateData['file_info'] = 'Created: ' . htmlspecialchars($page['file_info']['created']) . ' | Modified: ' . htmlspecialchars($page['file_info']['modified']); } else { $templateData['file_info'] = ''; } // Check if content exists for guide link $hasContent = !$this->isContentDirEmpty(); $templateData['has_content'] = $hasContent; // Don't show site title link on guide page $templateData['show_site_link'] = !$this->isContentDirEmpty() && !isset($_GET['guide']); // Load partials manually $hasContent = !$this->isContentDirEmpty() && !isset($_GET['guide']); $headerContent = file_get_contents($this->config['templates_dir'] . '/assets/header.mustache'); if (!$hasContent) { // Remove the link from header when no content $headerContent = preg_replace('/\s*