From 7a4ce8609cac432a6d64c57712566e4bdaedee68 Mon Sep 17 00:00:00 2001 From: luxick Date: Mon, 26 Jan 2026 09:38:52 +0100 Subject: [PATCH] Allow hotlinking images --- README.md | 25 ++++++++++++------ file.php | 2 +- src/ThumbnailHelper.php | 16 ++++++++++++ syntax/image.php | 58 ++++++++++++++++++++++++----------------- 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 1b83092..130bf0c 100644 --- a/README.md +++ b/README.md @@ -220,20 +220,29 @@ Clicking a thumbnail opens a lightbox viewer. Thumbnails are generated and cache ``` {{image>/Scape/photos/picture.jpg|This is the caption}} -{{image>/Scape/photos/picture.jpg?400|Resized to 400px width}} -{{image>/Scape/photos/picture.jpg?400x300|Fixed dimensions}} -{{image>/Scape/photos/picture.jpg?left|Float left}} -{{image>/Scape/photos/picture.jpg?400¢er|Resized and centered}} +{{image>/Scape/photos/picture.jpg|Caption|400}} +{{image>/Scape/photos/picture.jpg|Caption|400x300}} +{{image>/Scape/photos/picture.jpg|Caption|left}} +{{image>/Scape/photos/picture.jpg|Caption|400¢er}} +{{image>https://example.com/images/picture.jpg|Remote image caption}} +{{image>https://example.com/images/picture.jpg|Remote caption|400x300&left}} ``` -Renders a Wikipedia-style image box with optional caption. Parameters after `?` are separated by `&`: +Renders a Wikipedia-style image box with optional caption. The syntax uses pipe-separated parts: -- Size: `?200` (width) or `?200x150` (width × height) -- Alignment: `?left`, `?right` (default), or `?center` -- Combined: `?400&left` or `?400x300¢er` +- `{{image>path|caption}}` – Image with caption (uses defaults) +- `{{image>path|caption|options}}` – Image with caption and options + +Options (in the third part, separated by `&`): + +- Size: `200` (width) or `200x150` (width × height) +- Alignment: `left`, `right` (default), or `center` +- Combined: `400&left` or `400x300¢er` The image links to the full-size version when clicked. +Remote images (HTTP/HTTPS URLs) are linked directly without proxying or thumbnailing. + ### 5) Open a local path/folder (best-effort) ``` diff --git a/file.php b/file.php index 1819d9e..e253416 100644 --- a/file.php +++ b/file.php @@ -224,7 +224,7 @@ try { echo 'Path not readable: ' . $pathInfo['path']; exit; } - [$ext, $mime, $download] = mimetype($pathInfo['path'], false); + [, $mime, $download] = mimetype($pathInfo['path'], false); // Optional thumbnail mode: ?thumb=1&w=150&h=150 $thumb = (int)$INPUT->int('thumb'); diff --git a/src/ThumbnailHelper.php b/src/ThumbnailHelper.php index b3a8567..35d5076 100644 --- a/src/ThumbnailHelper.php +++ b/src/ThumbnailHelper.php @@ -9,6 +9,22 @@ namespace dokuwiki\plugin\luxtools; */ class ThumbnailHelper { + /** + * Check if a string is a HTTP/HTTPS URL. + * + * @param string $url + * @return bool + */ + public static function isRemoteUrl(string $url): bool + { + if ($url === '') return false; + $parts = @parse_url($url); + if (!is_array($parts)) return false; + if (empty($parts['scheme']) || empty($parts['host'])) return false; + $scheme = strtolower((string)$parts['scheme']); + return in_array($scheme, ['http', 'https'], true); + } + /** * Get thumbnail URL and metadata for rendering. * diff --git a/syntax/image.php b/syntax/image.php index 793daf8..849cdb9 100644 --- a/syntax/image.php +++ b/syntax/image.php @@ -45,34 +45,26 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin // Remove the leading {{image> and trailing }} $match = substr($match, strlen('{{image>'), -2); - // Split path and caption by | - $parts = explode('|', $match, 2); + // Split by | into: path, caption, options + // Format: {{image>path|caption|options}} + $parts = explode('|', $match, 3); $pathPart = trim($parts[0]); $caption = isset($parts[1]) ? trim($parts[1]) : ''; + $optionStr = isset($parts[2]) ? trim($parts[2]) : ''; - // Parse optional parameters from path (e.g., /path/image.jpg?200x150&right) - // Supported formats: - // ?200 - width only - // ?200x150 - width and height - // ?left - alignment only (left, right, center) - // ?200&right - width and alignment - // ?200x150¢er - full options + // Parse options from third part (e.g., "200x150&right") $width = null; $height = null; - $align = null; // Will use default if not specified + $align = null; - if (strpos($pathPart, '?') !== false) { - [$pathPart, $paramStr] = explode('?', $pathPart, 2); - $paramParts = explode('&', $paramStr); - - foreach ($paramParts as $param) { + if ($optionStr !== '') { + $optionParts = explode('&', $optionStr); + foreach ($optionParts as $param) { $param = trim($param); if ($param === '') continue; - // Check if it's an alignment keyword if (in_array($param, ['left', 'right', 'center'], true)) { $align = $param; - // Check if it's a size specification (digits, optionally with 'x' and more digits) } elseif (preg_match('/^(\d+)(?:x(\d+))?$/', $param, $m)) { $width = (int)$m[1]; if (isset($m[2]) && $m[2] !== '') { @@ -82,10 +74,12 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin } } - $path = Path::cleanPath($pathPart, false); + $isRemote = ThumbnailHelper::isRemoteUrl($pathPart); + $path = $isRemote ? $pathPart : Path::cleanPath($pathPart, false); return [ 'path' => $path, + 'is_remote' => $isRemote, 'caption' => $caption, 'align' => $align, 'width' => $width, @@ -120,6 +114,23 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin } } + if (!empty($data['is_remote'])) { + if (empty($data['path']) || !ThumbnailHelper::isRemoteUrl($data['path'])) { + $renderer->cdata('[n/a: Invalid URL]'); + return true; + } + + // Remote images: link directly, no proxying or thumbnailing + $thumb = [ + 'url' => $data['path'], + 'isFinal' => true, + 'thumbUrl' => $data['path'], + ]; + + $this->renderImageBox($renderer, $thumb, $data['path'], $data); + return true; + } + try { $pathHelper = new Path($this->getConf('paths')); // Use addTrailingSlash=false since this is a file path, not a directory @@ -162,7 +173,7 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin ); // Build full-size URL for linking - $fullUrl = $this->buildFileUrl($pathInfo, null, null, false); + $fullUrl = $this->buildImageUrl($pathInfo, null, null, false); $this->renderImageBox($renderer, $thumb, $fullUrl, $data); @@ -170,15 +181,15 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin } /** - * Build the file.php URL for the image. + * Build the file.php URL for a local image. * - * @param array $pathInfo Path info from Path helper + * @param array $pathInfo Path info array from Path helper * @param int|null $width Optional width * @param int|null $height Optional height * @param bool $thumbnail Whether to generate a thumbnail * @return string */ - protected function buildFileUrl(array $pathInfo, ?int $width, ?int $height, bool $thumbnail = false): string + protected function buildImageUrl(array $pathInfo, ?int $width, ?int $height, bool $thumbnail): string { global $ID; @@ -189,9 +200,8 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin ]; if ($thumbnail && ($width !== null || $height !== null)) { - // Enable thumbnail mode (same as gallery logic) $params['thumb'] = 1; - $params['q'] = 80; // JPEG quality + $params['q'] = 80; } if ($width !== null) {