Refine cache purgring

This commit is contained in:
2026-02-18 20:42:48 +01:00
parent b2600485c4
commit 1e52f75627
5 changed files with 182 additions and 12 deletions

View File

@@ -657,12 +657,30 @@ class action_plugin_luxtools extends ActionPlugin
return;
}
$removed = CacheInvalidation::purgeAll();
$message = (string)$this->getLang('cache_invalidate_success');
if ($message === '') {
$message = 'DokuWiki cache invalidated.';
$result = CacheInvalidation::purgeSelected(
$INPUT->bool('luxtools_purge_pagelinks'),
$INPUT->bool('luxtools_purge_thumbs')
);
$parts = [];
$dokuwikiMsg = (string)$this->getLang('cache_invalidate_success');
if ($dokuwikiMsg === '') $dokuwikiMsg = 'DokuWiki cache invalidated.';
$parts[] = $dokuwikiMsg . ' (' . $result['dokuwiki'] . ')';
if ($result['pagelinks'] !== null) {
$msg = (string)$this->getLang('cache_purge_pagelinks_success');
if ($msg === '') $msg = 'Pagelinks cache purged.';
$parts[] = $msg . ' (' . $result['pagelinks'] . ')';
}
msg($message . ' (' . $removed . ')', 1);
if ($result['thumbs'] !== null) {
$msg = (string)$this->getLang('cache_purge_thumbs_success');
if ($msg === '') $msg = 'Thumbnail cache purged.';
$parts[] = $msg . ' (' . $result['thumbs'] . ')';
}
msg(implode(' ', $parts), 1);
$this->redirectToShow($id);
}

View File

@@ -115,6 +115,62 @@
}
}
// ============================================================
// Purge Cache Dialog
// ============================================================
function initPurgeCacheDialog() {
var $link = jQuery('a.luxtools-invalidate-cache');
if ($link.length === 0) return;
$link.on('click.luxtools', function (e) {
e.preventDefault();
var href = $link.attr('href') || '';
var lang = (window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools)
? window.LANG.plugins.luxtools
: {};
var $dialog = jQuery(
'<div>' +
'<p>' + (lang.cache_purge_dialog_intro || 'The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:') + '</p>' +
'<p><label><input type="checkbox" id="luxtools-purge-pagelinks"> <strong>' + (lang.cache_purge_pagelinks_label || 'Pagelinks') + '</strong>' +
' &ndash; ' + (lang.cache_purge_pagelinks_desc || 'Purges the pagelink mapping cache') + '</label></p>' +
'<p><label><input type="checkbox" id="luxtools-purge-thumbs"> <strong>' + (lang.cache_purge_thumbs_label || 'Thumbnails') + '</strong>' +
' &ndash; ' + (lang.cache_purge_thumbs_desc || 'Purges all cached image thumbnails') + '</label></p>' +
'</div>'
);
$dialog.dialog({
title: lang.cache_purge_dialog_title || 'Purge Cache',
modal: true,
width: 420,
buttons: [
{
text: lang.cache_purge_cancel || 'Cancel',
click: function () { $dialog.dialog('close'); }
},
{
text: lang.cache_purge_confirm || 'Purge Cache',
click: function () {
var url = href;
if ($dialog.find('#luxtools-purge-pagelinks').prop('checked')) {
url += '&luxtools_purge_pagelinks=1';
}
if ($dialog.find('#luxtools-purge-thumbs').prop('checked')) {
url += '&luxtools_purge_thumbs=1';
}
$dialog.dialog('close');
window.location.href = url;
}
}
],
close: function () {
jQuery(this).dialog('destroy').remove();
}
});
});
}
// ============================================================
// Initialize
// ============================================================
@@ -123,6 +179,7 @@
if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
initChronologicalEventTimes();
if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
initPurgeCacheDialog();
}, false);
document.addEventListener('DOMContentLoaded', function () {
if (Scratchpads && Scratchpads.init) Scratchpads.init();

View File

@@ -90,3 +90,13 @@ $lang["cache_invalidate_button_title"] = "Gesamten DokuWiki-Cache leeren";
$lang["cache_invalidate_success"] = "DokuWiki-Cache invalidiert.";
$lang["cache_invalidate_denied"] = "Nur Admins dürfen den Cache invalidieren.";
$lang["cache_invalidate_badtoken"] = "Sicherheits-Token ungültig. Bitte erneut versuchen.";
$lang["cache_purge_dialog_title"] = "Cache leeren";
$lang["cache_purge_dialog_intro"] = "Der DokuWiki-Cache wird immer geleert. Optional können auch die luxtools-spezifischen Caches geleert werden (das Neu-Aufbauen dieser Caches ist ressourcenintensiv):";
$lang["cache_purge_pagelinks_label"] = "Seitenlinks";
$lang["cache_purge_pagelinks_desc"] = "Leert den Seitenlink-Zuordnungs-Cache (pagelink_cache.json)";
$lang["cache_purge_thumbs_label"] = "Vorschaubilder";
$lang["cache_purge_thumbs_desc"] = "Leert alle gecachten Vorschaubilder (thumbs-Ordner)";
$lang["cache_purge_cancel"] = "Abbrechen";
$lang["cache_purge_confirm"] = "Cache leeren";
$lang["cache_purge_pagelinks_success"] = "Seitenlink-Cache geleert.";
$lang["cache_purge_thumbs_success"] = "Vorschaubild-Cache geleert.";

View File

@@ -91,3 +91,13 @@ $lang["cache_invalidate_button_title"] = "Purge the entire DokuWiki cache";
$lang["cache_invalidate_success"] = "DokuWiki cache invalidated.";
$lang["cache_invalidate_denied"] = "Only admins can invalidate cache.";
$lang["cache_invalidate_badtoken"] = "Security token mismatch. Please retry.";
$lang["cache_purge_dialog_title"] = "Purge Cache";
$lang["cache_purge_dialog_intro"] = "The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches (rebuilding these is resource-intensive):";
$lang["cache_purge_pagelinks_label"] = "Pagelinks";
$lang["cache_purge_pagelinks_desc"] = "Purges the pagelink mapping cache (pagelink_cache.json)";
$lang["cache_purge_thumbs_label"] = "Thumbnails";
$lang["cache_purge_thumbs_desc"] = "Purges all cached image thumbnails (thumbs folder)";
$lang["cache_purge_cancel"] = "Cancel";
$lang["cache_purge_confirm"] = "Purge Cache";
$lang["cache_purge_pagelinks_success"] = "Pagelinks cache purged.";
$lang["cache_purge_thumbs_success"] = "Thumbnail cache purged.";

View File

@@ -12,11 +12,12 @@ namespace dokuwiki\plugin\luxtools;
class CacheInvalidation
{
/**
* Purge the complete DokuWiki cache directory.
* Purge the DokuWiki cache directory, preserving the luxtools subfolder.
*
* This removes all files and subdirectories inside the configured
* cache directory ($conf['cachedir']). It is equivalent to running
* `rm -rf data/cache/*` on the server.
* Removes all files and subdirectories inside $conf['cachedir'] except
* the `luxtools/` subdirectory, which contains plugin-specific caches
* (thumbnails, pagelink mapping) that are expensive to rebuild and are
* managed separately via {@see purgePagelinks()} and {@see purgeThumbs()}.
*
* @return int Number of removed entries.
*/
@@ -29,7 +30,79 @@ class CacheInvalidation
return 0;
}
return self::removeDirectoryEntries($cacheDir);
return self::removeDirectoryEntries($cacheDir, ['luxtools']);
}
/**
* Purge the luxtools pagelink cache file.
*
* Deletes {@see PageLink::CACHE_FILE} from the luxtools cache sub-directory
* ($conf['cachedir']/luxtools/pagelink_cache.json). The file is rebuilt
* automatically on the next page request that needs it.
*
* @return int 1 if the file was removed, 0 otherwise.
*/
public static function purgePagelinks(): int
{
global $conf;
$cacheDir = rtrim((string)($conf['cachedir'] ?? ''), '/');
if ($cacheDir === '') {
return 0;
}
$file = $cacheDir . '/luxtools/pagelink_cache.json';
if (!file_exists($file)) {
return 0;
}
return @unlink($file) ? 1 : 0;
}
/**
* Purge the luxtools thumbnail cache directory.
*
* Removes all files and sub-directories inside
* $conf['cachedir']/luxtools/thumbs. Thumbnails are regenerated
* on demand by file.php.
*
* @return int Number of removed entries.
*/
public static function purgeThumbs(): int
{
global $conf;
$cacheDir = rtrim((string)($conf['cachedir'] ?? ''), '/');
if ($cacheDir === '') {
return 0;
}
$thumbsDir = $cacheDir . '/luxtools/thumbs';
if (!is_dir($thumbsDir)) {
return 0;
}
return self::removeDirectoryEntries($thumbsDir);
}
/**
* Execute a selective cache purge.
*
* Always purges the DokuWiki cache (skipping the luxtools subfolder).
* Optionally also purges the pagelinks cache and/or the thumbnail cache.
*
* @param bool $pagelinks Whether to also purge the pagelink cache.
* @param bool $thumbs Whether to also purge the thumbnail cache.
* @return array{dokuwiki: int, pagelinks: int|null, thumbs: int|null}
* Removed-entry counts per component; pagelinks/thumbs are null when not purged.
*/
public static function purgeSelected(bool $pagelinks, bool $thumbs): array
{
return [
'dokuwiki' => self::purgeAll(),
'pagelinks' => $pagelinks ? self::purgePagelinks() : null,
'thumbs' => $thumbs ? self::purgeThumbs() : null,
];
}
/**
@@ -37,9 +110,10 @@ class CacheInvalidation
* without removing the directory itself.
*
* @param string $directory
* @param string[] $skip Entry names (not paths) to leave untouched.
* @return int Number of removed files/directories.
*/
public static function removeDirectoryEntries(string $directory): int
public static function removeDirectoryEntries(string $directory, array $skip = []): int
{
if ($directory === '' || !is_dir($directory) || !is_readable($directory)) {
return 0;
@@ -51,6 +125,7 @@ class CacheInvalidation
$removed = 0;
foreach ($entries as $entry) {
if ($entry === '.' || $entry === '..') continue;
if ($skip !== [] && in_array($entry, $skip, true)) continue;
$path = $directory . '/' . $entry;
if (is_dir($path) && !is_link($path)) {