Add automatic page title linking functionality

- Added autoLinkPageTitles() function to link page titles
- Added getAllPageTitles() to scan all content pages
- Added extractPageTitle() for MD/PHP/HTML files
- Added CSS styling for auto-links with dotted underline
- Page titles in content now automatically link to their pages
- Prevents linking inside existing HTML tags and links
This commit is contained in:
Edwin Noorlander 2025-11-19 14:23:12 +01:00
parent 35a990ecf3
commit b0483f5882
2 changed files with 96 additions and 0 deletions

View File

@ -219,6 +219,9 @@ class CodePressCMS {
$body = preg_replace('/href="\/blog\/([^"]+)"/', 'href="?page=blog/$1"', $body);
$body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
// Auto-link page titles to existing content pages
$body = $this->autoLinkPageTitles($body);
$body = preg_replace('/\n\n/', '</p><p>', $body);
$body = '<p>' . $body . '</p>';
$body = preg_replace('/<p><\/p>/', '', $body);
@ -231,6 +234,88 @@ class CodePressCMS {
];
}
private function autoLinkPageTitles($content) {
// Get all available pages with their titles
$pages = $this->getAllPageTitles();
foreach ($pages as $pagePath => $pageTitle) {
// Create a pattern that matches the exact page title (case-insensitive)
$pattern = '/\b' . preg_quote($pageTitle, '/') . '\b/i';
// Replace with link, but avoid linking inside existing links or headings
$replacement = function($matches) use ($pageTitle, $pagePath) {
// Check if we're inside an HTML tag or link
$before = substr($matches[0], 0, 50);
if (preg_match('/<[^>]*$/', $before) || preg_match('/href=/', $before)) {
return $matches[0]; // Don't link inside HTML tags
}
return '<a href="?page=' . $pagePath . '" class="auto-link" title="Ga naar ' . htmlspecialchars($pageTitle) . '">' . $matches[0] . '</a>';
};
$content = preg_replace_callback($pattern, $replacement, $content);
}
return $content;
}
private function getAllPageTitles() {
$pages = [];
$this->scanForPageTitles($this->config['content_dir'], '', $pages);
return $pages;
}
private function scanForPageTitles($dir, $prefix, &$pages) {
if (!is_dir($dir)) return;
$items = scandir($dir);
sort($items);
foreach ($items as $item) {
if ($item[0] === '.') continue;
$path = $dir . '/' . $item;
$relativePath = $prefix ? $prefix . '/' . $item : $item;
if (is_dir($path)) {
$this->scanForPageTitles($path, $relativePath, $pages);
} elseif (preg_match('/\.(md|php|html)$/', $item)) {
$title = $this->extractPageTitle($path);
if ($title && !empty(trim($title))) {
$pagePath = preg_replace('/\.[^.]+$/', '', $relativePath);
$pages[$pagePath] = $title;
}
}
}
}
private function extractPageTitle($filePath) {
$content = file_get_contents($filePath);
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
if ($extension === 'md') {
// Extract first H1 from Markdown
if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
return trim($matches[1]);
}
} elseif ($extension === 'php') {
// Extract title from PHP file
if (preg_match('/\$title\s*=\s*["\']([^"\']+)["\']/', $content, $matches)) {
return trim($matches[1]);
}
} elseif ($extension === 'html') {
// Extract title from HTML file
if (preg_match('/<title>(.*?)<\/title>/i', $content, $matches)) {
return trim(strip_tags($matches[1]));
}
if (preg_match('/<h1[^>]*>(.*?)<\/h1>/i', $content, $matches)) {
return trim(strip_tags($matches[1]));
}
}
return null;
}
private function parsePHP($filePath) {
ob_start();
$title = 'Untitled';

View File

@ -162,6 +162,17 @@
.site-info a:hover {
text-decoration: underline;
}
.auto-link {
color: #0d6efd;
text-decoration: none;
border-bottom: 1px dotted #0d6efd;
font-weight: 500;
}
.auto-link:hover {
color: #0a58ca;
text-decoration: none;
border-bottom-style: solid;
}
.search-form {
max-width: 300px;
}