xref: /template/strap/ComboStrap/PermalinkTag.php (revision 1089b8539119eb5c090150ef076fcc91756cfb97)
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