Use thumbnail/placeholder logic for imagebox

This commit is contained in:
2026-01-21 11:03:41 +01:00
parent e1d24c6627
commit 351485efb1
5 changed files with 275 additions and 70 deletions

View File

@@ -74,41 +74,58 @@ class Output
$genThumbW = (int)max(1, (int)round($thumbW * $thumbScale));
$genThumbH = (int)max(1, (int)round($thumbH * $thumbScale));
$placeholderUrl = DOKU_BASE . 'lib/images/blank.gif';
$placeholderId = $syntax ? trim((string)$syntax->getConf('thumb_placeholder')) : '';
if ($placeholderId !== '' && function_exists('ml')) {
// ml() builds a fetch.php URL for a MediaManager item
$placeholderUrl = ml($placeholderId, ['w' => $genThumbW, 'h' => $genThumbH], true, '&');
}
/** @var \Doku_Renderer_xhtml $renderer */
$renderer = $this->renderer;
$renderer->doc .= '<div class="luxtools-plugin luxtools-gallery" data-luxtools-gallery="1">';
global $ID;
$pageId = isset($ID) ? (string)$ID : '';
if (function_exists('cleanID')) {
$pageId = (string)cleanID($pageId);
}
foreach ($this->files as $item) {
$url = $this->itemWebUrl($item, !empty($params['randlinks']));
$thumbUrl = $this->withQueryParams($url, [
'thumb' => 1,
'w' => $genThumbW,
'h' => $genThumbH,
// Keep quality explicit so cache file naming stays stable.
'q' => $thumbQ,
]);
$safeUrl = hsc($url);
$safeThumbUrl = hsc($thumbUrl);
$safePlaceholderUrl = hsc($placeholderUrl);
$label = hsc($item['name']);
$caption = hsc(basename((string)($item['name'] ?? '')));
if ($caption === '') {
$caption = $label;
}
$initialSrc = $safePlaceholderUrl;
$dataThumb = ' data-thumb-src="' . $safeThumbUrl . '"';
$thumbCachePath = $this->thumbCachePathForItem($item, $genThumbW, $genThumbH, $thumbQ);
if (is_string($thumbCachePath) && $thumbCachePath !== '' && @is_file($thumbCachePath)) {
// Thumb already exists: start with it immediately (no JS swap needed)
$initialSrc = $safeThumbUrl;
// Use ThumbnailHelper to get thumbnail info
$imagePath = isset($item['path']) && is_string($item['path']) ? $item['path'] : '';
if ($imagePath !== '' && is_file($imagePath)) {
// Extract root and local from full path for helper
$root = $this->basedir;
$local = '';
if (str_starts_with($imagePath, $root)) {
$local = substr($imagePath, strlen($root));
}
$thumb = ThumbnailHelper::getThumbnail(
$root,
$local,
$pageId,
$genThumbW,
$genThumbH,
$thumbQ,
$placeholderId !== '' ? $placeholderId : null
);
$initialSrc = hsc($thumb['url']);
$dataThumb = $thumb['isFinal'] ? '' : ' data-thumb-src="' . hsc($thumb['thumbUrl']) . '"';
} else {
// Fallback: use URL directly
$thumbUrl = $this->withQueryParams($url, [
'thumb' => 1,
'w' => $genThumbW,
'h' => $genThumbH,
'q' => $thumbQ,
]);
$initialSrc = hsc($thumbUrl);
$dataThumb = '';
}
@@ -161,44 +178,6 @@ class Output
return $url . $glue . http_build_query($params, '', '&', PHP_QUERY_RFC3986) . $fragment;
}
/**
* Compute the expected thumbnail cache path for an item.
*
* Mirrors the hashing scheme in file.php so we can detect whether a thumb
* already exists and can be used immediately.
*
* @param array $item
* @param int $w
* @param int $h
* @param int $q
* @return string|null
*/
protected function thumbCachePathForItem(array $item, int $w, int $h, int $q): ?string
{
if (!isset($item['path']) || !is_string($item['path']) || $item['path'] === '') return null;
if (!isset($item['mtime'])) return null;
$path = $item['path'];
$mtime = (int)$item['mtime'];
// Decide output format the same way file.php does.
try {
[, $mime,] = mimetype($path, false);
} catch (\Throwable $e) {
return null;
}
if (!is_string($mime) || !str_starts_with($mime, 'image/')) return null;
$dstFormat = ($mime === 'image/png' || $mime === 'image/gif') ? 'png' : 'jpg';
global $conf;
if (!isset($conf['cachedir']) || !is_string($conf['cachedir']) || trim($conf['cachedir']) === '') return null;
$hash = sha1($path . '|' . $mtime . '|w=' . $w . '|h=' . $h . '|q=' . $q . '|f=' . $dstFormat);
$sub = substr($hash, 0, 2);
$cacheDir = rtrim($conf['cachedir'], '/');
return $cacheDir . '/luxtools/thumbs/' . $sub . '/' . $hash . '.' . $dstFormat;
}
/**
* Renders the files as a table, including details if configured that way.
*

189
src/ThumbnailHelper.php Normal file
View File

@@ -0,0 +1,189 @@
<?php
namespace dokuwiki\plugin\luxtools;
/**
* Shared utility for thumbnail cache management and display logic.
*
* Used by both the image syntax and the gallery rendering to avoid code duplication.
*/
class ThumbnailHelper
{
/**
* Get thumbnail URL and metadata for rendering.
*
* This is the main entry point for getting thumbnails. The helper handles
* all URL construction, cache checking, and provides ready-to-use URLs.
*
* @param string $rootPath Root filesystem path (e.g., '/data/images/')
* @param string $localPath Local path relative to root (e.g., 'photo.jpg')
* @param string $pageId Page ID for ACL check
* @param int $width Desired width
* @param int $height Desired height
* @param int $quality JPEG quality (0-100)
* @param string|null $placeholderId Optional MediaManager ID for custom placeholder
* @return array [
* 'url' => string, // Always usable URL (thumbnail or placeholder)
* 'isFinal' => bool, // true if thumbnail ready, false if placeholder
* 'thumbUrl' => string // Real thumbnail URL (for lazy loading)
* ]
*/
public static function getThumbnail(
string $rootPath,
string $localPath,
string $pageId,
int $width,
int $height,
int $quality = 80,
?string $placeholderId = null
): array {
$fullPath = $rootPath . $localPath;
$thumbUrl = self::buildThumbnailUrl($rootPath, $localPath, $pageId, $width, $height, $quality);
// Check if cached
$cachePath = self::getCachePath($fullPath, $width, $height, $quality);
$isCached = $cachePath !== null && @is_file($cachePath);
if ($isCached) {
return [
'url' => $thumbUrl,
'isFinal' => true,
'thumbUrl' => $thumbUrl,
];
}
// Not cached: return placeholder
return [
'url' => self::getPlaceholderUrl($width, $height, $placeholderId),
'isFinal' => false,
'thumbUrl' => $thumbUrl,
];
}
/**
* Build the file.php URL for a thumbnail.
*
* @param string $rootPath Root filesystem path
* @param string $localPath Local path relative to root
* @param string $pageId Page ID for ACL check
* @param int $width Width
* @param int $height Height
* @param int $quality JPEG quality
* @return string Complete URL to file.php with thumbnail parameters
*/
protected static function buildThumbnailUrl(
string $rootPath,
string $localPath,
string $pageId,
int $width,
int $height,
int $quality
): string {
$params = [
'root' => $rootPath,
'file' => $localPath,
'id' => $pageId,
'thumb' => 1,
'w' => $width,
'h' => $height,
'q' => $quality,
];
return DOKU_BASE . 'lib/plugins/luxtools/file.php?' . http_build_query($params, '', '&');
}
/**
* Compute the expected thumbnail cache path.
*
* Mirrors the hashing scheme in file.php so we can detect whether a thumb
* already exists and can be used immediately.
*
* @param string $path Full filesystem path to the image
* @param int $w Width
* @param int $h Height
* @param int $q Quality (JPEG)
* @return string|null Path to cached thumbnail, or null if unavailable
*/
public static function getCachePath(string $path, int $w, int $h, int $q = 80): ?string
{
if ($path === '' || !is_file($path)) return null;
$mtime = @filemtime($path);
if ($mtime === false) return null;
// Decide output format the same way file.php does
try {
[, $mime,] = mimetype($path, false);
} catch (\Throwable $e) {
return null;
}
if (!is_string($mime) || !str_starts_with($mime, 'image/')) return null;
$dstFormat = ($mime === 'image/png' || $mime === 'image/gif') ? 'png' : 'jpg';
global $conf;
if (!isset($conf['cachedir']) || !is_string($conf['cachedir']) || trim($conf['cachedir']) === '') return null;
$hash = sha1($path . '|' . $mtime . '|w=' . $w . '|h=' . $h . '|q=' . $q . '|f=' . $dstFormat);
$sub = substr($hash, 0, 2);
$cacheDir = rtrim($conf['cachedir'], '/');
return $cacheDir . '/luxtools/thumbs/' . $sub . '/' . $hash . '.' . $dstFormat;
}
/**
* Get placeholder image URL.
*
* @param int $width Desired width
* @param int $height Desired height
* @param string|null $placeholderId Optional MediaManager ID for custom placeholder
* @return string Placeholder URL
*/
public static function getPlaceholderUrl(int $width, int $height, ?string $placeholderId = null): string
{
$placeholderUrl = DOKU_BASE . 'lib/images/blank.gif';
if ($placeholderId !== null && $placeholderId !== '' && function_exists('ml')) {
$placeholderUrl = ml($placeholderId, ['w' => $width, 'h' => $height], true, '&');
}
return $placeholderUrl;
}
/**
* Determine the initial image source and data attributes for lazy thumbnail loading.
*
* Returns placeholder info if thumbnail needs to be loaded, or the actual
* thumbnail URL if it's already cached.
*
* @param string $imagePath Full filesystem path to the image
* @param string $thumbUrl URL to the thumbnail
* @param int $width Width
* @param int $height Height
* @param int $quality Quality (JPEG)
* @param string|null $placeholderId Optional MediaManager ID for custom placeholder
* @return array ['src' => string, 'dataThumbAttr' => string]
*/
public static function getDisplayInfo(
string $imagePath,
string $thumbUrl,
int $width,
int $height,
int $quality = 80,
?string $placeholderId = null
): array {
$thumbCachePath = self::getCachePath($imagePath, $width, $height, $quality);
if ($thumbCachePath !== null && @is_file($thumbCachePath)) {
// Thumbnail exists: display it immediately
return [
'src' => $thumbUrl,
'dataThumbAttr' => '',
];
}
// Thumbnail doesn't exist: show placeholder and lazy-load
$placeholderUrl = self::getPlaceholderUrl($width, $height, $placeholderId);
return [
'src' => $placeholderUrl,
'dataThumbAttr' => ' data-thumb-src="' . hsc($thumbUrl) . '"',
];
}
}