Fix newest homepage detection, file creation dates, real IP, and request log visitor type
- Fix detectNewestPage() to search subdirectories recursively and handle language prefixes - Fix getFileInfo() to preserve frontmatter created date or ctime instead of overwriting with mtime - Automatically store created date in frontmatter when creating/editing files - Add RequestLogger::getClientIp() with proxy and Cloudflare header support - Replace domain column in request log with visitor/bot type badges (Human, AI, Search, Scraper) - Update 'Activiteitenlog' to 'Activiteiten log' in UI and guide
This commit is contained in:
@@ -90,7 +90,11 @@ class CodePressCMS {
|
||||
{
|
||||
if ($this->effectiveDefaultPage === null) {
|
||||
$page = $this->config['default_page'] ?? 'auto';
|
||||
$this->effectiveDefaultPage = ($page === 'auto') ? $this->detectDefaultPage() : $page;
|
||||
$this->effectiveDefaultPage = match ($page) {
|
||||
'auto' => $this->detectDefaultPage(),
|
||||
'newest' => $this->detectNewestPage(),
|
||||
default => $page,
|
||||
};
|
||||
}
|
||||
return $this->effectiveDefaultPage;
|
||||
}
|
||||
@@ -438,7 +442,7 @@ class CodePressCMS {
|
||||
}
|
||||
|
||||
if (isset($result) && $actualFilePath) {
|
||||
$result['file_info'] = $this->getFileInfo($actualFilePath);
|
||||
$result['file_info'] = $this->getFileInfo($actualFilePath, $result['metadata'] ?? []);
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -449,19 +453,31 @@ class CodePressCMS {
|
||||
* Get file information including creation and modification dates
|
||||
*
|
||||
* @param string $filePath Path to the file
|
||||
* @param array $metadata Optional frontmatter metadata
|
||||
* @return array|null File information or null if file doesn't exist
|
||||
*/
|
||||
private function getFileInfo($filePath) {
|
||||
private function getFileInfo($filePath, array $metadata = []) {
|
||||
if (!file_exists($filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stats = stat($filePath);
|
||||
// Use birthtime if available (macOS/BSD), fall back to mtime on Linux where ctime is inode change time
|
||||
$createdTimestamp = $stats['birthtime'] ?? $stats['mtime'];
|
||||
$created = date('d-m-Y H:i', $createdTimestamp);
|
||||
$modified = date('d-m-Y H:i', $stats['mtime']);
|
||||
|
||||
// 1. Check if created date is present in frontmatter metadata
|
||||
$metaCreated = $metadata['created'] ?? $metadata['date'] ?? $metadata['date_created'] ?? $metadata['created_at'] ?? null;
|
||||
if ($metaCreated) {
|
||||
if (is_numeric($metaCreated)) {
|
||||
$created = date('d-m-Y H:i', (int)$metaCreated);
|
||||
} else {
|
||||
$created = (string)$metaCreated;
|
||||
}
|
||||
} else {
|
||||
// 2. Fallback to ctime (inode change time / creation time) or mtime
|
||||
$createdTimestamp = $stats['ctime'] ?? $stats['mtime'];
|
||||
$created = date('d-m-Y H:i', $createdTimestamp);
|
||||
}
|
||||
|
||||
return [
|
||||
'created' => $created,
|
||||
'modified' => $modified,
|
||||
@@ -1366,6 +1382,83 @@ class CodePressCMS {
|
||||
return $candidates[0]['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the newest modified page (last modified timestamp)
|
||||
*
|
||||
* @return string Page key that was most recently modified or created
|
||||
*/
|
||||
private function detectNewestPage(): string
|
||||
{
|
||||
$contentDir = $this->config['content_dir'];
|
||||
$realContentDir = realpath($contentDir);
|
||||
if (!$realContentDir || !is_dir($realContentDir)) return 'index';
|
||||
|
||||
$availableLangs = array_keys($this->getAvailableLanguages());
|
||||
if (empty($availableLangs)) {
|
||||
$availableLangs = ['nl', 'en'];
|
||||
}
|
||||
$langRegex = implode('|', array_map('preg_quote', $availableLangs));
|
||||
|
||||
$candidates = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($realContentDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $fileInfo) {
|
||||
if (!$fileInfo->isFile()) continue;
|
||||
|
||||
$ext = strtolower($fileInfo->getExtension());
|
||||
if (!in_array($ext, ['md', 'php', 'html'])) continue;
|
||||
|
||||
$filePath = $fileInfo->getRealPath();
|
||||
$relative = substr($filePath, strlen($realContentDir) + 1);
|
||||
|
||||
// Skip hidden files/directories (starting with . or -)
|
||||
$parts = explode('/', str_replace('\\', '/', $relative));
|
||||
$skip = false;
|
||||
foreach ($parts as $p) {
|
||||
if ($p !== '' && ($p[0] === '.' || $p[0] === '-')) {
|
||||
$skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($skip) continue;
|
||||
|
||||
// Strip extension
|
||||
$base = preg_replace('/\.(md|php|html)$/i', '', $relative);
|
||||
|
||||
// Handle language prefix e.g. nl.test -> test, or dir/en.page -> dir/page
|
||||
$filename = basename($base);
|
||||
$dirPrefix = dirname($base);
|
||||
$dirPrefix = ($dirPrefix === '.' || $dirPrefix === '') ? '' : $dirPrefix . '/';
|
||||
|
||||
if (preg_match('/^(' . $langRegex . ')\.(.+)$/i', $filename, $m)) {
|
||||
$pageKey = $dirPrefix . $m[2];
|
||||
} else {
|
||||
$pageKey = $base;
|
||||
}
|
||||
|
||||
// If it ends with /index, e.g. folder/index -> folder
|
||||
if (str_ends_with($pageKey, '/index')) {
|
||||
$pageKey = substr($pageKey, 0, -6);
|
||||
}
|
||||
|
||||
$mtime = $fileInfo->getMTime();
|
||||
$candidates[] = [
|
||||
'key' => $pageKey ?: 'index',
|
||||
'mtime' => $mtime
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($candidates)) return 'index';
|
||||
|
||||
usort($candidates, function ($a, $b) {
|
||||
return $b['mtime'] <=> $a['mtime'];
|
||||
});
|
||||
|
||||
return $candidates[0]['key'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get homepage title
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user