Improved Page lik handling

This commit is contained in:
2026-02-02 22:36:38 +01:00
parent 47a8bfa50a
commit 1b6df4a9e4
8 changed files with 245 additions and 130 deletions

View File

@@ -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 = '<span class="luxtools-pagelink-warning" title="' . hsc((string)$this->getLang('pagelink_multi_warning')) . '" aria-hidden="true">&#9888;</span>';
}
$html = '<span class="luxtools-pagelink-status" data-luxtools-pagelink="1"'
. ' data-uuid="' . hsc($uuid) . '"'
. ' data-copy="' . ($copyable ? '1' : '0') . '"'
. ' data-multiple="' . ($multiple ? '1' : '0') . '"'
. ($title !== '' ? ' title="' . hsc($title) . '"' : '')
. '>'
. hsc($statusText)
. $warning
. '</span>';
$event->data = $html . $event->data;
}
}

View File

@@ -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);
})();

View File

@@ -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";

View File

@@ -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";

View File

@@ -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;

View File

@@ -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 .= '<a href="#" class="luxtools-pagelink-copy" data-luxtools-pagelink-copy="1"'
. ' data-uuid="' . hsc($uuid) . '"'
. '>' . hsc($text) . '</a>';
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.
*

View File

@@ -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 .= '<a href="#" class="luxtools-pagelink-copy" data-luxtools-pagelink-copy="1"'
. ' data-uuid="' . hsc($uuid) . '"'
. '>' . hsc($text) . '</a>';
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 ?? '';
}
/**

View File

@@ -1,6 +1,10 @@
<?php
use dokuwiki\Extension\SyntaxPlugin;
use dokuwiki\plugin\luxtools\PageLink;
use dokuwiki\plugin\luxtools\Path;
require_once(__DIR__ . '/../autoload.php');
/**
* luxtools Plugin: Open local path syntax.
@@ -73,6 +77,41 @@ class syntax_plugin_luxtools_open extends SyntaxPlugin
return true;
}
// Resolve blobs alias to the linked folder (if available)
if ($this->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 .= '<a href="#" class="luxtools-pagelink-copy" data-luxtools-pagelink-copy="1"'
. ' data-uuid="' . hsc($uuid) . '"'
. '>' . hsc($text) . '</a>';
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 ?? '';
}
}