xref: /template/strap/ComboStrap/LinkMarkup.php (revision 46021406a45186deda55326760c58f274bb3715b)
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     */
78*46021406Sgerardnico    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        }
168*46021406Sgerardnico        $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
22704fd306cSNickeau
22804fd306cSNickeau                if (!FileSystems::exists($dokuPath)) {
22904fd306cSNickeau
23004fd306cSNickeau                    /**
23104fd306cSNickeau                     * Red color
23204fd306cSNickeau                     * if not `do=edit`
23304fd306cSNickeau                     */
23404fd306cSNickeau                    if (!$this->markupRef->getUrl()->hasProperty("do")) {
23504fd306cSNickeau                        $outputAttributes->addClassName(self::getHtmlClassNotExist());
23604fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'nofollow');
23704fd306cSNickeau                    }
23804fd306cSNickeau
23904fd306cSNickeau                } else {
24004fd306cSNickeau
24104fd306cSNickeau                    /**
24204fd306cSNickeau                     * Internal Link Class
24304fd306cSNickeau                     */
24404fd306cSNickeau                    $outputAttributes->addClassName(self::getHtmlClassInternalLink());
24504fd306cSNickeau
24604fd306cSNickeau                    /**
24704fd306cSNickeau                     * Link Creation
24804fd306cSNickeau                     * Do we need to set the title or the tooltip
24904fd306cSNickeau                     * Processing variables
25004fd306cSNickeau                     */
25104fd306cSNickeau                    $acronym = "";
25204fd306cSNickeau
25304fd306cSNickeau                    /**
25404fd306cSNickeau                     * Preview tooltip
25504fd306cSNickeau                     */
25604fd306cSNickeau                    $previewConfig = SiteConfig::getConfValue(self::CONF_PREVIEW_LINK, self::CONF_PREVIEW_LINK_DEFAULT);
25704fd306cSNickeau                    $preview = $outputAttributes->hasComponentAttributeAndRemove(self::PREVIEW_ATTRIBUTE);
25804fd306cSNickeau                    if ($preview || $previewConfig === 1) {
25904fd306cSNickeau                        Tooltip::addToolTipSnippetIfNeeded();
26004fd306cSNickeau                        // We use as heading, the name and not the title of the resource because otherwise it would be to lengthy
26104fd306cSNickeau                        $tooltipHtml = <<<EOF
26204fd306cSNickeau<h3>{$page->getNameOrDefault()}</h3>
26304fd306cSNickeau<p>{$page->getDescriptionOrElseDokuWiki()}</p>
26404fd306cSNickeauEOF;
26504fd306cSNickeau                        $dataAttributeNamespace = Bootstrap::getDataNamespace();
26604fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-toggle", "tooltip");
26704fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-placement", "top");
26804fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-html", "true");
26904fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $tooltipHtml);
27004fd306cSNickeau                    }
27104fd306cSNickeau
27204fd306cSNickeau                    /**
27304fd306cSNickeau                     * Low quality Page
27404fd306cSNickeau                     * (It has a higher priority than preview and
27504fd306cSNickeau                     * the code comes then after)
27604fd306cSNickeau                     */
27704fd306cSNickeau                    if ($page->isLowQualityPage()) {
27804fd306cSNickeau
27904fd306cSNickeau                        /**
28004fd306cSNickeau                         * Add a class to style it differently
28104fd306cSNickeau                         * (the acronym is added to the description, later)
28204fd306cSNickeau                         */
28304fd306cSNickeau                        $acronym = LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM;
28404fd306cSNickeau                        $lowerCaseLowQualityAcronym = strtolower(LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM);
28504fd306cSNickeau                        $outputAttributes->addClassName(StyleAttribute::addComboStrapSuffix(LowQualityPage::CLASS_SUFFIX));
28604fd306cSNickeau                        $snippetLowQualityPageId = $lowerCaseLowQualityAcronym;
28704fd306cSNickeau                        $snippetSystem->attachCssInternalStyleSheet($snippetLowQualityPageId);
28804fd306cSNickeau                        /**
28904fd306cSNickeau                         * Note The protection does occur on Javascript level, not on the HTML
29004fd306cSNickeau                         * because the created page is valid for a anonymous or logged-in user
29104fd306cSNickeau                         * Javascript is controlling
29204fd306cSNickeau                         */
29304fd306cSNickeau                        if (LowQualityPage::isProtectionEnabled()) {
29404fd306cSNickeau
29504fd306cSNickeau                            $linkType = LowQualityPage::getLowQualityLinkType();
29604fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_LINK, $linkType);
29704fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_SOURCE, $lowerCaseLowQualityAcronym);
29804fd306cSNickeau
29904fd306cSNickeau                            /**
30004fd306cSNickeau                             * Low Quality Page protection javascript is only for warning or login link
30104fd306cSNickeau                             */
30204fd306cSNickeau                            if (in_array($linkType, [PageProtection::PAGE_PROTECTION_LINK_WARNING, PageProtection::PAGE_PROTECTION_LINK_LOGIN])) {
30304fd306cSNickeau                                PageProtection::addPageProtectionSnippet();
30404fd306cSNickeau                            }
30504fd306cSNickeau
30604fd306cSNickeau                        }
30704fd306cSNickeau                    }
30804fd306cSNickeau
30904fd306cSNickeau                    /**
31004fd306cSNickeau                     * Late publication has a higher priority than
31104fd306cSNickeau                     * the late publication and the is therefore after
31204fd306cSNickeau                     * (In case this a low quality page late published)
31304fd306cSNickeau                     */
31404fd306cSNickeau                    if ($page->isLatePublication()) {
31504fd306cSNickeau                        /**
31604fd306cSNickeau                         * Add a class to style it differently if needed
31704fd306cSNickeau                         */
31804fd306cSNickeau                        $className = StyleAttribute::addComboStrapSuffix(PagePublicationDate::LATE_PUBLICATION_CLASS_PREFIX_NAME);
31904fd306cSNickeau                        $outputAttributes->addClassName($className);
32004fd306cSNickeau                        if (PagePublicationDate::isLatePublicationProtectionEnabled()) {
32104fd306cSNickeau                            $outputAttributes->removeOutputAttributeIfPresent(PageProtection::DATA_PP_LINK);
32204fd306cSNickeau                            $outputAttributes->removeOutputAttributeIfPresent(PageProtection::DATA_PP_SOURCE);
32304fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_LINK, PageProtection::PAGE_PROTECTION_LINK_LOGIN);
32404fd306cSNickeau                            $acronym = PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM;
32504fd306cSNickeau                            $lowerCaseLatePublicationAcronym = strtolower(PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM);
32604fd306cSNickeau                            $outputAttributes->addOutputAttributeValue(PageProtection::DATA_PP_SOURCE, $lowerCaseLatePublicationAcronym);
32704fd306cSNickeau                            PageProtection::addPageProtectionSnippet();
32804fd306cSNickeau                        }
32904fd306cSNickeau
33004fd306cSNickeau                    }
33104fd306cSNickeau
33204fd306cSNickeau                    /**
33304fd306cSNickeau                     * Title (ie tooltip vs title html attribute)
33404fd306cSNickeau                     */
33504fd306cSNickeau                    if (!$outputAttributes->hasAttribute("title")) {
33604fd306cSNickeau
33704fd306cSNickeau                        $description = PageDescription::createForPage($page)->getValueOrDefault();
33804fd306cSNickeau                        if (!empty($acronym)) {
33904fd306cSNickeau                            $description = $description . " ($acronym)";
34004fd306cSNickeau                        }
34104fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $description);
34204fd306cSNickeau
34304fd306cSNickeau                    }
34404fd306cSNickeau
34504fd306cSNickeau                }
34604fd306cSNickeau
34704fd306cSNickeau                break;
34804fd306cSNickeau
34904fd306cSNickeau            case MarkupRef::WINDOWS_SHARE_URI:
35004fd306cSNickeau                // https://www.dokuwiki.org/config:target
35104fd306cSNickeau                $windowsTarget = $conf['target']['windows'];
35204fd306cSNickeau                if (!empty($windowsTarget)) {
35304fd306cSNickeau                    $outputAttributes->addOutputAttributeValue('target', $windowsTarget);
35404fd306cSNickeau                }
35504fd306cSNickeau                $outputAttributes->addClassName("windows");
35604fd306cSNickeau                break;
35704fd306cSNickeau            case MarkupRef::LOCAL_URI:
35804fd306cSNickeau                $outputAttributes->addClassName(self::getHtmlClassLocalLink());
35904fd306cSNickeau                if (!$outputAttributes->hasAttribute("title")) {
36004fd306cSNickeau                    $description = ucfirst($this->markupRef->getUrl()->getFragment());
36104fd306cSNickeau                    if ($description !== "") {
36204fd306cSNickeau                        $description = str_replace("_", " ", $description);
36304fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("title", $description);
36404fd306cSNickeau                    }
36504fd306cSNickeau                }
36604fd306cSNickeau                break;
36704fd306cSNickeau            case MarkupRef::EMAIL_URI:
36804fd306cSNickeau                $outputAttributes->addClassName(self::getHtmlClassEmailLink());
36904fd306cSNickeau                /**
37004fd306cSNickeau                 * An email link is `<email>`
37104fd306cSNickeau                 * {@link Emaillink::connectTo()}
37204fd306cSNickeau                 * or
37304fd306cSNickeau                 * {@link PluginTrait::email()
37404fd306cSNickeau                 */
37504fd306cSNickeau                // common.php#obfsucate implements the $conf['mailguard']
37604fd306cSNickeau                $uri = $url->getPath();
37704fd306cSNickeau                $uri = $this->obfuscateEmail($uri);
37804fd306cSNickeau                $uri = urlencode($uri);
37904fd306cSNickeau                $queryParameters = $url->getQueryProperties();
38004fd306cSNickeau                if (sizeof($queryParameters) > 0) {
38104fd306cSNickeau                    $uri .= "?";
38204fd306cSNickeau                    foreach ($queryParameters as $key => $value) {
38304fd306cSNickeau                        $value = urlencode($value);
38404fd306cSNickeau                        $key = urlencode($key);
38504fd306cSNickeau                        if (in_array($key, self::EMAIL_VALID_PARAMETERS)) {
38604fd306cSNickeau                            $uri .= "$key=$value";
38704fd306cSNickeau                        }
38804fd306cSNickeau                    }
38904fd306cSNickeau                }
39004fd306cSNickeau                // replace href
39104fd306cSNickeau                $outputAttributes->removeOutputAttributeIfPresent("href");
39204fd306cSNickeau                $outputAttributes->addOutputAttributeValue("href", 'mailto:' . $uri);
39304fd306cSNickeau                break;
39404fd306cSNickeau            case MarkupRef::WEB_URI:
39504fd306cSNickeau                /**
39604fd306cSNickeau                 * It may be a absolute url
39704fd306cSNickeau                 * that points to the local website
39804fd306cSNickeau                 * (case of the {@link \syntax_plugin_combo_permalink}
39904fd306cSNickeau                 */
40004fd306cSNickeau                if ($url->isExternal()) {
40104fd306cSNickeau
40204fd306cSNickeau                    if ($conf['relnofollow']) {
40304fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'nofollow ugc');
40404fd306cSNickeau                    }
40504fd306cSNickeau                    // https://www.dokuwiki.org/config:target
40604fd306cSNickeau                    $externTarget = $conf['target']['extern'];
40704fd306cSNickeau                    if (!empty($externTarget)) {
40804fd306cSNickeau                        $outputAttributes->addOutputAttributeValue('target', $externTarget);
40904fd306cSNickeau                        $outputAttributes->addOutputAttributeValue("rel", 'noopener');
41004fd306cSNickeau                    }
41104fd306cSNickeau                    /**
41204fd306cSNickeau                     * Default class for default external link
41304fd306cSNickeau                     * To not interfere with other external link style
41404fd306cSNickeau                     * For instance, {@link \syntax_plugin_combo_share}
41504fd306cSNickeau                     */
41604fd306cSNickeau                    $outputAttributes->addClassName(self::getHtmlClassExternalLink());
41704fd306cSNickeau                }
41804fd306cSNickeau                break;
41904fd306cSNickeau            default:
42004fd306cSNickeau                /**
42104fd306cSNickeau                 * May be any external link
42204fd306cSNickeau                 * such as {@link \syntax_plugin_combo_share}
42304fd306cSNickeau                 */
42404fd306cSNickeau                break;
42504fd306cSNickeau
42604fd306cSNickeau        }
42704fd306cSNickeau
42804fd306cSNickeau        /**
42904fd306cSNickeau         * An email URL and title
43004fd306cSNickeau         * may be already encoded because of the vanguard configuration
43104fd306cSNickeau         *
43204fd306cSNickeau         * The url is not treated as an attribute
43304fd306cSNickeau         * because the transformation function encodes the value
43404fd306cSNickeau         * to mitigate XSS
43504fd306cSNickeau         *
43604fd306cSNickeau         */
43704fd306cSNickeau        if ($this->getMarkupRef()->getSchemeType() == MarkupRef::EMAIL_URI) {
43804fd306cSNickeau            $emailAddress = $this->obfuscateEmail($this->markupRef->getUrl()->getPath());
43904fd306cSNickeau            $outputAttributes->addOutputAttributeValue("title", $emailAddress);
44004fd306cSNickeau        }
44104fd306cSNickeau
44204fd306cSNickeau
44304fd306cSNickeau        /**
44404fd306cSNickeau         * Return
44504fd306cSNickeau         */
44604fd306cSNickeau        return $outputAttributes;
44704fd306cSNickeau
44804fd306cSNickeau
44904fd306cSNickeau    }
45004fd306cSNickeau
45104fd306cSNickeau
45204fd306cSNickeau    /**
45304fd306cSNickeau     * The label inside the anchor tag if there is none
45404fd306cSNickeau     * @param false $navigation
45504fd306cSNickeau     * @return string
45604fd306cSNickeau     * @throws ExceptionNotFound|ExceptionBadArgument
45704fd306cSNickeau     *
45804fd306cSNickeau     */
45904fd306cSNickeau    public
46004fd306cSNickeau    function getDefaultLabel(bool $navigation = false): string
46104fd306cSNickeau    {
46204fd306cSNickeau
46304fd306cSNickeau        switch ($this->getMarkupRef()->getSchemeType()) {
46404fd306cSNickeau            case MarkupRef::WIKI_URI:
46504fd306cSNickeau                $page = $this->getPage();
46604fd306cSNickeau                if ($navigation) {
46704fd306cSNickeau                    return ResourceName::createForResource($page)->getValueOrDefault();
46804fd306cSNickeau                } else {
46904fd306cSNickeau                    return PageTitle::createForMarkup($page)->getValueOrDefault();
47004fd306cSNickeau                }
47104fd306cSNickeau            case MarkupRef::EMAIL_URI:
47204fd306cSNickeau                global $conf;
47304fd306cSNickeau                $email = $this->markupRef->getUrl()->getPath();
47404fd306cSNickeau                switch ($conf['mailguard']) {
47504fd306cSNickeau                    case 'none' :
47604fd306cSNickeau                        return $email;
47704fd306cSNickeau                    case 'visible' :
47804fd306cSNickeau                    default :
47904fd306cSNickeau                        $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
48004fd306cSNickeau                        return strtr($email, $obfuscate);
48104fd306cSNickeau                }
48204fd306cSNickeau            case MarkupRef::INTERWIKI_URI:
48304fd306cSNickeau                try {
48404fd306cSNickeau                    $path = $this->markupRef->getInterWiki()->toUrl()->getPath();
48504fd306cSNickeau                    if ($path[0] === "/") {
48604fd306cSNickeau                        return substr($path, 1);
48704fd306cSNickeau                    } else {
48804fd306cSNickeau                        return $path;
48904fd306cSNickeau                    }
49004fd306cSNickeau                } catch (ExceptionBadSyntax|ExceptionNotFound $e) {
49104fd306cSNickeau                    return "interwiki";
49204fd306cSNickeau                }
49304fd306cSNickeau            case MarkupRef::LOCAL_URI:
49404fd306cSNickeau                return $this->markupRef->getUrl()->getFragment();
49504fd306cSNickeau            default:
49604fd306cSNickeau                return $this->markupRef->getRef();
49704fd306cSNickeau        }
49804fd306cSNickeau    }
49904fd306cSNickeau
50004fd306cSNickeau
50104fd306cSNickeau    private
50204fd306cSNickeau    function obfuscateEmail($email, $inAttribute = true): string
50304fd306cSNickeau    {
50404fd306cSNickeau        /**
50504fd306cSNickeau         * adapted from {@link obfuscate()} in common.php
50604fd306cSNickeau         */
50704fd306cSNickeau        global $conf;
50804fd306cSNickeau
50904fd306cSNickeau        $mailGuard = $conf['mailguard'];
51004fd306cSNickeau        if ($mailGuard === "hex" && $inAttribute) {
51104fd306cSNickeau            $mailGuard = "visible";
51204fd306cSNickeau        }
51304fd306cSNickeau        switch ($mailGuard) {
51404fd306cSNickeau            case 'visible' :
51504fd306cSNickeau                $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
51604fd306cSNickeau                return strtr($email, $obfuscate);
51704fd306cSNickeau
51804fd306cSNickeau            case 'hex' :
51904fd306cSNickeau                return Conversion::toHtml($email, true);
52004fd306cSNickeau
52104fd306cSNickeau            case 'none' :
52204fd306cSNickeau            default :
52304fd306cSNickeau                return $email;
52404fd306cSNickeau        }
52504fd306cSNickeau    }
52604fd306cSNickeau
52704fd306cSNickeau
52804fd306cSNickeau    /**
52904fd306cSNickeau     * @return bool
53004fd306cSNickeau     * @deprecated should not be here ref does not have the notion of relative
53104fd306cSNickeau     */
53204fd306cSNickeau    public
53304fd306cSNickeau    function isRelative(): bool
53404fd306cSNickeau    {
53504fd306cSNickeau        return strpos($this->getMarkupRef()->getRef(), WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) !== 0;
53604fd306cSNickeau    }
53704fd306cSNickeau
53804fd306cSNickeau    public
53904fd306cSNickeau    function getMarkupRef(): MarkupRef
54004fd306cSNickeau    {
54104fd306cSNickeau        return $this->markupRef;
54204fd306cSNickeau    }
54304fd306cSNickeau
54404fd306cSNickeau
54504fd306cSNickeau    public
54604fd306cSNickeau    static function getHtmlClassInternalLink(): string
54704fd306cSNickeau    {
54804fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
54904fd306cSNickeau        if ($oldClassName) {
55004fd306cSNickeau            return "wikilink1";
55104fd306cSNickeau        } else {
55204fd306cSNickeau            return "link-internal";
55304fd306cSNickeau        }
55404fd306cSNickeau    }
55504fd306cSNickeau
55604fd306cSNickeau    public
55704fd306cSNickeau    static function getHtmlClassEmailLink(): string
55804fd306cSNickeau    {
55904fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
56004fd306cSNickeau        if ($oldClassName) {
56104fd306cSNickeau            return "mail";
56204fd306cSNickeau        } else {
56304fd306cSNickeau            return "link-mail";
56404fd306cSNickeau        }
56504fd306cSNickeau    }
56604fd306cSNickeau
56704fd306cSNickeau    public static function getHtmlClassExternalLink(): string
56804fd306cSNickeau    {
56904fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
57004fd306cSNickeau        if ($oldClassName) {
57104fd306cSNickeau            return "urlextern";
57204fd306cSNickeau        } else {
57304fd306cSNickeau            return "link-external";
57404fd306cSNickeau        }
57504fd306cSNickeau    }
57604fd306cSNickeau
57704fd306cSNickeau//FYI: exist in dokuwiki is "wikilink1 but we let the control to the user
57804fd306cSNickeau    public
57904fd306cSNickeau    static function getHtmlClassNotExist(): string
58004fd306cSNickeau    {
58104fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
58204fd306cSNickeau        if ($oldClassName) {
58304fd306cSNickeau            return "wikilink2";
58404fd306cSNickeau        } else {
58504fd306cSNickeau            return self::TEXT_ERROR_CLASS;
58604fd306cSNickeau        }
58704fd306cSNickeau    }
58804fd306cSNickeau
58904fd306cSNickeau    public
59004fd306cSNickeau    function __toString()
59104fd306cSNickeau    {
59204fd306cSNickeau        return $this->getMarkupRef()->getRef();
59304fd306cSNickeau    }
59404fd306cSNickeau
59504fd306cSNickeau
59604fd306cSNickeau    /**
59704fd306cSNickeau     * @throws ExceptionNotFound
59804fd306cSNickeau     */
59904fd306cSNickeau    private
60004fd306cSNickeau    function getPage(): MarkupPath
60104fd306cSNickeau    {
60204fd306cSNickeau        return MarkupPath::createPageFromPathObject($this->getMarkupRef()->getPath());
60304fd306cSNickeau    }
60404fd306cSNickeau
60504fd306cSNickeau    /**
60604fd306cSNickeau     * Styling attribute
60704fd306cSNickeau     * may be passed via parameters
60804fd306cSNickeau     * for internal link
60904fd306cSNickeau     * We don't want the styling attribute
61004fd306cSNickeau     * in the URL
61104fd306cSNickeau     */
61204fd306cSNickeau    private
61304fd306cSNickeau    function collectStylingAttributeInUrl()
61404fd306cSNickeau    {
61504fd306cSNickeau
61604fd306cSNickeau
61704fd306cSNickeau        /**
61804fd306cSNickeau         * We will not overwrite the parameters if this is an dokuwiki
61904fd306cSNickeau         * action link (with the `do` property)
62004fd306cSNickeau         */
62104fd306cSNickeau        if ($this->markupRef->getUrl()->hasProperty("do")) {
62204fd306cSNickeau            return;
62304fd306cSNickeau        }
62404fd306cSNickeau
62504fd306cSNickeau        /**
62604fd306cSNickeau         * Add the attribute from the URL
62704fd306cSNickeau         * if this is not a `do`
62804fd306cSNickeau         */
62904fd306cSNickeau        switch ($this->markupRef->getSchemeType()) {
63004fd306cSNickeau            case MarkupRef::WIKI_URI:
63104fd306cSNickeau                foreach ($this->getMarkupRef()->getUrl()->getQueryProperties() as $key => $value) {
63204fd306cSNickeau                    if (!in_array($key, self::PROTECTED_URL_PROPERTY)) {
63304fd306cSNickeau                        $this->getMarkupRef()->getUrl()->deleteQueryParameter($key);
63404fd306cSNickeau                        if (!TagAttributes::isEmptyValue($value)) {
63504fd306cSNickeau                            $this->stylingAttributes->addComponentAttributeValue($key, $value);
63604fd306cSNickeau                        } else {
63704fd306cSNickeau                            $this->stylingAttributes->addEmptyComponentAttributeValue($key);
63804fd306cSNickeau                        }
63904fd306cSNickeau                    }
64004fd306cSNickeau                }
64104fd306cSNickeau                break;
64204fd306cSNickeau            case
64304fd306cSNickeau            MarkupRef::EMAIL_URI:
64404fd306cSNickeau                foreach ($this->getMarkupRef()->getUrl()->getQueryProperties() as $key => $value) {
64504fd306cSNickeau                    if (!in_array($key, self::EMAIL_VALID_PARAMETERS)) {
64604fd306cSNickeau                        $this->stylingAttributes->addComponentAttributeValue($key, $value);
64704fd306cSNickeau                    }
64804fd306cSNickeau                }
64904fd306cSNickeau                break;
65004fd306cSNickeau        }
65104fd306cSNickeau
65204fd306cSNickeau    }
65304fd306cSNickeau
65404fd306cSNickeau    /**
65504fd306cSNickeau     * @return TagAttributes - the unknown attributes in a url are collected as styling attributes if this not a do query
65604fd306cSNickeau     * by {@link LinkMarkup::collectStylingAttributeInUrl()}
65704fd306cSNickeau     */
65804fd306cSNickeau    public
65904fd306cSNickeau    function getStylingAttributes(): TagAttributes
66004fd306cSNickeau    {
66104fd306cSNickeau        return $this->stylingAttributes;
66204fd306cSNickeau    }
66304fd306cSNickeau
66404fd306cSNickeau
66504fd306cSNickeau}
666