1*36b712d8SJaap de Haan<?php 2*36b712d8SJaap de Haan 3*36b712d8SJaap de Haanclass plugin_bpmnio_link_processor 4*36b712d8SJaap de Haan{ 5*36b712d8SJaap de Haan /** 6*36b712d8SJaap de Haan * @return array{xml: string, links: array<string, array{href: string, target: string}>} 7*36b712d8SJaap de Haan */ 8*36b712d8SJaap de Haan public static function buildPayload(string $xml): array 9*36b712d8SJaap de Haan { 10*36b712d8SJaap de Haan if (trim($xml) === '') { 11*36b712d8SJaap de Haan return ['xml' => $xml, 'links' => []]; 12*36b712d8SJaap de Haan } 13*36b712d8SJaap de Haan 14*36b712d8SJaap de Haan $document = new DOMDocument('1.0', 'UTF-8'); 15*36b712d8SJaap de Haan $document->preserveWhiteSpace = true; 16*36b712d8SJaap de Haan $document->formatOutput = false; 17*36b712d8SJaap de Haan 18*36b712d8SJaap de Haan $previous = libxml_use_internal_errors(true); 19*36b712d8SJaap de Haan $loaded = $document->loadXML($xml); 20*36b712d8SJaap de Haan libxml_clear_errors(); 21*36b712d8SJaap de Haan libxml_use_internal_errors($previous); 22*36b712d8SJaap de Haan 23*36b712d8SJaap de Haan if (!$loaded) { 24*36b712d8SJaap de Haan return ['xml' => $xml, 'links' => []]; 25*36b712d8SJaap de Haan } 26*36b712d8SJaap de Haan 27*36b712d8SJaap de Haan $links = []; 28*36b712d8SJaap de Haan foreach ($document->getElementsByTagName('*') as $element) { 29*36b712d8SJaap de Haan if (!$element->hasAttribute('id') || !$element->hasAttribute('name')) { 30*36b712d8SJaap de Haan continue; 31*36b712d8SJaap de Haan } 32*36b712d8SJaap de Haan 33*36b712d8SJaap de Haan $parsedLink = self::parseLinkMarkup($element->getAttribute('name')); 34*36b712d8SJaap de Haan if ($parsedLink === null) { 35*36b712d8SJaap de Haan continue; 36*36b712d8SJaap de Haan } 37*36b712d8SJaap de Haan 38*36b712d8SJaap de Haan $target = self::resolveTarget($parsedLink['target']); 39*36b712d8SJaap de Haan if ($target === null || auth_quickaclcheck($target) < AUTH_READ) { 40*36b712d8SJaap de Haan continue; 41*36b712d8SJaap de Haan } 42*36b712d8SJaap de Haan 43*36b712d8SJaap de Haan $elementId = trim($element->getAttribute('id')); 44*36b712d8SJaap de Haan if ($elementId === '') { 45*36b712d8SJaap de Haan continue; 46*36b712d8SJaap de Haan } 47*36b712d8SJaap de Haan 48*36b712d8SJaap de Haan $label = $parsedLink['label'] !== '' ? $parsedLink['label'] : $target; 49*36b712d8SJaap de Haan 50*36b712d8SJaap de Haan $element->setAttribute('name', $label); 51*36b712d8SJaap de Haan $links[$elementId] = [ 52*36b712d8SJaap de Haan 'href' => self::buildHref($target), 53*36b712d8SJaap de Haan 'target' => $target, 54*36b712d8SJaap de Haan ]; 55*36b712d8SJaap de Haan } 56*36b712d8SJaap de Haan 57*36b712d8SJaap de Haan $renderXml = $document->saveXML(); 58*36b712d8SJaap de Haan if ($renderXml === false) { 59*36b712d8SJaap de Haan $renderXml = $xml; 60*36b712d8SJaap de Haan } 61*36b712d8SJaap de Haan 62*36b712d8SJaap de Haan return ['xml' => $renderXml, 'links' => $links]; 63*36b712d8SJaap de Haan } 64*36b712d8SJaap de Haan 65*36b712d8SJaap de Haan /** 66*36b712d8SJaap de Haan * @return array{target: string, label: string}|null 67*36b712d8SJaap de Haan */ 68*36b712d8SJaap de Haan private static function parseLinkMarkup(string $value): ?array 69*36b712d8SJaap de Haan { 70*36b712d8SJaap de Haan $value = trim($value); 71*36b712d8SJaap de Haan if (!preg_match('/^\[\[([^\]|]+)(?:\|([^\]]*))?\]\]$/', $value, $matches)) { 72*36b712d8SJaap de Haan return null; 73*36b712d8SJaap de Haan } 74*36b712d8SJaap de Haan 75*36b712d8SJaap de Haan $target = trim($matches[1]); 76*36b712d8SJaap de Haan if ($target === '') { 77*36b712d8SJaap de Haan return null; 78*36b712d8SJaap de Haan } 79*36b712d8SJaap de Haan 80*36b712d8SJaap de Haan return [ 81*36b712d8SJaap de Haan 'target' => $target, 82*36b712d8SJaap de Haan 'label' => isset($matches[2]) ? trim($matches[2]) : '', 83*36b712d8SJaap de Haan ]; 84*36b712d8SJaap de Haan } 85*36b712d8SJaap de Haan 86*36b712d8SJaap de Haan private static function resolveTarget(string $target): ?string 87*36b712d8SJaap de Haan { 88*36b712d8SJaap de Haan global $ID; 89*36b712d8SJaap de Haan 90*36b712d8SJaap de Haan $target = trim($target); 91*36b712d8SJaap de Haan if ($target === '' || preg_match('#^[a-z][a-z0-9+.-]*://#i', $target)) { 92*36b712d8SJaap de Haan return null; 93*36b712d8SJaap de Haan } 94*36b712d8SJaap de Haan 95*36b712d8SJaap de Haan if (str_starts_with($target, ':')) { 96*36b712d8SJaap de Haan return self::normalizeId(trim($target, ':')); 97*36b712d8SJaap de Haan } 98*36b712d8SJaap de Haan 99*36b712d8SJaap de Haan $baseNamespace = self::getNamespace((string) $ID); 100*36b712d8SJaap de Haan if (str_starts_with($target, '.')) { 101*36b712d8SJaap de Haan $segments = $baseNamespace === '' ? [] : explode(':', $baseNamespace); 102*36b712d8SJaap de Haan foreach (explode(':', $target) as $segment) { 103*36b712d8SJaap de Haan $segment = trim($segment); 104*36b712d8SJaap de Haan if ($segment === '' || $segment === '.') { 105*36b712d8SJaap de Haan continue; 106*36b712d8SJaap de Haan } 107*36b712d8SJaap de Haan if ($segment === '..') { 108*36b712d8SJaap de Haan array_pop($segments); 109*36b712d8SJaap de Haan continue; 110*36b712d8SJaap de Haan } 111*36b712d8SJaap de Haan $segments[] = $segment; 112*36b712d8SJaap de Haan } 113*36b712d8SJaap de Haan 114*36b712d8SJaap de Haan return self::normalizeId(implode(':', $segments)); 115*36b712d8SJaap de Haan } 116*36b712d8SJaap de Haan 117*36b712d8SJaap de Haan if (str_contains($target, ':')) { 118*36b712d8SJaap de Haan return self::normalizeId($target); 119*36b712d8SJaap de Haan } 120*36b712d8SJaap de Haan 121*36b712d8SJaap de Haan $pageId = $baseNamespace === '' ? $target : $baseNamespace . ':' . $target; 122*36b712d8SJaap de Haan return self::normalizeId($pageId); 123*36b712d8SJaap de Haan } 124*36b712d8SJaap de Haan 125*36b712d8SJaap de Haan private static function buildHref(string $target): string 126*36b712d8SJaap de Haan { 127*36b712d8SJaap de Haan return DOKU_BASE . 'doku.php?' . http_build_query(['id' => $target], '', '&', PHP_QUERY_RFC3986); 128*36b712d8SJaap de Haan } 129*36b712d8SJaap de Haan 130*36b712d8SJaap de Haan private static function getNamespace(string $pageId): string 131*36b712d8SJaap de Haan { 132*36b712d8SJaap de Haan $pos = strrpos($pageId, ':'); 133*36b712d8SJaap de Haan if ($pos === false) { 134*36b712d8SJaap de Haan return ''; 135*36b712d8SJaap de Haan } 136*36b712d8SJaap de Haan 137*36b712d8SJaap de Haan return substr($pageId, 0, $pos); 138*36b712d8SJaap de Haan } 139*36b712d8SJaap de Haan 140*36b712d8SJaap de Haan private static function normalizeId(string $value): ?string 141*36b712d8SJaap de Haan { 142*36b712d8SJaap de Haan $value = trim($value); 143*36b712d8SJaap de Haan if ($value === '') { 144*36b712d8SJaap de Haan return null; 145*36b712d8SJaap de Haan } 146*36b712d8SJaap de Haan 147*36b712d8SJaap de Haan if (function_exists('cleanID')) { 148*36b712d8SJaap de Haan $value = cleanID($value); 149*36b712d8SJaap de Haan } 150*36b712d8SJaap de Haan 151*36b712d8SJaap de Haan $segments = []; 152*36b712d8SJaap de Haan foreach (explode(':', $value) as $segment) { 153*36b712d8SJaap de Haan $segment = trim($segment); 154*36b712d8SJaap de Haan if ($segment === '' || $segment === '.') { 155*36b712d8SJaap de Haan continue; 156*36b712d8SJaap de Haan } 157*36b712d8SJaap de Haan if ($segment === '..') { 158*36b712d8SJaap de Haan array_pop($segments); 159*36b712d8SJaap de Haan continue; 160*36b712d8SJaap de Haan } 161*36b712d8SJaap de Haan $segments[] = $segment; 162*36b712d8SJaap de Haan } 163*36b712d8SJaap de Haan 164*36b712d8SJaap de Haan if ($segments === []) { 165*36b712d8SJaap de Haan return null; 166*36b712d8SJaap de Haan } 167*36b712d8SJaap de Haan 168*36b712d8SJaap de Haan return implode(':', $segments); 169*36b712d8SJaap de Haan } 170*36b712d8SJaap de Haan} 171