104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeaunamespace ComboStrap; 404fd306cSNickeau 504fd306cSNickeau 604fd306cSNickeauuse ComboStrap\Web\UrlEndpoint; 704fd306cSNickeauuse Doku_Handler; 804fd306cSNickeauuse syntax_plugin_combo_link; 904fd306cSNickeau 1004fd306cSNickeau 1104fd306cSNickeauclass PermalinkTag 1204fd306cSNickeau{ 1304fd306cSNickeau 1404fd306cSNickeau 1504fd306cSNickeau public const GENERATED_TYPE = "generated"; 1604fd306cSNickeau public const CANONICAL = PermalinkTag::TAG; 1704fd306cSNickeau public const NAMED_TYPE = "named"; 1804fd306cSNickeau public const TAG = "permalink"; 1904fd306cSNickeau public const FRAGMENT_ATTRIBUTE = "fragment"; 2004fd306cSNickeau 2104fd306cSNickeau public static function handleEnterSpecial(TagAttributes $attributes, int $state, Doku_Handler $handler): array 2204fd306cSNickeau { 2304fd306cSNickeau 2404fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 2504fd306cSNickeau $type = $attributes->getValueAndRemoveIfPresent(TagAttributes::TYPE_KEY); 2604fd306cSNickeau if ($type == null) { 2704fd306cSNickeau $type = self::GENERATED_TYPE; 2804fd306cSNickeau } else { 2904fd306cSNickeau $type = strtolower($type); 3004fd306cSNickeau } 3104fd306cSNickeau 3204fd306cSNickeau $strict = $attributes->getBooleanValueAndRemoveIfPresent(TagAttributes::STRICT, true); 3304fd306cSNickeau 3404fd306cSNickeau /** 3504fd306cSNickeau * Cache key dependencies 3604fd306cSNickeau */ 3704fd306cSNickeau try { 3804fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 3904fd306cSNickeau ->getExecutingMarkupHandler() 4004fd306cSNickeau ->getOutputCacheDependencies() 4104fd306cSNickeau ->addDependency(MarkupCacheDependencies::REQUESTED_PAGE_DEPENDENCY); 4204fd306cSNickeau } catch (ExceptionNotFound $e) { 4304fd306cSNickeau // not a fetcher markup run 4404fd306cSNickeau } 4504fd306cSNickeau 4604fd306cSNickeau try { 4704fd306cSNickeau $requestedPage = MarkupPath::createFromRequestedPage(); 4804fd306cSNickeau } catch (ExceptionNotFound $e) { 4904fd306cSNickeau return self::handleError( 5004fd306cSNickeau "No requested page was found", 5104fd306cSNickeau $strict, 5204fd306cSNickeau $callStack 5304fd306cSNickeau ); 5404fd306cSNickeau } 5504fd306cSNickeau $fragment = $attributes->getValueAndRemoveIfPresent(self::FRAGMENT_ATTRIBUTE); 5604fd306cSNickeau switch ($type) { 5704fd306cSNickeau case self::GENERATED_TYPE: 58*1089b853Sgerardnico 59*1089b853Sgerardnico 6004fd306cSNickeau try { 61*1089b853Sgerardnico $permanentValue = self::getPermalinkId($requestedPage); 6204fd306cSNickeau } catch (ExceptionNotFound $e) { 6304fd306cSNickeau return self::handleError( 6404fd306cSNickeau "The page id has not yet been set", 6504fd306cSNickeau $strict, 6604fd306cSNickeau $callStack 6704fd306cSNickeau ); 6804fd306cSNickeau } 6904fd306cSNickeau 7004fd306cSNickeau $url = UrlEndpoint::createBaseUrl() 7104fd306cSNickeau ->setPath("/$permanentValue") 7204fd306cSNickeau ->toAbsoluteUrl(); 7304fd306cSNickeau 7404fd306cSNickeau /** @noinspection DuplicatedCode */ 7504fd306cSNickeau if ($fragment !== null) { 7604fd306cSNickeau $fragment = OutlineSection::textToHtmlSectionId($fragment); 7704fd306cSNickeau $url->setFragment($fragment); 7804fd306cSNickeau } 7904fd306cSNickeau $attributes->addComponentAttributeValue(syntax_plugin_combo_link::MARKUP_REF_ATTRIBUTE, $url); 8004fd306cSNickeau $attributes->addOutputAttributeValue("rel", "nofollow"); 8104fd306cSNickeau syntax_plugin_combo_link::addOpenLinkTagInCallStack($callStack, $attributes); 8204fd306cSNickeau if ($state === DOKU_LEXER_SPECIAL) { 8304fd306cSNickeau self::addLinkContentInCallStack($callStack, $url); 8404fd306cSNickeau self::closeLinkInCallStack($callStack); 8504fd306cSNickeau } 8604fd306cSNickeau return []; 8704fd306cSNickeau case self::NAMED_TYPE: 8804fd306cSNickeau try { 8904fd306cSNickeau $requestedPage->getCanonical(); 9004fd306cSNickeau } catch (ExceptionNotFound $e) { 9104fd306cSNickeau $withIcon = false; // no icon in handle, error should be send to renderer 9204fd306cSNickeau $documentationUrlForCanonical = PluginUtility::getDocumentationHyperLink(Canonical::PROPERTY_NAME, "canonical value", $withIcon); 9304fd306cSNickeau $errorMessage = "The page ($requestedPage) does not have a $documentationUrlForCanonical. We can't create a named permalink"; 9404fd306cSNickeau return self::handleError($errorMessage, $strict, $callStack); 9504fd306cSNickeau } 9604fd306cSNickeau 9704fd306cSNickeau $urlPath = PageUrlPath::createForPage($requestedPage) 9804fd306cSNickeau ->getUrlPathFromType(PageUrlType::CONF_VALUE_CANONICAL_PATH); 9904fd306cSNickeau $urlId = WikiPath::removeRootSepIfPresent($urlPath); // delete the root sep (ie :) 10004fd306cSNickeau $canonicalUrl = UrlEndpoint::createDokuUrl() 10104fd306cSNickeau ->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $urlId) 10204fd306cSNickeau ->toAbsoluteUrl(); 10304fd306cSNickeau /** @noinspection DuplicatedCode */ 10404fd306cSNickeau if ($fragment !== null) { 10504fd306cSNickeau $fragment = OutlineSection::textToHtmlSectionId($fragment); 10604fd306cSNickeau $canonicalUrl->setFragment($fragment); 10704fd306cSNickeau } 10804fd306cSNickeau $attributes->addComponentAttributeValue(syntax_plugin_combo_link::MARKUP_REF_ATTRIBUTE, $canonicalUrl); 10904fd306cSNickeau $attributes->addOutputAttributeValue("rel", "nofollow"); 11004fd306cSNickeau syntax_plugin_combo_link::addOpenLinkTagInCallStack($callStack, $attributes); 11104fd306cSNickeau if ($state === DOKU_LEXER_SPECIAL) { 11204fd306cSNickeau self::addLinkContentInCallStack($callStack, $canonicalUrl); 11304fd306cSNickeau self::closeLinkInCallStack($callStack); 11404fd306cSNickeau } 11504fd306cSNickeau return []; 11604fd306cSNickeau default: 11704fd306cSNickeau return self::handleError( 11804fd306cSNickeau "The permalink type ({$attributes->getType()} is unknown.", 11904fd306cSNickeau $strict, 12004fd306cSNickeau $callStack 12104fd306cSNickeau ); 12204fd306cSNickeau 12304fd306cSNickeau } 12404fd306cSNickeau } 12504fd306cSNickeau 12604fd306cSNickeau public static function handleError(string $errorMessage, bool $strict, CallStack $callStack): array 12704fd306cSNickeau { 12804fd306cSNickeau 12904fd306cSNickeau $returnArray = []; 13004fd306cSNickeau if ($strict) { 13104fd306cSNickeau $returnArray[PluginUtility::EXIT_MESSAGE] = $errorMessage; 13204fd306cSNickeau } 13304fd306cSNickeau $returnArray[PluginUtility::EXIT_CODE] = 1; 13404fd306cSNickeau 13504fd306cSNickeau /** 13604fd306cSNickeau * If this is a button, we cache it 13704fd306cSNickeau */ 13804fd306cSNickeau $parent = $callStack->moveToParent(); 13904fd306cSNickeau if ($parent !== false && $parent->getTagName() === ButtonTag::MARKUP_LONG) { 14004fd306cSNickeau $parent->addAttribute(Display::DISPLAY, Display::DISPLAY_NONE_VALUE); 14104fd306cSNickeau } 14204fd306cSNickeau 14304fd306cSNickeau return $returnArray; 14404fd306cSNickeau } 14504fd306cSNickeau 14604fd306cSNickeau public static function getKnownTypes(): array 14704fd306cSNickeau { 14804fd306cSNickeau return [PermalinkTag::NAMED_TYPE, PermalinkTag::GENERATED_TYPE]; 14904fd306cSNickeau } 15004fd306cSNickeau 15104fd306cSNickeau public static function addLinkContentInCallStack(CallStack $callStack, string $url) 15204fd306cSNickeau { 15304fd306cSNickeau $callStack->appendCallAtTheEnd( 15404fd306cSNickeau Call::createComboCall( 15504fd306cSNickeau syntax_plugin_combo_link::TAG, 15604fd306cSNickeau DOKU_LEXER_UNMATCHED, 15704fd306cSNickeau [], 15804fd306cSNickeau null, 15904fd306cSNickeau null, 16004fd306cSNickeau $url 16104fd306cSNickeau )); 16204fd306cSNickeau } 16304fd306cSNickeau 16404fd306cSNickeau public static function handeExit(Doku_Handler $handler) 16504fd306cSNickeau { 16604fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 16704fd306cSNickeau $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); 16804fd306cSNickeau if ($openingCall->getExitCode() === 0) { 16904fd306cSNickeau // no error 17004fd306cSNickeau self::closeLinkInCallStack($callStack); 17104fd306cSNickeau } 17204fd306cSNickeau } 17304fd306cSNickeau 17404fd306cSNickeau public static function closeLinkInCallStack(CallStack $callStack) 17504fd306cSNickeau { 17604fd306cSNickeau $callStack->appendCallAtTheEnd( 17704fd306cSNickeau Call::createComboCall( 17804fd306cSNickeau syntax_plugin_combo_link::TAG, 17904fd306cSNickeau DOKU_LEXER_EXIT 18004fd306cSNickeau )); 18104fd306cSNickeau } 18204fd306cSNickeau 18304fd306cSNickeau public static function renderEnterSpecialXhtml(array $data): string 18404fd306cSNickeau { 18570bbd7f1Sgerardnico $errorMessage = $data[PluginUtility::EXIT_MESSAGE] ?? null; 18604fd306cSNickeau if (!empty($errorMessage)) { 18704fd306cSNickeau LogUtility::warning($errorMessage, PermalinkTag::CANONICAL); 18804fd306cSNickeau return "<span class=\"text-warning\">{$errorMessage}</span>"; 18904fd306cSNickeau } 19004fd306cSNickeau return ""; 19104fd306cSNickeau } 19204fd306cSNickeau 193*1089b853Sgerardnico /** 194*1089b853Sgerardnico * @throws ExceptionNotFound 195*1089b853Sgerardnico */ 196*1089b853Sgerardnico public static function getPermalinkId(MarkupPath $requestedPage): string 197*1089b853Sgerardnico { 198*1089b853Sgerardnico 199*1089b853Sgerardnico $pageId = $requestedPage->getPageIdAbbr(); 200*1089b853Sgerardnico return PageUrlPath::encodePageId($pageId); 201*1089b853Sgerardnico } 202*1089b853Sgerardnico} 203