From 47a8bfa50a618f762c802f746a783a0a9534fba6 Mon Sep 17 00:00:00 2001 From: luxick Date: Mon, 2 Feb 2026 21:55:35 +0100 Subject: [PATCH] Unlinking --- js/page-link.js | 62 +++++++++++++++++++++++++++++++++++++++++------- pagelink.php | 51 +++++++++++++++++++++++++++++++++++++++ src/PageLink.php | 55 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 8 deletions(-) diff --git a/js/page-link.js b/js/page-link.js index e4023a6..c3b72f1 100644 --- a/js/page-link.js +++ b/js/page-link.js @@ -41,22 +41,26 @@ return '/'; } - function ensurePageLink() { + function requestPageLink(cmd, params) { var pageId = getPageId(); if (!pageId) return false; var endpoint = getBaseUrl() + 'lib/plugins/luxtools/pagelink.php'; - var params = new window.URLSearchParams(); - params.set('cmd', 'ensure'); - params.set('id', pageId); - params.set('sectok', getSectok()); + var payload = new window.URLSearchParams(); + payload.set('cmd', cmd); + payload.set('id', pageId); + if (params && typeof params === 'object') { + Object.keys(params).forEach(function (key) { + payload.set(key, String(params[key])); + }); + } window.fetch(endpoint, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: params.toString() + body: payload.toString() }).then(function (res) { return res.json().catch(function () { return null; }).then(function (body) { if (!res.ok || !body || body.ok !== true) { @@ -66,13 +70,25 @@ }); }).catch(function (err) { if (window.console && window.console.warn) { - window.console.warn('PageLink creation failed:', err); + window.console.warn('PageLink request failed:', err); } }); return false; } + function fetchInfo() { + return requestPageLink('info', {}); + } + + function ensurePageLink() { + return requestPageLink('ensure', { sectok: getSectok() }); + } + + function unlinkPageLink() { + return requestPageLink('unlink', { sectok: getSectok() }); + } + function copyToClipboard(text) { if (!text) return; if (window.navigator && window.navigator.clipboard && window.navigator.clipboard.writeText) { @@ -126,7 +142,37 @@ window.addBtnActionLuxtoolsPageLink = function ($btn, props, edid) { $btn.on('click', function () { - ensurePageLink(); + var pageId = getPageId(); + if (!pageId) return false; + + var endpoint = getBaseUrl() + 'lib/plugins/luxtools/pagelink.php'; + var query = endpoint + '?cmd=info&id=' + encodeURIComponent(pageId); + + window.fetch(query, { + method: 'GET', + credentials: 'same-origin' + }).then(function (res) { + return res.json().catch(function () { return null; }).then(function (body) { + if (!res.ok || !body || body.ok !== true) { + throw new Error('request failed'); + } + return body; + }); + }).then(function (info) { + if (info && info.uuid) { + if (window.confirm('Unlink page?')) { + unlinkPageLink(); + } + return; + } + + ensurePageLink(); + }).catch(function (err) { + if (window.console && window.console.warn) { + window.console.warn('PageLink info failed:', err); + } + ensurePageLink(); + }); return false; }); return 'luxtools-pagelink'; diff --git a/pagelink.php b/pagelink.php index 5faa4bf..8eec4b1 100644 --- a/pagelink.php +++ b/pagelink.php @@ -54,6 +54,35 @@ if (auth_quickaclcheck($pageId) < AUTH_EDIT) { luxtools_pagelink_json(403, ['ok' => false, 'error' => 'forbidden']); } +if ($cmd === 'info') { + $depth = (int)$syntax->getConf('pagelink_search_depth'); + if ($depth < 0) $depth = 0; + + $pageLink = new PageLink((string)$syntax->getConf('paths'), $depth); + $uuid = $pageLink->getPageUuid($pageId); + if ($uuid === null) { + luxtools_pagelink_json(200, [ + 'ok' => true, + 'uuid' => null, + 'linked' => false, + 'folder' => null, + 'multiple' => false, + ]); + } + + $info = $pageLink->resolveUuid($uuid); + $folder = $info['folder'] ?? null; + $multiple = !empty($info['multiple']); + + luxtools_pagelink_json(200, [ + 'ok' => true, + 'uuid' => $uuid, + 'linked' => is_string($folder) && $folder !== '', + 'folder' => is_string($folder) ? $folder : null, + 'multiple' => $multiple, + ]); +} + if ($cmd === 'ensure') { if (strtoupper($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { luxtools_pagelink_json(405, ['ok' => false, 'error' => 'method not allowed']); @@ -83,4 +112,26 @@ if ($cmd === 'ensure') { luxtools_pagelink_json(200, ['ok' => true, 'uuid' => $uuid, 'created' => true]); } +if ($cmd === 'unlink') { + if (strtoupper($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { + luxtools_pagelink_json(405, ['ok' => false, 'error' => 'method not allowed']); + } + + if (!checkSecurityToken()) { + luxtools_pagelink_json(403, ['ok' => false, 'error' => 'bad token']); + } + + $depth = (int)$syntax->getConf('pagelink_search_depth'); + if ($depth < 0) $depth = 0; + + $pageLink = new PageLink((string)$syntax->getConf('paths'), $depth); + $result = $pageLink->unlinkPage($pageId); + + luxtools_pagelink_json(200, [ + 'ok' => true, + 'uuid' => $result['uuid'] ?? null, + 'folder' => $result['folder'] ?? null, + ]); +} + luxtools_pagelink_json(400, ['ok' => false, 'error' => 'unknown command']); diff --git a/src/PageLink.php b/src/PageLink.php index 0e6aec2..86676ff 100644 --- a/src/PageLink.php +++ b/src/PageLink.php @@ -87,6 +87,46 @@ class PageLink return (bool)p_set_metadata($pageId, [self::META_KEY => $uuid]); } + /** + * Remove the pagelink UUID from page metadata. + */ + public function removePageUuid(string $pageId): bool + { + if ($pageId === '') return false; + if (!function_exists('p_set_metadata')) return false; + + return (bool)p_set_metadata($pageId, [self::META_KEY => '']); + } + + /** + * Unlink a page: remove UUID, delete linked .pagelink file if present, and clear cache. + * + * @param string $pageId + * @return array{ok: bool, uuid: string|null, folder: string|null} + */ + public function unlinkPage(string $pageId): array + { + $uuid = $this->getPageUuid($pageId); + if ($uuid === null) { + return ['ok' => true, 'uuid' => null, 'folder' => null]; + } + + $linkInfo = $this->resolveUuid($uuid); + $folder = $linkInfo['folder'] ?? null; + + if (is_string($folder) && $folder !== '') { + $file = rtrim($folder, '/\\') . '/.pagelink'; + if (is_file($file) && !is_link($file)) { + @unlink($file); + } + } + + $this->removeCacheEntry($uuid); + $this->removePageUuid($pageId); + + return ['ok' => true, 'uuid' => $uuid, 'folder' => is_string($folder) ? $folder : null]; + } + /** * Resolve a pagelink UUID to a linked folder (if any). * @@ -177,6 +217,21 @@ class PageLink $this->cacheDirty = false; } + /** + * Remove a specific UUID from cache. + */ + public function removeCacheEntry(string $uuid): void + { + $uuid = self::normalizeUuid($uuid); + if ($uuid === null) return; + + $cache = $this->loadCache(); + if (!isset($cache[$uuid])) return; + + unset($cache[$uuid]); + $this->writeCache($cache); + } + /** * Get cache file path for pagelink mappings. */