xref: /plugin/combo/ComboStrap/MarkupRef.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
1*4cadd4f8SNickeau<?php
2*4cadd4f8SNickeau/**
3*4cadd4f8SNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved.
4*4cadd4f8SNickeau *
5*4cadd4f8SNickeau * This source code is licensed under the GPL license found in the
6*4cadd4f8SNickeau * COPYING  file in the root directory of this source tree.
7*4cadd4f8SNickeau *
8*4cadd4f8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
9*4cadd4f8SNickeau * @author   ComboStrap <support@combostrap.com>
10*4cadd4f8SNickeau *
11*4cadd4f8SNickeau */
12*4cadd4f8SNickeau
13*4cadd4f8SNickeaunamespace ComboStrap;
14*4cadd4f8SNickeau
15*4cadd4f8SNickeau
16*4cadd4f8SNickeauuse Doku_Renderer_metadata;
17*4cadd4f8SNickeauuse Doku_Renderer_xhtml;
18*4cadd4f8SNickeauuse dokuwiki\Extension\PluginTrait;
19*4cadd4f8SNickeauuse dokuwiki\Utf8\Conversion;
20*4cadd4f8SNickeauuse syntax_plugin_combo_tooltip;
21*4cadd4f8SNickeau
22*4cadd4f8SNickeaurequire_once(__DIR__ . '/PluginUtility.php');
23*4cadd4f8SNickeau
24*4cadd4f8SNickeau/**
25*4cadd4f8SNickeau *
26*4cadd4f8SNickeau * @package ComboStrap
27*4cadd4f8SNickeau *
28*4cadd4f8SNickeau * Parse the ref found in a markup link
29*4cadd4f8SNickeau * and return an XHTML compliant array
30*4cadd4f8SNickeau * with href, style, ... attributes
31*4cadd4f8SNickeau */
32*4cadd4f8SNickeauclass MarkupRef
33*4cadd4f8SNickeau{
34*4cadd4f8SNickeau
35*4cadd4f8SNickeau
36*4cadd4f8SNickeau    /**
37*4cadd4f8SNickeau     * Type of link
38*4cadd4f8SNickeau     */
39*4cadd4f8SNickeau    const INTERWIKI_URI = 'interwiki';
40*4cadd4f8SNickeau    const WINDOWS_SHARE_URI = 'windowsShare';
41*4cadd4f8SNickeau    const WEB_URI = 'external';
42*4cadd4f8SNickeau
43*4cadd4f8SNickeau    const EMAIL_URI = 'email';
44*4cadd4f8SNickeau    const LOCAL_URI = 'local';
45*4cadd4f8SNickeau    const WIKI_URI = 'internal';
46*4cadd4f8SNickeau    const VARIABLE_URI = 'internal_template';
47*4cadd4f8SNickeau
48*4cadd4f8SNickeau
49*4cadd4f8SNickeau    /**
50*4cadd4f8SNickeau     * Class added to the type of link
51*4cadd4f8SNickeau     * Class have styling rule conflict, they are by default not set
52*4cadd4f8SNickeau     * but this configuration permits to turn it back
53*4cadd4f8SNickeau     */
54*4cadd4f8SNickeau    const CONF_USE_DOKUWIKI_CLASS_NAME = "useDokuwikiLinkClassName";
55*4cadd4f8SNickeau    /**
56*4cadd4f8SNickeau     * This configuration will set for all internal link
57*4cadd4f8SNickeau     * the {@link MarkupRef::PREVIEW_ATTRIBUTE} preview attribute
58*4cadd4f8SNickeau     */
59*4cadd4f8SNickeau    const CONF_PREVIEW_LINK = "previewLink";
60*4cadd4f8SNickeau    const CONF_PREVIEW_LINK_DEFAULT = 0;
61*4cadd4f8SNickeau
62*4cadd4f8SNickeau
63*4cadd4f8SNickeau    const TEXT_ERROR_CLASS = "text-danger";
64*4cadd4f8SNickeau
65*4cadd4f8SNickeau    /**
66*4cadd4f8SNickeau     * The known parameters for an email url
67*4cadd4f8SNickeau     */
68*4cadd4f8SNickeau    const EMAIL_VALID_PARAMETERS = ["subject"];
69*4cadd4f8SNickeau
70*4cadd4f8SNickeau    /**
71*4cadd4f8SNickeau     * If set, it will show a page preview
72*4cadd4f8SNickeau     */
73*4cadd4f8SNickeau    const PREVIEW_ATTRIBUTE = "preview";
74*4cadd4f8SNickeau    const PREVIEW_TOOLTIP = "preview";
75*4cadd4f8SNickeau
76*4cadd4f8SNickeau    /**
77*4cadd4f8SNickeau     * Highlight Key
78*4cadd4f8SNickeau     * Adding this property to the internal query will highlight the words
79*4cadd4f8SNickeau     *
80*4cadd4f8SNickeau     * See {@link html_hilight}
81*4cadd4f8SNickeau     */
82*4cadd4f8SNickeau    const SEARCH_HIGHLIGHT_QUERY_PROPERTY = "s";
83*4cadd4f8SNickeau
84*4cadd4f8SNickeau
85*4cadd4f8SNickeau    /**
86*4cadd4f8SNickeau     * @var mixed
87*4cadd4f8SNickeau     */
88*4cadd4f8SNickeau    private $uriType;
89*4cadd4f8SNickeau    /**
90*4cadd4f8SNickeau     * @var mixed
91*4cadd4f8SNickeau     */
92*4cadd4f8SNickeau    private $ref;
93*4cadd4f8SNickeau
94*4cadd4f8SNickeau    /**
95*4cadd4f8SNickeau     * @var Page the internal linked page if the link is an internal one
96*4cadd4f8SNickeau     */
97*4cadd4f8SNickeau    private $linkedPage;
98*4cadd4f8SNickeau
99*4cadd4f8SNickeau    /**
100*4cadd4f8SNickeau     * @var string The value of the title attribute of an anchor
101*4cadd4f8SNickeau     */
102*4cadd4f8SNickeau    private $title;
103*4cadd4f8SNickeau
104*4cadd4f8SNickeau
105*4cadd4f8SNickeau    /**
106*4cadd4f8SNickeau     * The name of the wiki for an inter wiki link
107*4cadd4f8SNickeau     * @var string
108*4cadd4f8SNickeau     */
109*4cadd4f8SNickeau    private $wiki;
110*4cadd4f8SNickeau
111*4cadd4f8SNickeau
112*4cadd4f8SNickeau    /**
113*4cadd4f8SNickeau     *
114*4cadd4f8SNickeau     * @var false|string
115*4cadd4f8SNickeau     */
116*4cadd4f8SNickeau    private $schemeUri;
117*4cadd4f8SNickeau
118*4cadd4f8SNickeau    /**
119*4cadd4f8SNickeau     * The uri scheme that can be used inside a page
120*4cadd4f8SNickeau     * @var array
121*4cadd4f8SNickeau     */
122*4cadd4f8SNickeau    private $authorizedSchemes;
123*4cadd4f8SNickeau
124*4cadd4f8SNickeau
125*4cadd4f8SNickeau    /**
126*4cadd4f8SNickeau     * @var DokuwikiUrl
127*4cadd4f8SNickeau     */
128*4cadd4f8SNickeau    private $dokuwikiUrl;
129*4cadd4f8SNickeau    /**
130*4cadd4f8SNickeau     * @var array|string|null
131*4cadd4f8SNickeau     */
132*4cadd4f8SNickeau    private $type;
133*4cadd4f8SNickeau    /**
134*4cadd4f8SNickeau     * @var array
135*4cadd4f8SNickeau     */
136*4cadd4f8SNickeau    private $interwiki;
137*4cadd4f8SNickeau
138*4cadd4f8SNickeau    /**
139*4cadd4f8SNickeau     * Link constructor.
140*4cadd4f8SNickeau     * @param $ref
141*4cadd4f8SNickeau     */
142*4cadd4f8SNickeau    public function __construct($ref)
143*4cadd4f8SNickeau    {
144*4cadd4f8SNickeau
145*4cadd4f8SNickeau
146*4cadd4f8SNickeau        /**
147*4cadd4f8SNickeau         * Windows share link
148*4cadd4f8SNickeau         */
149*4cadd4f8SNickeau        if ($this->uriType == null) {
150*4cadd4f8SNickeau            if (preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u', $ref)) {
151*4cadd4f8SNickeau                $this->uriType = self::WINDOWS_SHARE_URI;
152*4cadd4f8SNickeau                $this->ref = $ref;
153*4cadd4f8SNickeau                return;
154*4cadd4f8SNickeau            }
155*4cadd4f8SNickeau        }
156*4cadd4f8SNickeau
157*4cadd4f8SNickeau        /**
158*4cadd4f8SNickeau         * URI like links section with query and fragment
159*4cadd4f8SNickeau         */
160*4cadd4f8SNickeau
161*4cadd4f8SNickeau        /**
162*4cadd4f8SNickeau         * Local
163*4cadd4f8SNickeau         */
164*4cadd4f8SNickeau        if ($this->uriType == null) {
165*4cadd4f8SNickeau            if (preg_match('!^#.+!', $ref)) {
166*4cadd4f8SNickeau                $this->uriType = self::LOCAL_URI;
167*4cadd4f8SNickeau                $this->ref = $ref;
168*4cadd4f8SNickeau            }
169*4cadd4f8SNickeau        }
170*4cadd4f8SNickeau
171*4cadd4f8SNickeau        /**
172*4cadd4f8SNickeau         * Email validation pattern
173*4cadd4f8SNickeau         * E-Mail (pattern below is defined in inc/mail.php)
174*4cadd4f8SNickeau         *
175*4cadd4f8SNickeau         * Example:
176*4cadd4f8SNickeau         * [[support@combostrap.com?subject=hallo]]
177*4cadd4f8SNickeau         * [[support@combostrap.com]]
178*4cadd4f8SNickeau         */
179*4cadd4f8SNickeau        if ($this->uriType == null) {
180*4cadd4f8SNickeau            $emailRfc2822 = "0-9a-zA-Z!#$%&'*+/=?^_`{|}~-";
181*4cadd4f8SNickeau            $emailPattern = '[' . $emailRfc2822 . ']+(?:\.[' . $emailRfc2822 . ']+)*@(?i:[0-9a-z][0-9a-z-]*\.)+(?i:[a-z]{2,63})';
182*4cadd4f8SNickeau            if (preg_match('<' . $emailPattern . '>', $ref)) {
183*4cadd4f8SNickeau                $this->uriType = self::EMAIL_URI;
184*4cadd4f8SNickeau                $this->ref = $ref;
185*4cadd4f8SNickeau                // we don't return. The query part is parsed afterwards
186*4cadd4f8SNickeau            }
187*4cadd4f8SNickeau        }
188*4cadd4f8SNickeau
189*4cadd4f8SNickeau
190*4cadd4f8SNickeau        /**
191*4cadd4f8SNickeau         * External (ie only https)
192*4cadd4f8SNickeau         */
193*4cadd4f8SNickeau        if ($this->uriType == null) {
194*4cadd4f8SNickeau            /**
195*4cadd4f8SNickeau             * Example: `https://`
196*4cadd4f8SNickeau             *
197*4cadd4f8SNickeau             * Other scheme are not yet recognized
198*4cadd4f8SNickeau             * because it can also be a wiki id
199*4cadd4f8SNickeau             * For instance, `mailto:` is also a valid page
200*4cadd4f8SNickeau             */
201*4cadd4f8SNickeau            if (preg_match('#^([a-z0-9\-\.+]+?)://#i', $ref)) {
202*4cadd4f8SNickeau                $this->uriType = self::WEB_URI;
203*4cadd4f8SNickeau                $this->schemeUri = strtolower(substr($ref, 0, strpos($ref, ":")));
204*4cadd4f8SNickeau                $this->ref = $ref;
205*4cadd4f8SNickeau            }
206*4cadd4f8SNickeau        }
207*4cadd4f8SNickeau
208*4cadd4f8SNickeau        /**
209*4cadd4f8SNickeau         * Interwiki ?
210*4cadd4f8SNickeau         */
211*4cadd4f8SNickeau        $refProcessing = $ref;
212*4cadd4f8SNickeau        if ($this->uriType == null) {
213*4cadd4f8SNickeau            $interwikiPosition = strpos($refProcessing, ">");
214*4cadd4f8SNickeau            if ($interwikiPosition !== false) {
215*4cadd4f8SNickeau                $this->wiki = strtolower(substr($refProcessing, 0, $interwikiPosition));
216*4cadd4f8SNickeau                $refProcessing = substr($refProcessing, $interwikiPosition + 1);
217*4cadd4f8SNickeau                $this->ref = $ref;
218*4cadd4f8SNickeau                $this->uriType = self::INTERWIKI_URI;
219*4cadd4f8SNickeau            }
220*4cadd4f8SNickeau        }
221*4cadd4f8SNickeau
222*4cadd4f8SNickeau        /**
223*4cadd4f8SNickeau         * Internal then
224*4cadd4f8SNickeau         */
225*4cadd4f8SNickeau        if ($this->uriType == null) {
226*4cadd4f8SNickeau            /**
227*4cadd4f8SNickeau             * It can be a link with a ref template
228*4cadd4f8SNickeau             */
229*4cadd4f8SNickeau            if (TemplateUtility::isVariable($ref)) {
230*4cadd4f8SNickeau                $this->uriType = self::VARIABLE_URI;
231*4cadd4f8SNickeau            } else {
232*4cadd4f8SNickeau                $this->uriType = self::WIKI_URI;
233*4cadd4f8SNickeau            }
234*4cadd4f8SNickeau            $this->ref = $ref;
235*4cadd4f8SNickeau        }
236*4cadd4f8SNickeau
237*4cadd4f8SNickeau
238*4cadd4f8SNickeau        /**
239*4cadd4f8SNickeau         * Url (called ref by dokuwiki)
240*4cadd4f8SNickeau         */
241*4cadd4f8SNickeau        $this->dokuwikiUrl = DokuwikiUrl::createFromUrl($refProcessing);
242*4cadd4f8SNickeau
243*4cadd4f8SNickeau
244*4cadd4f8SNickeau    }
245*4cadd4f8SNickeau
246*4cadd4f8SNickeau    public static function createFromPageId($id): MarkupRef
247*4cadd4f8SNickeau    {
248*4cadd4f8SNickeau        return new MarkupRef(":$id");
249*4cadd4f8SNickeau    }
250*4cadd4f8SNickeau
251*4cadd4f8SNickeau    public static function createFromRef(string $ref): MarkupRef
252*4cadd4f8SNickeau    {
253*4cadd4f8SNickeau        return new MarkupRef($ref);
254*4cadd4f8SNickeau    }
255*4cadd4f8SNickeau
256*4cadd4f8SNickeau
257*4cadd4f8SNickeau    /**
258*4cadd4f8SNickeau     * @param $uriType
259*4cadd4f8SNickeau     * @return $this
260*4cadd4f8SNickeau     */
261*4cadd4f8SNickeau    public function setUriType($uriType): MarkupRef
262*4cadd4f8SNickeau    {
263*4cadd4f8SNickeau        $this->uriType = $uriType;
264*4cadd4f8SNickeau        return $this;
265*4cadd4f8SNickeau    }
266*4cadd4f8SNickeau
267*4cadd4f8SNickeau
268*4cadd4f8SNickeau    /**
269*4cadd4f8SNickeau     *
270*4cadd4f8SNickeau     *
271*4cadd4f8SNickeau     *
272*4cadd4f8SNickeau     * @throws ExceptionCombo
273*4cadd4f8SNickeau     */
274*4cadd4f8SNickeau    public function toAttributes($logicalTag = \syntax_plugin_combo_link::TAG): TagAttributes
275*4cadd4f8SNickeau    {
276*4cadd4f8SNickeau
277*4cadd4f8SNickeau        $outputAttributes = TagAttributes::createEmpty($logicalTag);
278*4cadd4f8SNickeau
279*4cadd4f8SNickeau        $type = $this->getUriType();
280*4cadd4f8SNickeau
281*4cadd4f8SNickeau
282*4cadd4f8SNickeau        /**
283*4cadd4f8SNickeau         * Add the attribute from the URL
284*4cadd4f8SNickeau         * if this is not a `do`
285*4cadd4f8SNickeau         */
286*4cadd4f8SNickeau
287*4cadd4f8SNickeau        switch ($type) {
288*4cadd4f8SNickeau            case self::WIKI_URI:
289*4cadd4f8SNickeau                if (!$this->dokuwikiUrl->hasQueryParameter("do")) {
290*4cadd4f8SNickeau                    foreach ($this->getDokuwikiUrl()->getQueryParameters() as $key => $value) {
291*4cadd4f8SNickeau                        if ($key !== self::SEARCH_HIGHLIGHT_QUERY_PROPERTY) {
292*4cadd4f8SNickeau                            $outputAttributes->addComponentAttributeValue($key, $value);
293*4cadd4f8SNickeau                        }
294*4cadd4f8SNickeau                    }
295*4cadd4f8SNickeau                }
296*4cadd4f8SNickeau                break;
297*4cadd4f8SNickeau            case
298*4cadd4f8SNickeau            self::EMAIL_URI:
299*4cadd4f8SNickeau                foreach ($this->getDokuwikiUrl()->getQueryParameters() as $key => $value) {
300*4cadd4f8SNickeau                    if (!in_array($key, self::EMAIL_VALID_PARAMETERS)) {
301*4cadd4f8SNickeau                        $outputAttributes->addComponentAttributeValue($key, $value);
302*4cadd4f8SNickeau                    }
303*4cadd4f8SNickeau                }
304*4cadd4f8SNickeau                break;
305*4cadd4f8SNickeau        }
306*4cadd4f8SNickeau
307*4cadd4f8SNickeau
308*4cadd4f8SNickeau        global $conf;
309*4cadd4f8SNickeau
310*4cadd4f8SNickeau        /**
311*4cadd4f8SNickeau         * Get the url
312*4cadd4f8SNickeau         */
313*4cadd4f8SNickeau        $url = $this->getUrl();
314*4cadd4f8SNickeau        if (!empty($url)) {
315*4cadd4f8SNickeau            $outputAttributes->addOutputAttributeValue("href", $url);
316*4cadd4f8SNickeau        }
317*4cadd4f8SNickeau
318*4cadd4f8SNickeau
319*4cadd4f8SNickeau        /**
320*4cadd4f8SNickeau         * Processing by type
321*4cadd4f8SNickeau         */
322*4cadd4f8SNickeau        switch ($this->getUriType()) {
323*4cadd4f8SNickeau            case self::INTERWIKI_URI:
324*4cadd4f8SNickeau
325*4cadd4f8SNickeau                // normal link for the `this` wiki
326*4cadd4f8SNickeau                if ($this->getWiki() !== "this") {
327*4cadd4f8SNickeau                    PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot(self::INTERWIKI_URI);
328*4cadd4f8SNickeau                }
329*4cadd4f8SNickeau                /**
330*4cadd4f8SNickeau                 * Target
331*4cadd4f8SNickeau                 */
332*4cadd4f8SNickeau                $interWikiConf = $conf['target']['interwiki'];
333*4cadd4f8SNickeau                if (!empty($interWikiConf)) {
334*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue('target', $interWikiConf);
335*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue('rel', 'noopener');
336*4cadd4f8SNickeau                }
337*4cadd4f8SNickeau                $outputAttributes->addClassName(self::getHtmlClassInterWikiLink());
338*4cadd4f8SNickeau                $wikiClass = "iw_" . preg_replace('/[^_\-a-z0-9]+/i', '_', $this->getWiki());
339*4cadd4f8SNickeau                $outputAttributes->addClassName($wikiClass);
340*4cadd4f8SNickeau                if (!$this->wikiExists()) {
341*4cadd4f8SNickeau                    $outputAttributes->addClassName(self::getHtmlClassNotExist());
342*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue("rel", 'nofollow');
343*4cadd4f8SNickeau                }
344*4cadd4f8SNickeau
345*4cadd4f8SNickeau                break;
346*4cadd4f8SNickeau            case self::WIKI_URI:
347*4cadd4f8SNickeau                /**
348*4cadd4f8SNickeau                 * Derived from {@link Doku_Renderer_xhtml::internallink()}
349*4cadd4f8SNickeau                 */
350*4cadd4f8SNickeau                // https://www.dokuwiki.org/config:target
351*4cadd4f8SNickeau                $target = $conf['target']['wiki'];
352*4cadd4f8SNickeau                if (!empty($target)) {
353*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue('target', $target);
354*4cadd4f8SNickeau                }
355*4cadd4f8SNickeau                /**
356*4cadd4f8SNickeau                 * Internal Page
357*4cadd4f8SNickeau                 */
358*4cadd4f8SNickeau                $linkedPage = $this->getInternalPage();
359*4cadd4f8SNickeau                $outputAttributes->addOutputAttributeValue("data-wiki-id", $linkedPage->getDokuwikiId());
360*4cadd4f8SNickeau
361*4cadd4f8SNickeau
362*4cadd4f8SNickeau                if (!$linkedPage->exists()) {
363*4cadd4f8SNickeau
364*4cadd4f8SNickeau                    /**
365*4cadd4f8SNickeau                     * Red color
366*4cadd4f8SNickeau                     */
367*4cadd4f8SNickeau                    $outputAttributes->addClassName(self::getHtmlClassNotExist());
368*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue("rel", 'nofollow');
369*4cadd4f8SNickeau
370*4cadd4f8SNickeau                } else {
371*4cadd4f8SNickeau
372*4cadd4f8SNickeau                    /**
373*4cadd4f8SNickeau                     * Internal Link Class
374*4cadd4f8SNickeau                     */
375*4cadd4f8SNickeau                    $outputAttributes->addClassName(self::getHtmlClassInternalLink());
376*4cadd4f8SNickeau
377*4cadd4f8SNickeau                    /**
378*4cadd4f8SNickeau                     * Link Creation
379*4cadd4f8SNickeau                     * Do we need to set the title or the tooltip
380*4cadd4f8SNickeau                     * Processing variables
381*4cadd4f8SNickeau                     */
382*4cadd4f8SNickeau                    $acronym = "";
383*4cadd4f8SNickeau
384*4cadd4f8SNickeau                    /**
385*4cadd4f8SNickeau                     * Preview tooltip
386*4cadd4f8SNickeau                     */
387*4cadd4f8SNickeau                    $previewConfig = PluginUtility::getConfValue(self::CONF_PREVIEW_LINK, self::CONF_PREVIEW_LINK_DEFAULT);
388*4cadd4f8SNickeau                    $preview = $outputAttributes->getBooleanValueAndRemoveIfPresent(self::PREVIEW_ATTRIBUTE, $previewConfig);
389*4cadd4f8SNickeau                    if ($preview) {
390*4cadd4f8SNickeau                        Tooltip::addToolTipSnippetIfNeeded();
391*4cadd4f8SNickeau                        $tooltipHtml = <<<EOF
392*4cadd4f8SNickeau<h3>{$linkedPage->getNameOrDefault()}</h3>
393*4cadd4f8SNickeau<p>{$linkedPage->getDescriptionOrElseDokuWiki()}</p>
394*4cadd4f8SNickeauEOF;
395*4cadd4f8SNickeau                        $dataAttributeNamespace = Bootstrap::getDataNamespace();
396*4cadd4f8SNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-toggle", "tooltip");
397*4cadd4f8SNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-placement", "top");
398*4cadd4f8SNickeau                        $outputAttributes->addOutputAttributeValue("data{$dataAttributeNamespace}-html", "true");
399*4cadd4f8SNickeau                        $outputAttributes->addOutputAttributeValue("title", $tooltipHtml);
400*4cadd4f8SNickeau                    }
401*4cadd4f8SNickeau
402*4cadd4f8SNickeau                    /**
403*4cadd4f8SNickeau                     * Low quality Page
404*4cadd4f8SNickeau                     * (It has a higher priority than preview and
405*4cadd4f8SNickeau                     * the code comes then after)
406*4cadd4f8SNickeau                     */
407*4cadd4f8SNickeau                    $pageProtectionAcronym = strtolower(PageProtection::ACRONYM);
408*4cadd4f8SNickeau                    if ($linkedPage->isLowQualityPage()) {
409*4cadd4f8SNickeau
410*4cadd4f8SNickeau                        /**
411*4cadd4f8SNickeau                         * Add a class to style it differently
412*4cadd4f8SNickeau                         * (the acronym is added to the description, later)
413*4cadd4f8SNickeau                         */
414*4cadd4f8SNickeau                        $acronym = LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM;
415*4cadd4f8SNickeau                        $lowerCaseLowQualityAcronym = strtolower(LowQualityPage::LOW_QUALITY_PROTECTION_ACRONYM);
416*4cadd4f8SNickeau                        $outputAttributes->addClassName(LowQualityPage::CLASS_NAME . "-combo");
417*4cadd4f8SNickeau                        $snippetLowQualityPageId = $lowerCaseLowQualityAcronym;
418*4cadd4f8SNickeau                        PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot($snippetLowQualityPageId);
419*4cadd4f8SNickeau                        /**
420*4cadd4f8SNickeau                         * Note The protection does occur on Javascript level, not on the HTML
421*4cadd4f8SNickeau                         * because the created page is valid for a anonymous or logged-in user
422*4cadd4f8SNickeau                         * Javascript is controlling
423*4cadd4f8SNickeau                         */
424*4cadd4f8SNickeau                        if (LowQualityPage::isProtectionEnabled()) {
425*4cadd4f8SNickeau
426*4cadd4f8SNickeau                            $linkType = LowQualityPage::getLowQualityLinkType();
427*4cadd4f8SNickeau                            $outputAttributes->addOutputAttributeValue("data-$pageProtectionAcronym-link", $linkType);
428*4cadd4f8SNickeau                            $outputAttributes->addOutputAttributeValue("data-$pageProtectionAcronym-source", $lowerCaseLowQualityAcronym);
429*4cadd4f8SNickeau
430*4cadd4f8SNickeau                            /**
431*4cadd4f8SNickeau                             * Low Quality Page protection javascript is only for warning or login link
432*4cadd4f8SNickeau                             */
433*4cadd4f8SNickeau                            if (in_array($linkType, [PageProtection::PAGE_PROTECTION_LINK_WARNING, PageProtection::PAGE_PROTECTION_LINK_LOGIN])) {
434*4cadd4f8SNickeau                                PageProtection::addPageProtectionSnippet();
435*4cadd4f8SNickeau                            }
436*4cadd4f8SNickeau
437*4cadd4f8SNickeau                        }
438*4cadd4f8SNickeau                    }
439*4cadd4f8SNickeau
440*4cadd4f8SNickeau                    /**
441*4cadd4f8SNickeau                     * Late publication has a higher priority than
442*4cadd4f8SNickeau                     * the late publication and the is therefore after
443*4cadd4f8SNickeau                     * (In case this a low quality page late published)
444*4cadd4f8SNickeau                     */
445*4cadd4f8SNickeau                    if ($linkedPage->isLatePublication()) {
446*4cadd4f8SNickeau                        /**
447*4cadd4f8SNickeau                         * Add a class to style it differently if needed
448*4cadd4f8SNickeau                         */
449*4cadd4f8SNickeau                        $outputAttributes->addClassName(PagePublicationDate::LATE_PUBLICATION_CLASS_NAME . "-combo");
450*4cadd4f8SNickeau                        if (PagePublicationDate::isLatePublicationProtectionEnabled()) {
451*4cadd4f8SNickeau                            $acronym = PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM;
452*4cadd4f8SNickeau                            $lowerCaseLatePublicationAcronym = strtolower(PagePublicationDate::LATE_PUBLICATION_PROTECTION_ACRONYM);
453*4cadd4f8SNickeau                            $outputAttributes->addOutputAttributeValue("data-$pageProtectionAcronym-link", PageProtection::PAGE_PROTECTION_LINK_LOGIN);
454*4cadd4f8SNickeau                            $outputAttributes->addOutputAttributeValue("data-$pageProtectionAcronym-source", $lowerCaseLatePublicationAcronym);
455*4cadd4f8SNickeau                            PageProtection::addPageProtectionSnippet();
456*4cadd4f8SNickeau                        }
457*4cadd4f8SNickeau
458*4cadd4f8SNickeau                    }
459*4cadd4f8SNickeau
460*4cadd4f8SNickeau                    /**
461*4cadd4f8SNickeau                     * Title (ie tooltip vs title html attribute)
462*4cadd4f8SNickeau                     */
463*4cadd4f8SNickeau                    if (!$outputAttributes->hasAttribute("title")) {
464*4cadd4f8SNickeau
465*4cadd4f8SNickeau                        /**
466*4cadd4f8SNickeau                         * If this is not a link into the same page
467*4cadd4f8SNickeau                         */
468*4cadd4f8SNickeau                        if (!empty($this->getDokuwikiUrl()->getPath())) {
469*4cadd4f8SNickeau                            $description = $linkedPage->getDescriptionOrElseDokuWiki();
470*4cadd4f8SNickeau                            if (empty($description)) {
471*4cadd4f8SNickeau                                // Rare case
472*4cadd4f8SNickeau                                $description = $linkedPage->getH1OrDefault();
473*4cadd4f8SNickeau                            }
474*4cadd4f8SNickeau                            if (!empty($acronym)) {
475*4cadd4f8SNickeau                                $description = $description . " ($acronym)";
476*4cadd4f8SNickeau                            }
477*4cadd4f8SNickeau                            $outputAttributes->addOutputAttributeValue("title", $description);
478*4cadd4f8SNickeau                        }
479*4cadd4f8SNickeau
480*4cadd4f8SNickeau                    }
481*4cadd4f8SNickeau
482*4cadd4f8SNickeau                }
483*4cadd4f8SNickeau
484*4cadd4f8SNickeau                break;
485*4cadd4f8SNickeau
486*4cadd4f8SNickeau            case self::WINDOWS_SHARE_URI:
487*4cadd4f8SNickeau                // https://www.dokuwiki.org/config:target
488*4cadd4f8SNickeau                $windowsTarget = $conf['target']['windows'];
489*4cadd4f8SNickeau                if (!empty($windowsTarget)) {
490*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue('target', $windowsTarget);
491*4cadd4f8SNickeau                }
492*4cadd4f8SNickeau                $outputAttributes->addClassName("windows");
493*4cadd4f8SNickeau                break;
494*4cadd4f8SNickeau            case self::LOCAL_URI:
495*4cadd4f8SNickeau                break;
496*4cadd4f8SNickeau            case self::EMAIL_URI:
497*4cadd4f8SNickeau                $outputAttributes->addClassName(self::getHtmlClassEmailLink());
498*4cadd4f8SNickeau                break;
499*4cadd4f8SNickeau            case self::WEB_URI:
500*4cadd4f8SNickeau                if ($conf['relnofollow']) {
501*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue("rel", 'nofollow ugc');
502*4cadd4f8SNickeau                }
503*4cadd4f8SNickeau                // https://www.dokuwiki.org/config:target
504*4cadd4f8SNickeau                $externTarget = $conf['target']['extern'];
505*4cadd4f8SNickeau                if (!empty($externTarget)) {
506*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue('target', $externTarget);
507*4cadd4f8SNickeau                    $outputAttributes->addOutputAttributeValue("rel", 'noopener');
508*4cadd4f8SNickeau                }
509*4cadd4f8SNickeau                if ($this->type === null) {
510*4cadd4f8SNickeau                    /**
511*4cadd4f8SNickeau                     * Default class for default external link
512*4cadd4f8SNickeau                     * To not interfere with other external link style
513*4cadd4f8SNickeau                     * For instance, {@link \syntax_plugin_combo_share}
514*4cadd4f8SNickeau                     */
515*4cadd4f8SNickeau                    $outputAttributes->addClassName(self::getHtmlClassExternalLink());
516*4cadd4f8SNickeau                }
517*4cadd4f8SNickeau                break;
518*4cadd4f8SNickeau            default:
519*4cadd4f8SNickeau                /**
520*4cadd4f8SNickeau                 * May be any external link
521*4cadd4f8SNickeau                 * such as {@link \syntax_plugin_combo_share}
522*4cadd4f8SNickeau                 */
523*4cadd4f8SNickeau                break;
524*4cadd4f8SNickeau
525*4cadd4f8SNickeau        }
526*4cadd4f8SNickeau
527*4cadd4f8SNickeau        /**
528*4cadd4f8SNickeau         * An email URL and title
529*4cadd4f8SNickeau         * may be already encoded because of the vanguard configuration
530*4cadd4f8SNickeau         *
531*4cadd4f8SNickeau         * The url is not treated as an attribute
532*4cadd4f8SNickeau         * because the transformation function encodes the value
533*4cadd4f8SNickeau         * to mitigate XSS
534*4cadd4f8SNickeau         *
535*4cadd4f8SNickeau         */
536*4cadd4f8SNickeau        if ($this->getUriType() == self::EMAIL_URI) {
537*4cadd4f8SNickeau            $emailAddress = $this->obfuscateEmail($this->dokuwikiUrl->getPath());
538*4cadd4f8SNickeau            $outputAttributes->addOutputAttributeValue("title", $emailAddress);
539*4cadd4f8SNickeau        }
540*4cadd4f8SNickeau
541*4cadd4f8SNickeau        /**
542*4cadd4f8SNickeau         * Return
543*4cadd4f8SNickeau         */
544*4cadd4f8SNickeau        return $outputAttributes;
545*4cadd4f8SNickeau
546*4cadd4f8SNickeau
547*4cadd4f8SNickeau    }
548*4cadd4f8SNickeau
549*4cadd4f8SNickeau
550*4cadd4f8SNickeau    /**
551*4cadd4f8SNickeau     * Return the type of link from an ID
552*4cadd4f8SNickeau     *
553*4cadd4f8SNickeau     * @return string a `TYPE_xxx` constant
554*4cadd4f8SNickeau     */
555*4cadd4f8SNickeau    public
556*4cadd4f8SNickeau    function getUriType(): string
557*4cadd4f8SNickeau    {
558*4cadd4f8SNickeau        return $this->uriType;
559*4cadd4f8SNickeau    }
560*4cadd4f8SNickeau
561*4cadd4f8SNickeau
562*4cadd4f8SNickeau    /**
563*4cadd4f8SNickeau     * @return Page - the internal page or an error if the link is not an internal one
564*4cadd4f8SNickeau     */
565*4cadd4f8SNickeau    public
566*4cadd4f8SNickeau    function getInternalPage(): Page
567*4cadd4f8SNickeau    {
568*4cadd4f8SNickeau        if ($this->linkedPage == null) {
569*4cadd4f8SNickeau            if ($this->getUriType() == self::WIKI_URI) {
570*4cadd4f8SNickeau                // if there is no path, this is the actual page
571*4cadd4f8SNickeau                $pathOrId = $this->dokuwikiUrl->getPath();
572*4cadd4f8SNickeau
573*4cadd4f8SNickeau                $this->linkedPage = Page::createPageFromNonQualifiedPath($pathOrId);
574*4cadd4f8SNickeau
575*4cadd4f8SNickeau            } else {
576*4cadd4f8SNickeau                throw new \RuntimeException("You can't ask the internal page id from a link that is not an internal one");
577*4cadd4f8SNickeau            }
578*4cadd4f8SNickeau        }
579*4cadd4f8SNickeau        return $this->linkedPage;
580*4cadd4f8SNickeau    }
581*4cadd4f8SNickeau
582*4cadd4f8SNickeau    public
583*4cadd4f8SNickeau    function getRef()
584*4cadd4f8SNickeau    {
585*4cadd4f8SNickeau        return $this->ref;
586*4cadd4f8SNickeau    }
587*4cadd4f8SNickeau
588*4cadd4f8SNickeau    /**
589*4cadd4f8SNickeau     * The label inside the anchor tag if there is none
590*4cadd4f8SNickeau     * @param false $navigation
591*4cadd4f8SNickeau     * @return string|null
592*4cadd4f8SNickeau     */
593*4cadd4f8SNickeau    public function getLabel(bool $navigation = false): ?string
594*4cadd4f8SNickeau    {
595*4cadd4f8SNickeau
596*4cadd4f8SNickeau        switch ($this->getUriType()) {
597*4cadd4f8SNickeau            case self::WIKI_URI:
598*4cadd4f8SNickeau                if ($navigation) {
599*4cadd4f8SNickeau                    return $this->getInternalPage()->getNameOrDefault();
600*4cadd4f8SNickeau                } else {
601*4cadd4f8SNickeau                    return $this->getInternalPage()->getTitleOrDefault();
602*4cadd4f8SNickeau                }
603*4cadd4f8SNickeau
604*4cadd4f8SNickeau            case self::EMAIL_URI:
605*4cadd4f8SNickeau
606*4cadd4f8SNickeau                global $conf;
607*4cadd4f8SNickeau                $email = $this->dokuwikiUrl->getPath();
608*4cadd4f8SNickeau                switch ($conf['mailguard']) {
609*4cadd4f8SNickeau                    case 'none' :
610*4cadd4f8SNickeau                        return $email;
611*4cadd4f8SNickeau                    case 'visible' :
612*4cadd4f8SNickeau                    default :
613*4cadd4f8SNickeau                        $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
614*4cadd4f8SNickeau                        return strtr($email, $obfuscate);
615*4cadd4f8SNickeau                }
616*4cadd4f8SNickeau            case self::INTERWIKI_URI:
617*4cadd4f8SNickeau                return $this->dokuwikiUrl->getPath();
618*4cadd4f8SNickeau            case self::LOCAL_URI:
619*4cadd4f8SNickeau                return $this->dokuwikiUrl->getFragment();
620*4cadd4f8SNickeau            default:
621*4cadd4f8SNickeau                return $this->getRef();
622*4cadd4f8SNickeau        }
623*4cadd4f8SNickeau    }
624*4cadd4f8SNickeau
625*4cadd4f8SNickeau    /**
626*4cadd4f8SNickeau     * @param $title - the value of the title attribute of the anchor
627*4cadd4f8SNickeau     */
628*4cadd4f8SNickeau    public
629*4cadd4f8SNickeau    function setTitle($title)
630*4cadd4f8SNickeau    {
631*4cadd4f8SNickeau        $this->title = $title;
632*4cadd4f8SNickeau    }
633*4cadd4f8SNickeau
634*4cadd4f8SNickeau
635*4cadd4f8SNickeau    /**
636*4cadd4f8SNickeau     * @throws ExceptionCombo
637*4cadd4f8SNickeau     * @var string $targetEnvironmentAmpersand
638*4cadd4f8SNickeau     * By default, all data are encoded
639*4cadd4f8SNickeau     * at {@link TagAttributes::encodeToHtmlValue()}
640*4cadd4f8SNickeau     * therefore the default is non-encoded
641*4cadd4f8SNickeau     *
642*4cadd4f8SNickeau     */
643*4cadd4f8SNickeau    public function getUrl()
644*4cadd4f8SNickeau    {
645*4cadd4f8SNickeau
646*4cadd4f8SNickeau        switch ($this->getUriType()) {
647*4cadd4f8SNickeau            case self::WIKI_URI:
648*4cadd4f8SNickeau                $page = $this->getInternalPage();
649*4cadd4f8SNickeau
650*4cadd4f8SNickeau                /**
651*4cadd4f8SNickeau                 * Styling attribute
652*4cadd4f8SNickeau                 * may be passed via parameters
653*4cadd4f8SNickeau                 * for internal link
654*4cadd4f8SNickeau                 * We don't want the styling attribute
655*4cadd4f8SNickeau                 * in the URL
656*4cadd4f8SNickeau                 *
657*4cadd4f8SNickeau                 * We will not overwrite the parameters if this is an dokuwiki
658*4cadd4f8SNickeau                 * action link (with the `do` property)
659*4cadd4f8SNickeau                 */
660*4cadd4f8SNickeau                if ($this->dokuwikiUrl->hasQueryParameter("do")) {
661*4cadd4f8SNickeau
662*4cadd4f8SNickeau                    $absoluteUrl = Site::shouldUrlBeAbsolute();
663*4cadd4f8SNickeau                    $url = wl(
664*4cadd4f8SNickeau                        $page->getDokuwikiId(),
665*4cadd4f8SNickeau                        $this->dokuwikiUrl->getQueryParameters(),
666*4cadd4f8SNickeau                        $absoluteUrl
667*4cadd4f8SNickeau                    );
668*4cadd4f8SNickeau
669*4cadd4f8SNickeau                } else {
670*4cadd4f8SNickeau
671*4cadd4f8SNickeau                    /**
672*4cadd4f8SNickeau                     * No parameters by default known
673*4cadd4f8SNickeau                     */
674*4cadd4f8SNickeau                    $url = $page->getCanonicalUrl(
675*4cadd4f8SNickeau                        [],
676*4cadd4f8SNickeau                        false
677*4cadd4f8SNickeau                    );
678*4cadd4f8SNickeau
679*4cadd4f8SNickeau                    /**
680*4cadd4f8SNickeau                     * The search term
681*4cadd4f8SNickeau                     * Code adapted found at {@link Doku_Renderer_xhtml::internallink()}
682*4cadd4f8SNickeau                     * We can't use the previous {@link wl function}
683*4cadd4f8SNickeau                     * because it encode too much
684*4cadd4f8SNickeau                     */
685*4cadd4f8SNickeau                    $searchTerms = $this->dokuwikiUrl->getQueryParameter(self::SEARCH_HIGHLIGHT_QUERY_PROPERTY);
686*4cadd4f8SNickeau                    if ($searchTerms !== null) {
687*4cadd4f8SNickeau                        $url .= DokuwikiUrl::AMPERSAND_CHARACTER;
688*4cadd4f8SNickeau                        PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("search-hit");
689*4cadd4f8SNickeau                        if (is_array($searchTerms)) {
690*4cadd4f8SNickeau                            /**
691*4cadd4f8SNickeau                             * To verify, do we really need the []
692*4cadd4f8SNickeau                             * to get an array in php ?
693*4cadd4f8SNickeau                             */
694*4cadd4f8SNickeau                            $searchTermsQuery = [];
695*4cadd4f8SNickeau                            foreach ($searchTerms as $searchTerm) {
696*4cadd4f8SNickeau                                $searchTermsQuery[] = "s[]=$searchTerm";
697*4cadd4f8SNickeau                            }
698*4cadd4f8SNickeau                            $url .= implode(DokuwikiUrl::AMPERSAND_CHARACTER, $searchTermsQuery);
699*4cadd4f8SNickeau                        } else {
700*4cadd4f8SNickeau                            $url .= "s=$searchTerms";
701*4cadd4f8SNickeau                        }
702*4cadd4f8SNickeau                    }
703*4cadd4f8SNickeau
704*4cadd4f8SNickeau
705*4cadd4f8SNickeau                }
706*4cadd4f8SNickeau                if ($this->dokuwikiUrl->getFragment() != null) {
707*4cadd4f8SNickeau                    /**
708*4cadd4f8SNickeau                     * pageutils (transform a fragment in section id)
709*4cadd4f8SNickeau                     */
710*4cadd4f8SNickeau                    $check = false;
711*4cadd4f8SNickeau                    $url .= '#' . sectionID($this->dokuwikiUrl->getFragment(), $check);
712*4cadd4f8SNickeau                }
713*4cadd4f8SNickeau                break;
714*4cadd4f8SNickeau            case self::INTERWIKI_URI:
715*4cadd4f8SNickeau                $wiki = $this->wiki;
716*4cadd4f8SNickeau                $extendedPath = $this->dokuwikiUrl->getPath();
717*4cadd4f8SNickeau                if ($this->dokuwikiUrl->getFragment() !== null) {
718*4cadd4f8SNickeau                    $extendedPath .= "#{$this->dokuwikiUrl->getFragment()}";
719*4cadd4f8SNickeau                }
720*4cadd4f8SNickeau                $url = $this->interWikiRefToUrl($wiki, $extendedPath);
721*4cadd4f8SNickeau                break;
722*4cadd4f8SNickeau            case self::WINDOWS_SHARE_URI:
723*4cadd4f8SNickeau                $url = str_replace('\\', '/', $this->getRef());
724*4cadd4f8SNickeau                $url = 'file:///' . $url;
725*4cadd4f8SNickeau                break;
726*4cadd4f8SNickeau            case self::EMAIL_URI:
727*4cadd4f8SNickeau                /**
728*4cadd4f8SNickeau                 * An email link is `<email>`
729*4cadd4f8SNickeau                 * {@link Emaillink::connectTo()}
730*4cadd4f8SNickeau                 * or
731*4cadd4f8SNickeau                 * {@link PluginTrait::email()
732*4cadd4f8SNickeau                 */
733*4cadd4f8SNickeau                // common.php#obfsucate implements the $conf['mailguard']
734*4cadd4f8SNickeau                $uri = $this->getDokuwikiUrl()->getPath();
735*4cadd4f8SNickeau                $uri = $this->obfuscateEmail($uri);
736*4cadd4f8SNickeau                $uri = urlencode($uri);
737*4cadd4f8SNickeau                $queryParameters = $this->getDokuwikiUrl()->getQueryParameters();
738*4cadd4f8SNickeau                if (sizeof($queryParameters) > 0) {
739*4cadd4f8SNickeau                    $uri .= "?";
740*4cadd4f8SNickeau                    foreach ($queryParameters as $key => $value) {
741*4cadd4f8SNickeau                        $value = urlencode($value);
742*4cadd4f8SNickeau                        $key = urlencode($key);
743*4cadd4f8SNickeau                        if (in_array($key, self::EMAIL_VALID_PARAMETERS)) {
744*4cadd4f8SNickeau                            $uri .= "$key=$value";
745*4cadd4f8SNickeau                        }
746*4cadd4f8SNickeau                    }
747*4cadd4f8SNickeau                }
748*4cadd4f8SNickeau                $url = 'mailto:' . $uri;
749*4cadd4f8SNickeau                break;
750*4cadd4f8SNickeau            case self::LOCAL_URI:
751*4cadd4f8SNickeau                $check = false;
752*4cadd4f8SNickeau                $url = '#' . sectionID($this->ref, $check);
753*4cadd4f8SNickeau                break;
754*4cadd4f8SNickeau            case self::WEB_URI:
755*4cadd4f8SNickeau                /**
756*4cadd4f8SNickeau                 * Default is external
757*4cadd4f8SNickeau                 * For instance, {@link \syntax_plugin_combo_share} link
758*4cadd4f8SNickeau                 */
759*4cadd4f8SNickeau                /**
760*4cadd4f8SNickeau                 * Authorized scheme only
761*4cadd4f8SNickeau                 * to not inject code
762*4cadd4f8SNickeau                 */
763*4cadd4f8SNickeau                if (is_null($this->authorizedSchemes)) {
764*4cadd4f8SNickeau                    // https://www.dokuwiki.org/urlschemes
765*4cadd4f8SNickeau                    $this->authorizedSchemes = getSchemes();
766*4cadd4f8SNickeau                    $this->authorizedSchemes[] = "whatsapp";
767*4cadd4f8SNickeau                    $this->authorizedSchemes[] = "mailto";
768*4cadd4f8SNickeau                }
769*4cadd4f8SNickeau                if (!in_array($this->schemeUri, $this->authorizedSchemes)) {
770*4cadd4f8SNickeau                    throw new ExceptionCombo("The scheme ($this->schemeUri) is not authorized as uri");
771*4cadd4f8SNickeau                } else {
772*4cadd4f8SNickeau                    $url = $this->ref;
773*4cadd4f8SNickeau                }
774*4cadd4f8SNickeau                break;
775*4cadd4f8SNickeau            case self::VARIABLE_URI:
776*4cadd4f8SNickeau                throw new ExceptionCombo("A template variable uri ($this->ref) can not give back an url, it should be first be replaced");
777*4cadd4f8SNickeau            default:
778*4cadd4f8SNickeau                throw new ExceptionCombo("The structure of the reference ($this->ref) is unknown");
779*4cadd4f8SNickeau        }
780*4cadd4f8SNickeau
781*4cadd4f8SNickeau
782*4cadd4f8SNickeau        return $url;
783*4cadd4f8SNickeau    }
784*4cadd4f8SNickeau
785*4cadd4f8SNickeau    public function getWiki(): ?string
786*4cadd4f8SNickeau    {
787*4cadd4f8SNickeau        return $this->wiki;
788*4cadd4f8SNickeau    }
789*4cadd4f8SNickeau
790*4cadd4f8SNickeau
791*4cadd4f8SNickeau    public
792*4cadd4f8SNickeau    function getScheme()
793*4cadd4f8SNickeau    {
794*4cadd4f8SNickeau        return $this->schemeUri;
795*4cadd4f8SNickeau    }
796*4cadd4f8SNickeau
797*4cadd4f8SNickeau
798*4cadd4f8SNickeau    private
799*4cadd4f8SNickeau    function wikiExists(): bool
800*4cadd4f8SNickeau    {
801*4cadd4f8SNickeau        $wikis = getInterwiki();
802*4cadd4f8SNickeau        return key_exists($this->wiki, $wikis);
803*4cadd4f8SNickeau    }
804*4cadd4f8SNickeau
805*4cadd4f8SNickeau    private
806*4cadd4f8SNickeau    function obfuscateEmail($email, $inAttribute = true): string
807*4cadd4f8SNickeau    {
808*4cadd4f8SNickeau        /**
809*4cadd4f8SNickeau         * adapted from {@link obfuscate()} in common.php
810*4cadd4f8SNickeau         */
811*4cadd4f8SNickeau        global $conf;
812*4cadd4f8SNickeau
813*4cadd4f8SNickeau        $mailGuard = $conf['mailguard'];
814*4cadd4f8SNickeau        if ($mailGuard === "hex" && $inAttribute) {
815*4cadd4f8SNickeau            $mailGuard = "visible";
816*4cadd4f8SNickeau        }
817*4cadd4f8SNickeau        switch ($mailGuard) {
818*4cadd4f8SNickeau            case 'visible' :
819*4cadd4f8SNickeau                $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
820*4cadd4f8SNickeau                return strtr($email, $obfuscate);
821*4cadd4f8SNickeau
822*4cadd4f8SNickeau            case 'hex' :
823*4cadd4f8SNickeau                return Conversion::toHtml($email, true);
824*4cadd4f8SNickeau
825*4cadd4f8SNickeau            case 'none' :
826*4cadd4f8SNickeau            default :
827*4cadd4f8SNickeau                return $email;
828*4cadd4f8SNickeau        }
829*4cadd4f8SNickeau    }
830*4cadd4f8SNickeau
831*4cadd4f8SNickeau
832*4cadd4f8SNickeau    public
833*4cadd4f8SNickeau    function isRelative(): bool
834*4cadd4f8SNickeau    {
835*4cadd4f8SNickeau        return strpos($this->path, ':') !== 0;
836*4cadd4f8SNickeau    }
837*4cadd4f8SNickeau
838*4cadd4f8SNickeau    public
839*4cadd4f8SNickeau    function getDokuwikiUrl(): DokuwikiUrl
840*4cadd4f8SNickeau    {
841*4cadd4f8SNickeau        return $this->dokuwikiUrl;
842*4cadd4f8SNickeau    }
843*4cadd4f8SNickeau
844*4cadd4f8SNickeau
845*4cadd4f8SNickeau    public
846*4cadd4f8SNickeau    static function getHtmlClassInternalLink(): string
847*4cadd4f8SNickeau    {
848*4cadd4f8SNickeau        $oldClassName = PluginUtility::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
849*4cadd4f8SNickeau        if ($oldClassName) {
850*4cadd4f8SNickeau            return "wikilink1";
851*4cadd4f8SNickeau        } else {
852*4cadd4f8SNickeau            return "link-internal";
853*4cadd4f8SNickeau        }
854*4cadd4f8SNickeau    }
855*4cadd4f8SNickeau
856*4cadd4f8SNickeau    public
857*4cadd4f8SNickeau    static function getHtmlClassEmailLink(): string
858*4cadd4f8SNickeau    {
859*4cadd4f8SNickeau        $oldClassName = PluginUtility::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
860*4cadd4f8SNickeau        if ($oldClassName) {
861*4cadd4f8SNickeau            return "mail";
862*4cadd4f8SNickeau        } else {
863*4cadd4f8SNickeau            return "link-mail";
864*4cadd4f8SNickeau        }
865*4cadd4f8SNickeau    }
866*4cadd4f8SNickeau
867*4cadd4f8SNickeau    public
868*4cadd4f8SNickeau    static function getHtmlClassInterWikiLink(): string
869*4cadd4f8SNickeau    {
870*4cadd4f8SNickeau        $oldClassName = PluginUtility::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
871*4cadd4f8SNickeau        if ($oldClassName) {
872*4cadd4f8SNickeau            return "interwiki";
873*4cadd4f8SNickeau        } else {
874*4cadd4f8SNickeau            return "link-interwiki";
875*4cadd4f8SNickeau        }
876*4cadd4f8SNickeau    }
877*4cadd4f8SNickeau
878*4cadd4f8SNickeau    public
879*4cadd4f8SNickeau    static function getHtmlClassExternalLink(): string
880*4cadd4f8SNickeau    {
881*4cadd4f8SNickeau        $oldClassName = PluginUtility::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
882*4cadd4f8SNickeau        if ($oldClassName) {
883*4cadd4f8SNickeau            return "urlextern";
884*4cadd4f8SNickeau        } else {
885*4cadd4f8SNickeau            return "link-external";
886*4cadd4f8SNickeau        }
887*4cadd4f8SNickeau    }
888*4cadd4f8SNickeau
889*4cadd4f8SNickeau//FYI: exist in dokuwiki is "wikilink1 but we let the control to the user
890*4cadd4f8SNickeau    public
891*4cadd4f8SNickeau    static function getHtmlClassNotExist(): string
892*4cadd4f8SNickeau    {
893*4cadd4f8SNickeau        $oldClassName = PluginUtility::getConfValue(self::CONF_USE_DOKUWIKI_CLASS_NAME);
894*4cadd4f8SNickeau        if ($oldClassName) {
895*4cadd4f8SNickeau            return "wikilink2";
896*4cadd4f8SNickeau        } else {
897*4cadd4f8SNickeau            return self::TEXT_ERROR_CLASS;
898*4cadd4f8SNickeau        }
899*4cadd4f8SNickeau    }
900*4cadd4f8SNickeau
901*4cadd4f8SNickeau    public
902*4cadd4f8SNickeau    function __toString()
903*4cadd4f8SNickeau    {
904*4cadd4f8SNickeau        return $this->ref;
905*4cadd4f8SNickeau    }
906*4cadd4f8SNickeau
907*4cadd4f8SNickeau    private
908*4cadd4f8SNickeau    function getEmailObfuscationConfiguration()
909*4cadd4f8SNickeau    {
910*4cadd4f8SNickeau        global $conf;
911*4cadd4f8SNickeau        return $conf['mailguard'];
912*4cadd4f8SNickeau    }
913*4cadd4f8SNickeau
914*4cadd4f8SNickeau    /**
915*4cadd4f8SNickeau     * @param string $shortcut
916*4cadd4f8SNickeau     * @param string $reference
917*4cadd4f8SNickeau     * @return mixed|string
918*4cadd4f8SNickeau     * Adapted  from {@link Doku_Renderer_xhtml::_resolveInterWiki()}
919*4cadd4f8SNickeau     * @noinspection DuplicatedCode
920*4cadd4f8SNickeau     */
921*4cadd4f8SNickeau    private function interWikiRefToUrl(string &$shortcut, string $reference)
922*4cadd4f8SNickeau    {
923*4cadd4f8SNickeau
924*4cadd4f8SNickeau        if ($this->interwiki === null) {
925*4cadd4f8SNickeau            $this->interwiki = getInterwiki();
926*4cadd4f8SNickeau        }
927*4cadd4f8SNickeau
928*4cadd4f8SNickeau        // Get interwiki URL
929*4cadd4f8SNickeau        if (isset($this->interwiki[$shortcut])) {
930*4cadd4f8SNickeau            $url = $this->interwiki[$shortcut];
931*4cadd4f8SNickeau        } elseif (isset($this->interwiki['default'])) {
932*4cadd4f8SNickeau            $shortcut = 'default';
933*4cadd4f8SNickeau            $url = $this->interwiki[$shortcut];
934*4cadd4f8SNickeau        } else {
935*4cadd4f8SNickeau            // not parsable interwiki outputs '' to make sure string manipulation works
936*4cadd4f8SNickeau            $shortcut = '';
937*4cadd4f8SNickeau            $url = '';
938*4cadd4f8SNickeau        }
939*4cadd4f8SNickeau
940*4cadd4f8SNickeau        //split into hash and url part
941*4cadd4f8SNickeau        $hash = strrchr($reference, '#');
942*4cadd4f8SNickeau        if ($hash) {
943*4cadd4f8SNickeau            $reference = substr($reference, 0, -strlen($hash));
944*4cadd4f8SNickeau            $hash = substr($hash, 1);
945*4cadd4f8SNickeau        }
946*4cadd4f8SNickeau
947*4cadd4f8SNickeau        //replace placeholder
948*4cadd4f8SNickeau        if (preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) {
949*4cadd4f8SNickeau            //use placeholders
950*4cadd4f8SNickeau            $url = str_replace('{URL}', rawurlencode($reference), $url);
951*4cadd4f8SNickeau            //wiki names will be cleaned next, otherwise urlencode unsafe chars
952*4cadd4f8SNickeau            $url = str_replace('{NAME}', ($url[0] === ':') ? $reference :
953*4cadd4f8SNickeau                preg_replace_callback('/[[\\\\\]^`{|}#%]/', function ($match) {
954*4cadd4f8SNickeau                    return rawurlencode($match[0]);
955*4cadd4f8SNickeau                }, $reference), $url);
956*4cadd4f8SNickeau            $parsed = parse_url($reference);
957*4cadd4f8SNickeau            if (empty($parsed['scheme'])) $parsed['scheme'] = '';
958*4cadd4f8SNickeau            if (empty($parsed['host'])) $parsed['host'] = '';
959*4cadd4f8SNickeau            if (empty($parsed['port'])) $parsed['port'] = 80;
960*4cadd4f8SNickeau            if (empty($parsed['path'])) $parsed['path'] = '';
961*4cadd4f8SNickeau            if (empty($parsed['query'])) $parsed['query'] = '';
962*4cadd4f8SNickeau            $url = strtr($url, [
963*4cadd4f8SNickeau                '{SCHEME}' => $parsed['scheme'],
964*4cadd4f8SNickeau                '{HOST}' => $parsed['host'],
965*4cadd4f8SNickeau                '{PORT}' => $parsed['port'],
966*4cadd4f8SNickeau                '{PATH}' => $parsed['path'],
967*4cadd4f8SNickeau                '{QUERY}' => $parsed['query'],
968*4cadd4f8SNickeau            ]);
969*4cadd4f8SNickeau        } else if ($url != '') {
970*4cadd4f8SNickeau            // make sure when no url is defined, we keep it null
971*4cadd4f8SNickeau            // default
972*4cadd4f8SNickeau            $url = $url . rawurlencode($reference);
973*4cadd4f8SNickeau        }
974*4cadd4f8SNickeau        //handle as wiki links
975*4cadd4f8SNickeau        if ($url[0] === ':') {
976*4cadd4f8SNickeau            $urlParam = null;
977*4cadd4f8SNickeau            $id = $url;
978*4cadd4f8SNickeau            if (strpos($url, '?') !== false) {
979*4cadd4f8SNickeau                list($id, $urlParam) = explode('?', $url, 2);
980*4cadd4f8SNickeau            }
981*4cadd4f8SNickeau            $url = wl(cleanID($id), $urlParam);
982*4cadd4f8SNickeau            $exists = page_exists($id);
983*4cadd4f8SNickeau        }
984*4cadd4f8SNickeau        if ($hash) $url .= '#' . rawurlencode($hash);
985*4cadd4f8SNickeau
986*4cadd4f8SNickeau        return $url;
987*4cadd4f8SNickeau    }
988*4cadd4f8SNickeau
989*4cadd4f8SNickeau
990*4cadd4f8SNickeau}
991