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