xref: /template/strap/ComboStrap/LinkMarkup.php (revision b90165b7b19dc6a57d25a69f7c99af38875109f4)
104fd306cSNickeau<?php
204fd306cSNickeau/**
304fd306cSNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved.
404fd306cSNickeau *
504fd306cSNickeau * This source code is licensed under the GPL license found in the
604fd306cSNickeau * COPYING  file in the root directory of this source tree.
704fd306cSNickeau *
804fd306cSNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
904fd306cSNickeau * @author   ComboStrap <support@combostrap.com>
1004fd306cSNickeau *
1104fd306cSNickeau */
1204fd306cSNickeau
1304fd306cSNickeaunamespace ComboStrap;
1404fd306cSNickeau
1504fd306cSNickeau
1604fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
1704fd306cSNickeauuse ComboStrap\TagAttribute\StyleAttribute;
1804fd306cSNickeauuse Doku_Renderer_xhtml;
1904fd306cSNickeauuse dokuwiki\Extension\PluginTrait;
2004fd306cSNickeauuse dokuwiki\Utf8\Conversion;
2104fd306cSNickeauuse syntax_plugin_combo_link;
2204fd306cSNickeau
2304fd306cSNickeau
2404fd306cSNickeau/**
2504fd306cSNickeau *
2604fd306cSNickeau * @package ComboStrap
2704fd306cSNickeau *
2804fd306cSNickeau * Parse the ref found in a markup link
2904fd306cSNickeau * and return an {@link LinkMarkup::toAttributes()} array for an anchor (a)
3004fd306cSNickeau * with href, style, ... attributes
3104fd306cSNickeau *
3204fd306cSNickeau */
3304fd306cSNickeauclass LinkMarkup
3404fd306cSNickeau{
3504fd306cSNickeau
3604fd306cSNickeau
3704fd306cSNickeau    /**
3804fd306cSNickeau     * Class added to the type of link
3904fd306cSNickeau     * Class have styling rule conflict, they are by default not set
4004fd306cSNickeau     * but this configuration permits to turn it back
4104fd306cSNickeau     */
4204fd306cSNickeau    const CONF_USE_DOKUWIKI_CLASS_NAME = "useDokuwikiLinkClassName";
4304fd306cSNickeau
4404fd306cSNickeau    /**
4504fd306cSNickeau     * This configuration will set for all internal link
4604fd306cSNickeau     * the {@link LinkMarkup::PREVIEW_ATTRIBUTE} preview attribute
4704fd306cSNickeau     */
4804fd306cSNickeau    const CONF_PREVIEW_LINK = "previewLink";
4904fd306cSNickeau    const CONF_PREVIEW_LINK_DEFAULT = 0;
5004fd306cSNickeau
5104fd306cSNickeau
5204fd306cSNickeau    const TEXT_ERROR_CLASS = "text-danger";
5304fd306cSNickeau
5404fd306cSNickeau    /**
5504fd306cSNickeau     * The known parameters for an email url
5604fd306cSNickeau     * The other are styling attribute :)
5704fd306cSNickeau     */
5804fd306cSNickeau    const EMAIL_VALID_PARAMETERS = ["subject"];
5904fd306cSNickeau
6004fd306cSNickeau    /**
6104fd306cSNickeau     * If set, it will show a page preview
6204fd306cSNickeau     */
6304fd306cSNickeau    const PREVIEW_ATTRIBUTE = "preview";
6404fd306cSNickeau
6504fd306cSNickeau
6604fd306cSNickeau    /**
6704fd306cSNickeau     * Highlight Key
6804fd306cSNickeau     * Adding this property to the internal query will highlight the words
6904fd306cSNickeau     *
7004fd306cSNickeau     * See {@link html_hilight}
7104fd306cSNickeau     */
7204fd306cSNickeau    const SEARCH_HIGHLIGHT_QUERY_PROPERTY = "s";
7304fd306cSNickeau    const DATA_WIKI_ID = "data-wiki-id";
7404fd306cSNickeau
7504fd306cSNickeau    /**
7604fd306cSNickeau     * For styling on the anchor tag (ie a)
7704fd306cSNickeau     */
7846021406Sgerardnico    public const ANCHOR_HTML_SNIPPET_ID = "anchor";
7904fd306cSNickeau
8004fd306cSNickeau    /**
8104fd306cSNickeau     * Url properties
8204fd306cSNickeau     * that are not seen as styling properties
8304fd306cSNickeau     * for a page
8404fd306cSNickeau     * We could also build the {@link FetcherPage}
8504fd306cSNickeau     * and see which attributes were not taken ?
8604fd306cSNickeau     */
8704fd306cSNickeau    const PROTECTED_URL_PROPERTY = [
8804fd306cSNickeau        self::SEARCH_HIGHLIGHT_QUERY_PROPERTY,
8904fd306cSNickeau        DokuWikiId::DOKUWIKI_ID_ATTRIBUTE,
9004fd306cSNickeau        PageTemplateName::PROPERTY_NAME,
9104fd306cSNickeau        FetcherPage::PURGE
9204fd306cSNickeau    ];
9304fd306cSNickeau
9404fd306cSNickeau
9504fd306cSNickeau    private MarkupRef $markupRef;
9604fd306cSNickeau
9704fd306cSNickeau
9804fd306cSNickeau    private TagAttributes $stylingAttributes;
9904fd306cSNickeau
10004fd306cSNickeau    /**
10104fd306cSNickeau     * Link constructor.
10204fd306cSNickeau     * @param String $ref
10304fd306cSNickeau     * @throws ExceptionBadArgument
10404fd306cSNickeau     * @throws ExceptionBadSyntax
10504fd306cSNickeau     * @throws ExceptionNotFound
10604fd306cSNickeau     */
10704fd306cSNickeau    public function __construct(string $ref)
10804fd306cSNickeau    {
10904fd306cSNickeau
11004fd306cSNickeau        $this->stylingAttributes = TagAttributes::createEmpty(syntax_plugin_combo_link::TAG);
11104fd306cSNickeau
11204fd306cSNickeau        $this->markupRef = MarkupRef::createLinkFromRef($ref);
11304fd306cSNickeau
11404fd306cSNickeau        $this->collectStylingAttributeInUrl();
11504fd306cSNickeau
11604fd306cSNickeau
11704fd306cSNickeau    }
11804fd306cSNickeau
11904fd306cSNickeau    public static function createFromPageIdOrPath($id): LinkMarkup
12004fd306cSNickeau    {
12104fd306cSNickeau        WikiPath::addRootSeparatorIfNotPresent($id);
12204fd306cSNickeau        try {
12304fd306cSNickeau            return new LinkMarkup($id);
12404fd306cSNickeau        } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotFound $e) {
12504fd306cSNickeau            throw new ExceptionRuntime("Internal error: an id should be a good reference");
12604fd306cSNickeau        }
12704fd306cSNickeau    }
12804fd306cSNickeau
12904fd306cSNickeau    /**
13004fd306cSNickeau     * @throws ExceptionBadArgument
13104fd306cSNickeau     * @throws ExceptionBadSyntax
13204fd306cSNickeau     * @throws ExceptionNotFound
13304fd306cSNickeau     */
13404fd306cSNickeau    public static function createFromRef(string $ref): LinkMarkup
13504fd306cSNickeau    {
13604fd306cSNickeau        return new LinkMarkup($ref);
13704fd306cSNickeau    }
13804fd306cSNickeau
13904fd306cSNickeau    public static function getHtmlClassLocalLink(): string
14004fd306cSNickeau    {
14104fd306cSNickeau        return "link-local";
14204fd306cSNickeau    }
14304fd306cSNickeau
14404fd306cSNickeau
14504fd306cSNickeau    /**
14604fd306cSNickeau     *
14704fd306cSNickeau     * @throws ExceptionNotFound
14804fd306cSNickeau     */
14904fd306cSNickeau    public function toAttributes(): TagAttributes
15004fd306cSNickeau    {
15104fd306cSNickeau
15204fd306cSNickeau        $outputAttributes = $this->stylingAttributes;
15304fd306cSNickeau
15404fd306cSNickeau
15504fd306cSNickeau        $url = $this->getMarkupRef()->getUrl();
15604fd306cSNickeau        $outputAttributes->addOutputAttributeValue("href", $url->toString());
15704fd306cSNickeau
15804fd306cSNickeau        /**
15904fd306cSNickeau         * The search term
16004fd306cSNickeau         * Code adapted found at {@link Doku_Renderer_xhtml::internallink()}
16104fd306cSNickeau         * We can't use the previous {@link wl function}
16204fd306cSNickeau         * because it encode too much
16304fd306cSNickeau         */
16404fd306cSNickeau        $snippetSystem = PluginUtility::getSnippetManager();
16504fd306cSNickeau        if ($url->hasProperty(self::SEARCH_HIGHLIGHT_QUERY_PROPERTY)) {
16604fd306cSNickeau            $snippetSystem->attachCssInternalStyleSheet("search-hit");
16704fd306cSNickeau        }
16846021406Sgerardnico        $snippetSystem->attachCssInternalStyleSheet(self::ANCHOR_HTML_SNIPPET_ID);
16904fd306cSNickeau
17004fd306cSNickeau        global $conf;
17104fd306cSNickeau
17204fd306cSNickeau
17304fd306cSNickeau        /**
17404fd306cSNickeau         * Processing by type
17504fd306cSNickeau         */
17604fd306cSNickeau        switch ($this->getMarkupRef()->getSchemeType()) {
17704fd306cSNickeau            case MarkupRef::INTERWIKI_URI:
17804fd306cSNickeau                try {
17904fd306cSNickeau                    $interWiki = $this->getMarkupRef()->getInterWiki();
18004fd306cSNickeau                } catch (ExceptionNotFound $e) {
18104fd306cSNickeau                    LogUtility::internalError("The interwiki should be available. We were unable to create the link attributes.");
18204fd306cSNickeau                    return $outputAttributes;
18304fd306cSNickeau                }
18404fd306cSNickeau                // normal link for the `this` wiki
18504fd306cSNickeau                if ($interWiki->getWiki() !== "this") {
18604fd306cSNickeau                    $snippetSystem->attachCssInternalStyleSheet(MarkupRef::INTERWIKI_URI);
18704fd306cSNickeau                }
18804fd306cSNickeau                $cssRules = $interWiki->getDefaultCssRules();
18904fd306cSNickeau                $snippetSystem->attachCssInternalStyleSheet(MarkupRef::INTERWIKI_URI, $cssRules);
19004fd306cSNickeau                try {
19104fd306cSNickeau                    $cssRules = $interWiki->getSpecificCssRules();
19204fd306cSNickeau                    $snippetSystem->attachCssInternalStyleSheet(MarkupRef::INTERWIKI_URI . "-" . $interWiki->getWiki(), $cssRules);
19304fd306cSNickeau                } catch (ExceptionNotFound $e) {
19404fd306cSNickeau                    // no media find for the wiki
19504fd306cSNickeau                }
19604fd306cSNickeau                /**
19704fd306cSNickeau                 * Target
19804fd306cSNickeau                 */
19904fd306cSNickeau                $interWikiConf = $conf['target']['interwiki'];
20004fd306cSNickeau                if (!empty($interWikiConf)) {
20104fd306cSNickeau                    $outputAttributes->addOutputAttributeValue('target', $interWikiConf);
20204fd306cSNickeau                    $outputAttributes->addOutputAttributeValue('rel', 'noopener');
20304fd306cSNickeau                }
20404fd306cSNickeau                $outputAttributes->addClassName($interWiki->getComponentClass());
20504fd306cSNickeau                $outputAttributes->addClassName($interWiki->getSubComponentClass());
20604fd306cSNickeau                break;
20704fd306cSNickeau            case MarkupRef::WIKI_URI:
20804fd306cSNickeau                /**
20904fd306cSNickeau                 * Derived from {@link Doku_Renderer_xhtml::internallink()}
21004fd306cSNickeau                 */
21104fd306cSNickeau                // https://www.dokuwiki.org/config:target
21204fd306cSNickeau                $target = $conf['target']['wiki'];
21304fd306cSNickeau                if (!empty($target)) {
21404fd306cSNickeau                    $outputAttributes->addOutputAttributeValue('target', $target);
21504fd306cSNickeau                }
21604fd306cSNickeau                /**
21704fd306cSNickeau                 * Internal Page
21804fd306cSNickeau                 */
21904fd306cSNickeau                try {
22004fd306cSNickeau                    $dokuPath = $this->getMarkupRef()->getPath();
22104fd306cSNickeau                } catch (ExceptionNotFound $e) {
22204fd306cSNickeau                    throw new ExceptionNotFound("We were unable to process the internal link dokuwiki id on the link. The path was not found. Error: {$e->getMessage()}");
22304fd306cSNickeau                }
22404fd306cSNickeau                $page = MarkupPath::createPageFromPathObject($dokuPath);
22504fd306cSNickeau                $outputAttributes->addOutputAttributeValue(self::DATA_WIKI_ID, $dokuPath->getWikiId());
22604fd306cSNickeau
227*b90165b7Sgerardnico                /**
228*b90165b7Sgerardnico                 * Preview, we add it here because even if the file does not exist
229*b90165b7Sgerardnico                 * we need to delete this attribute so that it's not in the HTML
230*b90165b7Sgerardnico                 */
231*b90165b7Sgerardnico                $previewConfig = SiteConfig::getConfValue(self::CONF_PREVIEW_LINK, self::CONF_PREVIEW_LINK_DEFAULT);
232*b90165b7Sgerardnico                $preview = $outputAttributes->getBooleanValueAndRemoveIfPresent(self::PREVIEW_ATTRIBUTE, $previewConfig);
23304fd306cSNickeau
23404fd306cSNickeau                if (!FileSystems::exists($dokuPath)) {
23504fd306cSNickeau
23604fd306cSNickeau                    /**
23704fd306cSNickeau                     * Red color
23804fd306cSNickeau                     * if not `do=edit`
23904fd306cSNickeau                     */
24004fd306cSNickeau                    if (!$this->markupRef->getUrl()->hasProperty("do")) {
24104fd306cSNickeau                        $outputAttributes->addClassName(self::getHtmlClassNotExist());
24204fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'nofollow');
24304fd306cSNickeau                    }
24404fd306cSNickeau
24504fd306cSNickeau                } else {
24604fd306cSNickeau
24704fd306cSNickeau                    /**
24804fd306cSNickeau                     * Internal Link Class
24904fd306cSNickeau                     */
25004fd306cSNickeau                    $outputAttributes->addClassName(self::getHtmlClassInternalLink());
25104fd306cSNickeau
25204fd306cSNickeau                    /**
25304fd306cSNickeau                     * Link Creation
25404fd306cSNickeau                     * Do we need to set the title or the tooltip
25504fd306cSNickeau                     * Processing variables
25604fd306cSNickeau                     */
25704fd306cSNickeau                    $acronym = "";
25804fd306cSNickeau
25904fd306cSNickeau                    /**
26004fd306cSNickeau                     * Preview tooltip
26104fd306cSNickeau                     */
262*b90165b7Sgerardnico                    if ($preview) {
26304fd306cSNickeau                        Tooltip::addToolTipSnippetIfNeeded();
26404fd306cSNickeau                        // We use as heading, the name and not the title of the resource because otherwise it would be to lengthy
26504fd306cSNickeau                        $tooltipHtml = <<<EOF
26604fd306cSNickeau<h3>{$page->getNameOrDefault()}</h3>
26704fd306cSNickeau<p>{$page->getDescriptionOrElseDokuWiki()}</p>
26804fd306cSNickeauEOF;
26904fd306cSNickeau                        $dataAttributeNamespace = Bootstrap::getDataNamespace();
27004fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-toggle", "tooltip");
27104fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-placement", "top");
27204fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-html", "true");
27304fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $tooltipHtml);
27404fd306cSNickeau                    }
27504fd306cSNickeau
27604fd306cSNickeau                    /**
27704fd306cSNickeau                     * Low quality Page
27804fd306cSNickeau                     * (It has a higher priority than preview and
27904fd306cSNickeau                     * the code comes then after)
28004fd306cSNickeau                     */
28104fd306cSNickeau                    if ($page->isLowQualityPage()) {
28204fd306cSNickeau
28304fd306cSNickeau                        /**
28404fd306cSNickeau                         * Add a class to style it differently
28504fd306cSNickeau                         * (the acronym is added to the description, later)
28604fd306cSNickeau                         */
28704fd306cSNickeau                        $acronym = LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM;
28804fd306cSNickeau                        $lowerCaseLowQualityAcronym = strtolower(LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM);
28904fd306cSNickeau                        $outputAttributes->addClassName(StyleAttribute::addComboStrapSuffix(LowQualityPage::CLASS_SUFFIX));
29004fd306cSNickeau                        $snippetLowQualityPageId = $lowerCaseLowQualityAcronym;
29104fd306cSNickeau                        $snippetSystem->attachCssInternalStyleSheet($snippetLowQualityPageId);
29204fd306cSNickeau                        /**
29304fd306cSNickeau                         * Note The protection does occur on Javascript level, not on the HTML
29404fd306cSNickeau                         * because the created page is valid for a anonymous or logged-in user
29504fd306cSNickeau                         * Javascript is controlling
29604fd306cSNickeau                         */
29704fd306cSNickeau                        if (LowQualityPage::isProtectionEnabled()) {
29804fd306cSNickeau
29904fd306cSNickeau                            $linkType = LowQualityPage::getLowQualityLinkType();
30004fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_LINK, $linkType);
30104fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_SOURCE, $lowerCaseLowQualityAcronym);
30204fd306cSNickeau
30304fd306cSNickeau                            /**
30404fd306cSNickeau                             * Low Quality Page protection javascript is only for warning or login link
30504fd306cSNickeau                             */
30604fd306cSNickeau                            if (in_array($linkType, [PageProtection::PAGE_PROTECTION_LINK_WARNING, PageProtection::PAGE_PROTECTION_LINK_LOGIN])) {
30704fd306cSNickeau                                PageProtection::addPageProtectionSnippet();
30804fd306cSNickeau                            }
30904fd306cSNickeau
31004fd306cSNickeau                        }
31104fd306cSNickeau                    }
31204fd306cSNickeau
31304fd306cSNickeau                    /**
31404fd306cSNickeau                     * Late publication has a higher priority than
31504fd306cSNickeau                     * the late publication and the is therefore after
31604fd306cSNickeau                     * (In case this a low quality page late published)
31704fd306cSNickeau                     */
31804fd306cSNickeau                    if ($page->isLatePublication()) {
31904fd306cSNickeau                        /**
32004fd306cSNickeau                         * Add a class to style it differently if needed
32104fd306cSNickeau                         */
32204fd306cSNickeau                        $className = StyleAttribute::addComboStrapSuffix(PagePublicationDate::LATE_PUBLICATION_CLASS_PREFIX_NAME);
32304fd306cSNickeau                        $outputAttributes->addClassName($className);
32404fd306cSNickeau                        if (PagePublicationDate::isLatePublicationProtectionEnabled()) {
32504fd306cSNickeau                            $outputAttributes->removeOutputAttributeIfPresent(PageProtection::DATA_PP_LINK);
32604fd306cSNickeau                            $outputAttributes->removeOutputAttributeIfPresent(PageProtection::DATA_PP_SOURCE);
32704fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_LINK, PageProtection::PAGE_PROTECTION_LINK_LOGIN);
32804fd306cSNickeau                            $acronym = PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM;
32904fd306cSNickeau                            $lowerCaseLatePublicationAcronym = strtolower(PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM);
33004fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_SOURCE, $lowerCaseLatePublicationAcronym);
33104fd306cSNickeau                            PageProtection::addPageProtectionSnippet();
33204fd306cSNickeau                        }
33304fd306cSNickeau
33404fd306cSNickeau                    }
33504fd306cSNickeau
33604fd306cSNickeau                    /**
33704fd306cSNickeau                     * Title (ie tooltip vs title html attribute)
33804fd306cSNickeau                     */
33904fd306cSNickeau                    if (!$outputAttributes->hasAttribute("title")) {
34004fd306cSNickeau
34104fd306cSNickeau                        $description = PageDescription::createForPage($page)->getValueOrDefault();
34204fd306cSNickeau                        if (!empty($acronym)) {
34304fd306cSNickeau                            $description = $description . " ($acronym)";
34404fd306cSNickeau                        }
34504fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $description);
34604fd306cSNickeau
34704fd306cSNickeau                    }
34804fd306cSNickeau
34904fd306cSNickeau                }
35004fd306cSNickeau
35104fd306cSNickeau                break;
35204fd306cSNickeau
35304fd306cSNickeau            case MarkupRef::WINDOWS_SHARE_URI:
35404fd306cSNickeau                // https://www.dokuwiki.org/config:target
35504fd306cSNickeau                $windowsTarget = $conf['target']['windows'];
35604fd306cSNickeau                if (!empty($windowsTarget)) {
35704fd306cSNickeau                    $outputAttributes->addOutputAttributeValue('target', $windowsTarget);
35804fd306cSNickeau                }
35904fd306cSNickeau                $outputAttributes->addClassName("windows");
36004fd306cSNickeau                break;
36104fd306cSNickeau            case MarkupRef::LOCAL_URI:
36204fd306cSNickeau                $outputAttributes->addClassName(self::getHtmlClassLocalLink());
36304fd306cSNickeau                if (!$outputAttributes->hasAttribute("title")) {
36404fd306cSNickeau                    $description = ucfirst($this->markupRef->getUrl()->getFragment());
36504fd306cSNickeau                    if ($description !== "") {
36604fd306cSNickeau                        $description = str_replace("_", " ", $description);
36704fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $description);
36804fd306cSNickeau                    }
36904fd306cSNickeau                }
37004fd306cSNickeau                break;
37104fd306cSNickeau            case MarkupRef::EMAIL_URI:
37204fd306cSNickeau                $outputAttributes->addClassName(self::getHtmlClassEmailLink());
37304fd306cSNickeau                /**
37404fd306cSNickeau                 * An email link is `<email>`
37504fd306cSNickeau                 * {@link Emaillink::connectTo()}
37604fd306cSNickeau                 * or
37704fd306cSNickeau                 * {@link PluginTrait::email()
37804fd306cSNickeau                 */
37904fd306cSNickeau                // common.php#obfsucate implements the $conf['mailguard']
38004fd306cSNickeau                $uri = $url->getPath();
38104fd306cSNickeau                $uri = $this->obfuscateEmail($uri);
38204fd306cSNickeau                $uri = urlencode($uri);
38304fd306cSNickeau                $queryParameters = $url->getQueryProperties();
38404fd306cSNickeau                if (sizeof($queryParameters) > 0) {
38504fd306cSNickeau                    $uri .= "?";
38604fd306cSNickeau                    foreach ($queryParameters as $key => $value) {
38704fd306cSNickeau                        $value = urlencode($value);
38804fd306cSNickeau                        $key = urlencode($key);
38904fd306cSNickeau                        if (in_array($key, self::EMAIL_VALID_PARAMETERS)) {
39004fd306cSNickeau                            $uri .= "$key=$value";
39104fd306cSNickeau                        }
39204fd306cSNickeau                    }
39304fd306cSNickeau                }
39404fd306cSNickeau                // replace href
39504fd306cSNickeau                $outputAttributes->removeOutputAttributeIfPresent("href");
39604fd306cSNickeau                $outputAttributes->addOutputAttributeValue("href", 'mailto:' . $uri);
39704fd306cSNickeau                break;
39804fd306cSNickeau            case MarkupRef::WEB_URI:
39904fd306cSNickeau                /**
40004fd306cSNickeau                 * It may be a absolute url
40104fd306cSNickeau                 * that points to the local website
40204fd306cSNickeau                 * (case of the {@link \syntax_plugin_combo_permalink}
40304fd306cSNickeau                 */
40404fd306cSNickeau                if ($url->isExternal()) {
40504fd306cSNickeau
40604fd306cSNickeau                    if ($conf['relnofollow']) {
40704fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'nofollow ugc');
40804fd306cSNickeau                    }
40904fd306cSNickeau                    // https://www.dokuwiki.org/config:target
41004fd306cSNickeau                    $externTarget = $conf['target']['extern'];
41104fd306cSNickeau                    if (!empty($externTarget)) {
41204fd306cSNickeau                        $outputAttributes->addOutputAttributeValue('target', $externTarget);
41304fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'noopener');
41404fd306cSNickeau                    }
41504fd306cSNickeau                    /**
41604fd306cSNickeau                     * Default class for default external link
41704fd306cSNickeau                     * To not interfere with other external link style
41804fd306cSNickeau                     * For instance, {@link \syntax_plugin_combo_share}
41904fd306cSNickeau                     */
42004fd306cSNickeau                    $outputAttributes->addClassName(self::getHtmlClassExternalLink());
42104fd306cSNickeau                }
42204fd306cSNickeau                break;
42304fd306cSNickeau            default:
42404fd306cSNickeau                /**
42504fd306cSNickeau                 * May be any external link
42604fd306cSNickeau                 * such as {@link \syntax_plugin_combo_share}
42704fd306cSNickeau                 */
42804fd306cSNickeau                break;
42904fd306cSNickeau
43004fd306cSNickeau        }
43104fd306cSNickeau
43204fd306cSNickeau        /**
43304fd306cSNickeau         * An email URL and title
43404fd306cSNickeau         * may be already encoded because of the vanguard configuration
43504fd306cSNickeau         *
43604fd306cSNickeau         * The url is not treated as an attribute
43704fd306cSNickeau         * because the transformation function encodes the value
43804fd306cSNickeau         * to mitigate XSS
43904fd306cSNickeau         *
44004fd306cSNickeau         */
44104fd306cSNickeau        if ($this->getMarkupRef()->getSchemeType() == MarkupRef::EMAIL_URI) {
44204fd306cSNickeau            $emailAddress = $this->obfuscateEmail($this->markupRef->getUrl()->getPath());
44304fd306cSNickeau            $outputAttributes->addOutputAttributeValue("title", $emailAddress);
44404fd306cSNickeau        }
44504fd306cSNickeau
44604fd306cSNickeau
44704fd306cSNickeau        /**
44804fd306cSNickeau         * Return
44904fd306cSNickeau         */
45004fd306cSNickeau        return $outputAttributes;
45104fd306cSNickeau
45204fd306cSNickeau
45304fd306cSNickeau    }
45404fd306cSNickeau
45504fd306cSNickeau
45604fd306cSNickeau    /**
45704fd306cSNickeau     * The label inside the anchor tag if there is none
45804fd306cSNickeau     * @param false $navigation
45904fd306cSNickeau     * @return string
46004fd306cSNickeau     * @throws ExceptionNotFound|ExceptionBadArgument
46104fd306cSNickeau     *
46204fd306cSNickeau     */
46304fd306cSNickeau    public
46404fd306cSNickeau    function getDefaultLabel(bool $navigation = false): string
46504fd306cSNickeau    {
46604fd306cSNickeau
46704fd306cSNickeau        switch ($this->getMarkupRef()->getSchemeType()) {
46804fd306cSNickeau            case MarkupRef::WIKI_URI:
46904fd306cSNickeau                $page = $this->getPage();
47004fd306cSNickeau                if ($navigation) {
47104fd306cSNickeau                    return ResourceName::createForResource($page)->getValueOrDefault();
47204fd306cSNickeau                } else {
47304fd306cSNickeau                    return PageTitle::createForMarkup($page)->getValueOrDefault();
47404fd306cSNickeau                }
47504fd306cSNickeau            case MarkupRef::EMAIL_URI:
47604fd306cSNickeau                global $conf;
47704fd306cSNickeau                $email = $this->markupRef->getUrl()->getPath();
47804fd306cSNickeau                switch ($conf['mailguard']) {
47904fd306cSNickeau                    case 'none' :
48004fd306cSNickeau                        return $email;
48104fd306cSNickeau                    case 'visible' :
48204fd306cSNickeau                    default :
48304fd306cSNickeau                        $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
48404fd306cSNickeau                        return strtr($email, $obfuscate);
48504fd306cSNickeau                }
48604fd306cSNickeau            case MarkupRef::INTERWIKI_URI:
48704fd306cSNickeau                try {
48804fd306cSNickeau                    $path = $this->markupRef->getInterWiki()->toUrl()->getPath();
48904fd306cSNickeau                    if ($path[0] === "/") {
49004fd306cSNickeau                        return substr($path, 1);
49104fd306cSNickeau                    } else {
49204fd306cSNickeau                        return $path;
49304fd306cSNickeau                    }
49404fd306cSNickeau                } catch (ExceptionBadSyntax|ExceptionNotFound $e) {
49504fd306cSNickeau                    return "interwiki";
49604fd306cSNickeau                }
49704fd306cSNickeau            case MarkupRef::LOCAL_URI:
49804fd306cSNickeau                return $this->markupRef->getUrl()->getFragment();
49904fd306cSNickeau            default:
50004fd306cSNickeau                return $this->markupRef->getRef();
50104fd306cSNickeau        }
50204fd306cSNickeau    }
50304fd306cSNickeau
50404fd306cSNickeau
50504fd306cSNickeau    private
50604fd306cSNickeau    function obfuscateEmail($email, $inAttribute = true): string
50704fd306cSNickeau    {
50804fd306cSNickeau        /**
50904fd306cSNickeau         * adapted from {@link obfuscate()} in common.php
51004fd306cSNickeau         */
51104fd306cSNickeau        global $conf;
51204fd306cSNickeau
51304fd306cSNickeau        $mailGuard = $conf['mailguard'];
51404fd306cSNickeau        if ($mailGuard === "hex" && $inAttribute) {
51504fd306cSNickeau            $mailGuard = "visible";
51604fd306cSNickeau        }
51704fd306cSNickeau        switch ($mailGuard) {
51804fd306cSNickeau            case 'visible' :
51904fd306cSNickeau                $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
52004fd306cSNickeau                return strtr($email, $obfuscate);
52104fd306cSNickeau
52204fd306cSNickeau            case 'hex' :
52304fd306cSNickeau                return Conversion::toHtml($email, true);
52404fd306cSNickeau
52504fd306cSNickeau            case 'none' :
52604fd306cSNickeau            default :
52704fd306cSNickeau                return $email;
52804fd306cSNickeau        }
52904fd306cSNickeau    }
53004fd306cSNickeau
53104fd306cSNickeau
53204fd306cSNickeau    /**
53304fd306cSNickeau     * @return bool
53404fd306cSNickeau     * @deprecated should not be here ref does not have the notion of relative
53504fd306cSNickeau     */
53604fd306cSNickeau    public
53704fd306cSNickeau    function isRelative(): bool
53804fd306cSNickeau    {
53904fd306cSNickeau        return strpos($this->getMarkupRef()->getRef(), WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) !== 0;
54004fd306cSNickeau    }
54104fd306cSNickeau
54204fd306cSNickeau    public
54304fd306cSNickeau    function getMarkupRef(): MarkupRef
54404fd306cSNickeau    {
54504fd306cSNickeau        return $this->markupRef;
54604fd306cSNickeau    }
54704fd306cSNickeau
54804fd306cSNickeau
54904fd306cSNickeau    public
55004fd306cSNickeau    static function getHtmlClassInternalLink(): string
55104fd306cSNickeau    {
55204fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
55304fd306cSNickeau        if ($oldClassName) {
55404fd306cSNickeau            return "wikilink1";
55504fd306cSNickeau        } else {
55604fd306cSNickeau            return "link-internal";
55704fd306cSNickeau        }
55804fd306cSNickeau    }
55904fd306cSNickeau
56004fd306cSNickeau    public
56104fd306cSNickeau    static function getHtmlClassEmailLink(): string
56204fd306cSNickeau    {
56304fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
56404fd306cSNickeau        if ($oldClassName) {
56504fd306cSNickeau            return "mail";
56604fd306cSNickeau        } else {
56704fd306cSNickeau            return "link-mail";
56804fd306cSNickeau        }
56904fd306cSNickeau    }
57004fd306cSNickeau
57104fd306cSNickeau    public static function getHtmlClassExternalLink(): string
57204fd306cSNickeau    {
57304fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
57404fd306cSNickeau        if ($oldClassName) {
57504fd306cSNickeau            return "urlextern";
57604fd306cSNickeau        } else {
57704fd306cSNickeau            return "link-external";
57804fd306cSNickeau        }
57904fd306cSNickeau    }
58004fd306cSNickeau
58104fd306cSNickeau//FYI: exist in dokuwiki is "wikilink1 but we let the control to the user
58204fd306cSNickeau    public
58304fd306cSNickeau    static function getHtmlClassNotExist(): string
58404fd306cSNickeau    {
58504fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
58604fd306cSNickeau        if ($oldClassName) {
58704fd306cSNickeau            return "wikilink2";
58804fd306cSNickeau        } else {
58904fd306cSNickeau            return self::TEXT_ERROR_CLASS;
59004fd306cSNickeau        }
59104fd306cSNickeau    }
59204fd306cSNickeau
59304fd306cSNickeau    public
59404fd306cSNickeau    function __toString()
59504fd306cSNickeau    {
59604fd306cSNickeau        return $this->getMarkupRef()->getRef();
59704fd306cSNickeau    }
59804fd306cSNickeau
59904fd306cSNickeau
60004fd306cSNickeau    /**
60104fd306cSNickeau     * @throws ExceptionNotFound
60204fd306cSNickeau     */
60304fd306cSNickeau    private
60404fd306cSNickeau    function getPage(): MarkupPath
60504fd306cSNickeau    {
60604fd306cSNickeau        return MarkupPath::createPageFromPathObject($this->getMarkupRef()->getPath());
60704fd306cSNickeau    }
60804fd306cSNickeau
60904fd306cSNickeau    /**
61004fd306cSNickeau     * Styling attribute
61104fd306cSNickeau     * may be passed via parameters
61204fd306cSNickeau     * for internal link
61304fd306cSNickeau     * We don't want the styling attribute
61404fd306cSNickeau     * in the URL
61504fd306cSNickeau     */
61604fd306cSNickeau    private
61704fd306cSNickeau    function collectStylingAttributeInUrl()
61804fd306cSNickeau    {
61904fd306cSNickeau
62004fd306cSNickeau
62104fd306cSNickeau        /**
62204fd306cSNickeau         * We will not overwrite the parameters if this is an dokuwiki
62304fd306cSNickeau         * action link (with the `do` property)
62404fd306cSNickeau         */
62504fd306cSNickeau        if ($this->markupRef->getUrl()->hasProperty("do")) {
62604fd306cSNickeau            return;
62704fd306cSNickeau        }
62804fd306cSNickeau
62904fd306cSNickeau        /**
63004fd306cSNickeau         * Add the attribute from the URL
63104fd306cSNickeau         * if this is not a `do`
63204fd306cSNickeau         */
63304fd306cSNickeau        switch ($this->markupRef->getSchemeType()) {
63404fd306cSNickeau            case MarkupRef::WIKI_URI:
63504fd306cSNickeau                foreach ($this->getMarkupRef()->getUrl()->getQueryProperties() as $key => $value) {
63604fd306cSNickeau                    if (!in_array($key, self::PROTECTED_URL_PROPERTY)) {
63704fd306cSNickeau                        $this->getMarkupRef()->getUrl()->deleteQueryParameter($key);
63804fd306cSNickeau                        if (!TagAttributes::isEmptyValue($value)) {
63904fd306cSNickeau                            $this->stylingAttributes->addComponentAttributeValue($key, $value);
64004fd306cSNickeau                        } else {
64104fd306cSNickeau                            $this->stylingAttributes->addEmptyComponentAttributeValue($key);
64204fd306cSNickeau                        }
64304fd306cSNickeau                    }
64404fd306cSNickeau                }
64504fd306cSNickeau                break;
64604fd306cSNickeau            case
64704fd306cSNickeau            MarkupRef::EMAIL_URI:
64804fd306cSNickeau                foreach ($this->getMarkupRef()->getUrl()->getQueryProperties() as $key => $value) {
64904fd306cSNickeau                    if (!in_array($key, self::EMAIL_VALID_PARAMETERS)) {
65004fd306cSNickeau                        $this->stylingAttributes->addComponentAttributeValue($key, $value);
65104fd306cSNickeau                    }
65204fd306cSNickeau                }
65304fd306cSNickeau                break;
65404fd306cSNickeau        }
65504fd306cSNickeau
65604fd306cSNickeau    }
65704fd306cSNickeau
65804fd306cSNickeau    /**
65904fd306cSNickeau     * @return TagAttributes - the unknown attributes in a url are collected as styling attributes if this not a do query
66004fd306cSNickeau     * by {@link LinkMarkup::collectStylingAttributeInUrl()}
66104fd306cSNickeau     */
66204fd306cSNickeau    public
66304fd306cSNickeau    function getStylingAttributes(): TagAttributes
66404fd306cSNickeau    {
66504fd306cSNickeau        return $this->stylingAttributes;
66604fd306cSNickeau    }
66704fd306cSNickeau
66804fd306cSNickeau
66904fd306cSNickeau}
670