diff --git a/action.php b/action.php
index 17b85b5..8e5e7a2 100644
--- a/action.php
+++ b/action.php
@@ -3,8 +3,6 @@
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\Event;
use dokuwiki\Extension\EventHandler;
-use dokuwiki\plugin\luxtools\PageLink;
-
require_once(__DIR__ . '/autoload.php');
/**
@@ -21,12 +19,6 @@ class action_plugin_luxtools extends ActionPlugin
$this,
"addScripts",
);
- $controller->register_hook(
- "TPL_CONTENT_DISPLAY",
- "BEFORE",
- $this,
- "addPageLinkStatus",
- );
$controller->register_hook(
"CSS_STYLES_INCLUDED",
"BEFORE",
@@ -137,71 +129,4 @@ class action_plugin_luxtools extends ActionPlugin
"block" => false,
];
}
-
- /**
- * Inject page link status above the page content.
- *
- * @param Event $event
- * @param mixed $param
- * @return void
- */
- public function addPageLinkStatus(Event $event, $param)
- {
- global $ACT, $ID;
-
- if (!is_string($ACT) || $ACT !== 'show') {
- return;
- }
-
- if (!is_string($ID) || $ID === '') {
- return;
- }
-
- $pageId = $ID;
- if (function_exists('cleanID')) {
- $pageId = (string)cleanID($pageId);
- }
- if ($pageId === '') return;
-
- $pathConfig = (string)$this->getConf('paths');
- $depth = (int)$this->getConf('pagelink_search_depth');
- if ($depth < 0) $depth = 0;
-
- $pageLink = new PageLink($pathConfig, $depth);
- $uuid = $pageLink->getPageUuid($pageId);
- if ($uuid === null) return;
-
- $linkInfo = $pageLink->resolveUuid($uuid);
- $folder = $linkInfo['folder'] ?? null;
- $multiple = !empty($linkInfo['multiple']);
-
- $statusText = '';
- $copyable = false;
- $title = '';
- if (is_string($folder) && $folder !== '') {
- $trimmed = rtrim($folder, '/\\');
- $statusText = basename($trimmed);
- $title = $trimmed;
- } else {
- $statusText = (string)$this->getLang('pagelink_unlinked');
- $copyable = true;
- }
-
- $warning = '';
- if ($multiple) {
- $warning = '⚠';
- }
-
- $html = ''
- . hsc($statusText)
- . $warning
- . '';
-
- $event->data = $html . $event->data;
- }
}
diff --git a/js/page-link.js b/js/page-link.js
index c3b72f1..009b4f6 100644
--- a/js/page-link.js
+++ b/js/page-link.js
@@ -77,10 +77,6 @@
return false;
}
- function fetchInfo() {
- return requestPageLink('info', {});
- }
-
function ensurePageLink() {
return requestPageLink('ensure', { sectok: getSectok() });
}
@@ -111,33 +107,24 @@
} catch (e2) {}
}
- function attachStatus() {
- var status = document.querySelector('.luxtools-pagelink-status[data-luxtools-pagelink="1"]');
- if (!status) return;
+ function attachCopyTargets() {
+ var targets = document.querySelectorAll('[data-luxtools-pagelink-copy="1"]');
+ if (!targets || !targets.length) return;
- var pageTitle = document.querySelector('#dokuwiki__content h1')
- || document.querySelector('.pageId')
- || document.querySelector('h1');
-
- if (pageTitle && pageTitle.appendChild) {
- status.classList.add('is-inline');
- pageTitle.appendChild(status);
- }
-
- var copy = String(status.getAttribute('data-copy') || '') === '1';
- if (copy) {
- status.setAttribute('role', 'button');
- status.setAttribute('tabindex', '0');
- status.addEventListener('click', function (e) {
+ targets.forEach(function (el) {
+ if (!el || !el.getAttribute) return;
+ el.setAttribute('role', 'button');
+ el.setAttribute('tabindex', '0');
+ el.addEventListener('click', function (e) {
e.preventDefault();
- copyToClipboard(String(status.getAttribute('data-uuid') || '').trim());
+ copyToClipboard(String(el.getAttribute('data-uuid') || '').trim());
});
- status.addEventListener('keydown', function (e) {
+ el.addEventListener('keydown', function (e) {
if (!e || (e.key !== 'Enter' && e.key !== ' ')) return;
e.preventDefault();
- copyToClipboard(String(status.getAttribute('data-uuid') || '').trim());
+ copyToClipboard(String(el.getAttribute('data-uuid') || '').trim());
});
- }
+ });
}
window.addBtnActionLuxtoolsPageLink = function ($btn, props, edid) {
@@ -178,5 +165,5 @@
return 'luxtools-pagelink';
};
- document.addEventListener('DOMContentLoaded', attachStatus, false);
+ document.addEventListener('DOMContentLoaded', attachCopyTargets, false);
})();
diff --git a/lang/de/lang.php b/lang/de/lang.php
index f63fe0f..5e4f046 100644
--- a/lang/de/lang.php
+++ b/lang/de/lang.php
@@ -78,5 +78,5 @@ $lang["toolbar_code_sample"] = "Ihr Code hier";
$lang["toolbar_datefix_title"] = "Datums-Fix";
$lang["toolbar_datefix_all_title"] = "Datums-Fix (Alle)";
$lang["toolbar_pagelink_title"] = "Seiten-Link";
-$lang["pagelink_unlinked"] = "Nicht verknüpft: ID kopieren";
+$lang["pagelink_unlinked"] = "Seite nicht verknüpft (ID kopieren)";
$lang["pagelink_multi_warning"] = "Mehrere Ordner verknüpft";
diff --git a/lang/en/lang.php b/lang/en/lang.php
index d8f1c3c..bd4f74d 100644
--- a/lang/en/lang.php
+++ b/lang/en/lang.php
@@ -78,5 +78,5 @@ $lang["toolbar_code_sample"] = "your code here";
$lang["toolbar_datefix_title"] = "Date Fix";
$lang["toolbar_datefix_all_title"] = "Date Fix (All)";
$lang["toolbar_pagelink_title"] = "Page Link";
-$lang["pagelink_unlinked"] = "Not linked: Copy ID";
+$lang["pagelink_unlinked"] = "Page not linked (Copy ID)";
$lang["pagelink_multi_warning"] = "Multiple folders linked";
diff --git a/style.css b/style.css
index d8c09d7..1ff1a21 100644
--- a/style.css
+++ b/style.css
@@ -49,36 +49,23 @@ div.luxtools-plugin .luxtools-empty {
padding: 0.25em 0;
}
-/* Page link status (next to page title) */
-span.luxtools-pagelink-status {
+/* Page link copy message (unlinked blobs alias) */
+a.luxtools-pagelink-copy,
+a.luxtools-pagelink-copy:visited {
display: inline-flex;
align-items: center;
- gap: 0.35em;
- font-size: 0.75em;
- line-height: 1.2;
+ font-size: 0.85em;
+ line-height: 1.3;
margin: 0.25em 0;
padding: 0.15em 0.4em;
border: 1px solid @ini_border;
border-radius: 0.2em;
background-color: @ini_background_alt;
color: inherit;
-}
-
-span.luxtools-pagelink-status.is-inline {
- margin-left: 0.5em;
- margin-top: 0;
- margin-bottom: 0;
-}
-
-span.luxtools-pagelink-status[data-copy="1"] {
+ text-decoration: none;
cursor: pointer;
}
-span.luxtools-pagelink-warning {
- font-size: 0.95em;
- opacity: 0.8;
-}
-
/* Image gallery spacing. */
div.luxtools-gallery {
padding-bottom: 0.5em;
diff --git a/syntax/AbstractSyntax.php b/syntax/AbstractSyntax.php
index 66fe8ee..0c9e901 100644
--- a/syntax/AbstractSyntax.php
+++ b/syntax/AbstractSyntax.php
@@ -211,6 +211,10 @@ abstract class syntax_plugin_luxtools_abstract extends SyntaxPlugin
try {
$pathConfig = (string)$this->getConf('paths');
$blobsRoot = $this->resolveBlobsRoot();
+ if ($blobsRoot === '' && $this->isBlobsPath($basePath)) {
+ $this->renderPageNotLinked($renderer);
+ return false;
+ }
if ($blobsRoot !== '') {
$pathConfig = rtrim($pathConfig) . "\n" . $blobsRoot . "\nA> blobs";
}
@@ -222,6 +226,55 @@ abstract class syntax_plugin_luxtools_abstract extends SyntaxPlugin
}
}
+ /**
+ * Check if the given path uses the blobs alias.
+ */
+ protected function isBlobsPath(string $path): bool
+ {
+ $trimmed = ltrim($path, '/');
+ return preg_match('/^blobs(\/|$)/', $trimmed) === 1;
+ }
+
+ /**
+ * Render the "Page not linked" message with copy ID affordance.
+ */
+ protected function renderPageNotLinked(\Doku_Renderer $renderer): void
+ {
+ $uuid = $this->getPageUuidSafe();
+ $text = (string)$this->getLang('pagelink_unlinked');
+
+ if ($renderer instanceof \Doku_Renderer_xhtml) {
+ $renderer->doc .= '' . hsc($text) . '';
+ return;
+ }
+
+ $renderer->cdata('[n/a: ' . $text . ']');
+ }
+
+ /**
+ * Read the current page UUID (if any).
+ */
+ protected function getPageUuidSafe(): string
+ {
+ global $ID;
+ $pageId = is_string($ID) ? $ID : '';
+ if ($pageId === '') return '';
+
+ if (function_exists('cleanID')) {
+ $pageId = (string)cleanID($pageId);
+ }
+ if ($pageId === '') return '';
+
+ $depth = (int)$this->getConf('pagelink_search_depth');
+ if ($depth < 0) $depth = 0;
+
+ $pageLink = new PageLink((string)$this->getConf('paths'), $depth);
+ $uuid = $pageLink->getPageUuid($pageId);
+ return $uuid ?? '';
+ }
+
/**
* Resolve the current page's pagelink folder for the blobs alias.
*
diff --git a/syntax/image.php b/syntax/image.php
index 1992c82..2a960ef 100644
--- a/syntax/image.php
+++ b/syntax/image.php
@@ -133,7 +133,18 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin
}
try {
- $pathHelper = new Path($this->buildPathConfigWithBlobs());
+ $blobsRoot = $this->resolveBlobsRoot();
+ if ($blobsRoot === '' && $this->isBlobsPath($data['path'] ?? '')) {
+ $this->renderPageNotLinked($renderer);
+ return true;
+ }
+
+ $pathConfig = (string)$this->getConf('paths');
+ if ($blobsRoot !== '') {
+ $pathConfig = rtrim($pathConfig) . "\n" . $blobsRoot . "\nA> blobs";
+ }
+
+ $pathHelper = new Path($pathConfig);
// Use addTrailingSlash=false since this is a file path, not a directory
$pathInfo = $pathHelper->getPathInfo($data['path'], false);
} catch (\Exception $e) {
@@ -216,16 +227,52 @@ class syntax_plugin_luxtools_image extends SyntaxPlugin
}
/**
- * Build a path configuration string, adding the blobs alias if available.
+ * Check if the given path uses the blobs alias.
*/
- protected function buildPathConfigWithBlobs(): string
+ protected function isBlobsPath(string $path): bool
{
- $pathConfig = (string)$this->getConf('paths');
- $blobsRoot = $this->resolveBlobsRoot();
- if ($blobsRoot !== '') {
- $pathConfig = rtrim($pathConfig) . "\n" . $blobsRoot . "\nA> blobs";
+ $trimmed = ltrim($path, '/');
+ return preg_match('/^blobs(\/|$)/', $trimmed) === 1;
+ }
+
+ /**
+ * Render the "Page not linked" message with copy ID affordance.
+ */
+ protected function renderPageNotLinked(\Doku_Renderer $renderer): void
+ {
+ $uuid = $this->getPageUuidSafe();
+ $text = (string)$this->getLang('pagelink_unlinked');
+
+ if ($renderer instanceof \Doku_Renderer_xhtml) {
+ $renderer->doc .= '' . hsc($text) . '';
+ return;
}
- return $pathConfig;
+
+ $renderer->cdata('[n/a: ' . $text . ']');
+ }
+
+ /**
+ * Read the current page UUID (if any).
+ */
+ protected function getPageUuidSafe(): string
+ {
+ global $ID;
+ $pageId = is_string($ID) ? $ID : '';
+ if ($pageId === '') return '';
+
+ if (function_exists('cleanID')) {
+ $pageId = (string)cleanID($pageId);
+ }
+ if ($pageId === '') return '';
+
+ $depth = (int)$this->getConf('pagelink_search_depth');
+ if ($depth < 0) $depth = 0;
+
+ $pageLink = new PageLink((string)$this->getConf('paths'), $depth);
+ $uuid = $pageLink->getPageUuid($pageId);
+ return $uuid ?? '';
}
/**
diff --git a/syntax/open.php b/syntax/open.php
index 5d82051..9212c87 100644
--- a/syntax/open.php
+++ b/syntax/open.php
@@ -1,6 +1,10 @@
isBlobsPath($path)) {
+ $blobsRoot = $this->resolveBlobsRoot();
+ if ($blobsRoot === '') {
+ $this->renderPageNotLinked($renderer);
+ return true;
+ }
+
+ try {
+ $pathConfig = (string)$this->getConf('paths');
+ $pathConfig = rtrim($pathConfig) . "\n" . $blobsRoot . "\nA> blobs";
+ $pathHelper = new Path($pathConfig);
+ $resolvedPath = $path;
+ $isBlobsRoot = (rtrim($resolvedPath, '/') === 'blobs');
+ if ($isBlobsRoot) {
+ $resolvedPath = rtrim($resolvedPath, '/') . '/';
+ }
+ $pathInfo = $pathHelper->getPathInfo($resolvedPath, $isBlobsRoot);
+ $path = $pathInfo['path'];
+ } catch (\Exception $e) {
+ $renderer->cdata('[n/a: ' . $this->getLang('error_outsidejail') . ']');
+ return true;
+ }
+ }
+
+ // Map local paths back to their configured aliases before opening.
+ if (!preg_match('/^[a-zA-Z][a-zA-Z0-9+.-]*:/', $path)) {
+ try {
+ $pathHelper = new Path((string)$this->getConf('paths'));
+ $path = $pathHelper->mapToAliasPath($path);
+ } catch (\Exception $e) {
+ // ignore mapping failures
+ }
+ }
+
$serviceUrl = trim((string)$this->getConf('open_service_url'));
$serviceToken = trim((string)$this->getConf('open_service_token'));
@@ -106,4 +145,81 @@ class syntax_plugin_luxtools_open extends SyntaxPlugin
$renderer->doc .= $renderer->_formatLink($link);
return true;
}
+
+ /**
+ * Check if the given path uses the blobs alias.
+ */
+ protected function isBlobsPath(string $path): bool
+ {
+ $trimmed = ltrim($path, '/');
+ return preg_match('/^blobs(\/|$)/', $trimmed) === 1;
+ }
+
+ /**
+ * Resolve the current page's pagelink folder for the blobs alias.
+ */
+ protected function resolveBlobsRoot(): string
+ {
+ global $ID;
+ $pageId = is_string($ID) ? $ID : '';
+ if ($pageId === '') return '';
+
+ if (function_exists('cleanID')) {
+ $pageId = (string)cleanID($pageId);
+ }
+ if ($pageId === '') return '';
+
+ $depth = (int)$this->getConf('pagelink_search_depth');
+ if ($depth < 0) $depth = 0;
+
+ $pageLink = new PageLink((string)$this->getConf('paths'), $depth);
+ $uuid = $pageLink->getPageUuid($pageId);
+ if ($uuid === null) return '';
+
+ $linkInfo = $pageLink->resolveUuid($uuid);
+ $folder = $linkInfo['folder'] ?? '';
+ if (!is_string($folder) || $folder === '') return '';
+
+ return $folder;
+ }
+
+ /**
+ * Render the "Page not linked" message with copy ID affordance.
+ */
+ protected function renderPageNotLinked(\Doku_Renderer $renderer): void
+ {
+ $uuid = $this->getPageUuidSafe();
+ $text = (string)$this->getLang('pagelink_unlinked');
+
+ if ($renderer instanceof \Doku_Renderer_xhtml) {
+ $renderer->doc .= '' . hsc($text) . '';
+ return;
+ }
+
+ $renderer->cdata('[n/a: ' . $text . ']');
+ }
+
+ /**
+ * Read the current page UUID (if any).
+ */
+ protected function getPageUuidSafe(): string
+ {
+ global $ID;
+ $pageId = is_string($ID) ? $ID : '';
+ if ($pageId === '') return '';
+
+ if (function_exists('cleanID')) {
+ $pageId = (string)cleanID($pageId);
+ }
+ if ($pageId === '') return '';
+
+ $depth = (int)$this->getConf('pagelink_search_depth');
+ if ($depth < 0) $depth = 0;
+
+ $pageLink = new PageLink((string)$this->getConf('paths'), $depth);
+ $uuid = $pageLink->getPageUuid($pageId);
+ return $uuid ?? '';
+ }
}