xref: /plugin/combo/ComboStrap/InterWiki.php (revision 191745f71da859a90b0e61a0366cb9f311012f21)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeaunamespace ComboStrap;
404fd306cSNickeau
504fd306cSNickeauuse ComboStrap\Web\Url;
604fd306cSNickeau
704fd306cSNickeauclass InterWiki
804fd306cSNickeau{
904fd306cSNickeau
1004fd306cSNickeau
1104fd306cSNickeau    const DEFAULT_INTERWIKI_NAME = 'default';
1204fd306cSNickeau
1304fd306cSNickeau    /**
1404fd306cSNickeau     * The pattern that select the characters to encode in URL
1504fd306cSNickeau     */
1604fd306cSNickeau    const CHARACTERS_TO_ENCODE = '/[[\\\\\]^`{|}#%]/';
1704fd306cSNickeau    const IW_PREFIX = "iw_";
1804fd306cSNickeau
1904fd306cSNickeau
2004fd306cSNickeau    private static ?array $INTERWIKI_URL_TEMPLATES = null;
2104fd306cSNickeau
2204fd306cSNickeau    private string $name;
2304fd306cSNickeau    private string $ref;
2404fd306cSNickeau
2504fd306cSNickeau    private ?string $urlWithoutFragment = null;
2604fd306cSNickeau
2704fd306cSNickeau    private ?string $fragment = null;
2804fd306cSNickeau
2904fd306cSNickeau    private string $markupType;
3004fd306cSNickeau
3104fd306cSNickeau    /**
3204fd306cSNickeau     * @param string $interWikiRef - The interwiki
3304fd306cSNickeau     * @param string $markupType - The {@link MarkupRef::getSchemeType()} ie media or link
3404fd306cSNickeau     */
3504fd306cSNickeau    public function __construct(string $interWikiRef, string $markupType)
3604fd306cSNickeau    {
3704fd306cSNickeau
3804fd306cSNickeau        $this->ref = $interWikiRef;
3904fd306cSNickeau        $this->markupType = $markupType;
4004fd306cSNickeau        [$this->name, $this->urlWithoutFragment] = explode(">", $interWikiRef, 2);
4104fd306cSNickeau
42*191745f7SNico        # The name is a key that should be lowercase
43*191745f7SNico        $this->name = strtolower($this->name);
4404fd306cSNickeau
4504fd306cSNickeau        $hash = strrchr($this->urlWithoutFragment, '#');
4604fd306cSNickeau        if ($hash) {
4704fd306cSNickeau            $this->urlWithoutFragment = substr($this->urlWithoutFragment, 0, -strlen($hash));
4804fd306cSNickeau            $this->fragment = substr($hash, 1);
4904fd306cSNickeau        }
5004fd306cSNickeau
5104fd306cSNickeau    }
5204fd306cSNickeau
5304fd306cSNickeau    public static function addInterWiki(string $name, string $value)
5404fd306cSNickeau    {
5504fd306cSNickeau        ExecutionContext::getActualOrCreateFromEnv()
5604fd306cSNickeau            ->getConfig()
5704fd306cSNickeau            ->addInterWiki($name, $value);
5804fd306cSNickeau
5904fd306cSNickeau    }
6004fd306cSNickeau
6104fd306cSNickeau
6204fd306cSNickeau    public static function createMediaInterWikiFromString(string $ref): InterWiki
6304fd306cSNickeau    {
6404fd306cSNickeau        return new InterWiki($ref, MarkupRef::MEDIA_TYPE);
6504fd306cSNickeau    }
6604fd306cSNickeau
6704fd306cSNickeau    /**
6804fd306cSNickeau     * @return string - the general component class
6904fd306cSNickeau     */
7004fd306cSNickeau    public static function getComponentClass(): string
7104fd306cSNickeau    {
7204fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(LinkMarkup::CONF_USE_DOKUWIKI_CLASS_NAME);
7304fd306cSNickeau        if ($oldClassName) {
7404fd306cSNickeau            return "interwiki";
7504fd306cSNickeau        } else {
7604fd306cSNickeau            return "link-interwiki";
7704fd306cSNickeau        }
7804fd306cSNickeau    }
7904fd306cSNickeau
8004fd306cSNickeau    /**
8104fd306cSNickeau     * @throws ExceptionNotFound
8204fd306cSNickeau     * @throws ExceptionBadSyntax
8304fd306cSNickeau     * @throws ExceptionBadArgument
8404fd306cSNickeau     * Adapted  from {@link Doku_Renderer_xhtml::_resolveInterWiki()}
8504fd306cSNickeau     */
8604fd306cSNickeau    public function toUrl(): Url
8704fd306cSNickeau    {
8804fd306cSNickeau
8904fd306cSNickeau        $originalInterWikiUrlTemplate = $this->getTemplateUrlStringOrDefault();
9004fd306cSNickeau        $interWikiUrlTemplate = $originalInterWikiUrlTemplate;
9104fd306cSNickeau
9204fd306cSNickeau        /**
9304fd306cSNickeau         * Dokuwiki Id template
9404fd306cSNickeau         */
9504fd306cSNickeau        if ($interWikiUrlTemplate[0] === ':') {
9604fd306cSNickeau            $interWikiUrlTemplate = str_replace(
9704fd306cSNickeau                '{NAME}',
9804fd306cSNickeau                $this->urlWithoutFragment,
9904fd306cSNickeau                $interWikiUrlTemplate
10004fd306cSNickeau            );
10104fd306cSNickeau            if ($this->fragment !== null) {
10204fd306cSNickeau                $interWikiUrlTemplate = "$interWikiUrlTemplate#$this->fragment";
10304fd306cSNickeau            }
10404fd306cSNickeau            switch ($this->markupType) {
10504fd306cSNickeau                case MarkupRef::MEDIA_TYPE:
10604fd306cSNickeau                    return MarkupRef::createMediaFromRef($interWikiUrlTemplate)->getUrl();
10704fd306cSNickeau                case MarkupRef::LINK_TYPE:
10804fd306cSNickeau                default:
10904fd306cSNickeau                    return MarkupRef::createLinkFromRef($interWikiUrlTemplate)->getUrl();
11004fd306cSNickeau            }
11104fd306cSNickeau
11204fd306cSNickeau        }
11304fd306cSNickeau
11404fd306cSNickeau        // Replace placeholder if any
11504fd306cSNickeau        if (preg_match('#{URL}#', $interWikiUrlTemplate)) {
11604fd306cSNickeau
11704fd306cSNickeau            // Replace the Url
11804fd306cSNickeau            $interWikiUrlTemplate = str_replace(
11904fd306cSNickeau                '{URL}',
12004fd306cSNickeau                rawurlencode($this->urlWithoutFragment),
12104fd306cSNickeau                $interWikiUrlTemplate
12204fd306cSNickeau            );
12304fd306cSNickeau
12404fd306cSNickeau        }
12504fd306cSNickeau
12604fd306cSNickeau        // Name placeholder means replace with URL encoding
12704fd306cSNickeau        if (preg_match('#{NAME}#', $interWikiUrlTemplate)) {
12804fd306cSNickeau
12904fd306cSNickeau            $interWikiUrlTemplate = str_replace(
13004fd306cSNickeau                '{NAME}',
13104fd306cSNickeau                preg_replace_callback(
13204fd306cSNickeau                    self::CHARACTERS_TO_ENCODE,
13304fd306cSNickeau                    function ($match) {
13404fd306cSNickeau                        return rawurlencode($match[0]);
13504fd306cSNickeau                    },
13604fd306cSNickeau                    $this->urlWithoutFragment
13704fd306cSNickeau                ),
13804fd306cSNickeau                $interWikiUrlTemplate
13904fd306cSNickeau            );
14004fd306cSNickeau
14104fd306cSNickeau        }
14204fd306cSNickeau
14304fd306cSNickeau        // Url replacement
14404fd306cSNickeau        if (preg_match('#{(SCHEME|HOST|PORT|PATH|QUERY)}#', $interWikiUrlTemplate)) {
14504fd306cSNickeau
14604fd306cSNickeau            $parsed = parse_url($this->urlWithoutFragment);
14704fd306cSNickeau            if (empty($parsed['scheme'])) $parsed['scheme'] = '';
14804fd306cSNickeau            if (empty($parsed['host'])) $parsed['host'] = '';
14904fd306cSNickeau            if (empty($parsed['port'])) $parsed['port'] = 80;
15004fd306cSNickeau            if (empty($parsed['path'])) $parsed['path'] = '';
15104fd306cSNickeau            if (empty($parsed['query'])) $parsed['query'] = '';
15204fd306cSNickeau            $interWikiUrlTemplate = strtr($interWikiUrlTemplate, [
15304fd306cSNickeau                '{SCHEME}' => $parsed['scheme'],
15404fd306cSNickeau                '{HOST}' => $parsed['host'],
15504fd306cSNickeau                '{PORT}' => $parsed['port'],
15604fd306cSNickeau                '{PATH}' => $parsed['path'],
15704fd306cSNickeau                '{QUERY}' => $parsed['query'],
15804fd306cSNickeau            ]);
15904fd306cSNickeau        }
16004fd306cSNickeau
16104fd306cSNickeau        // If no replacement
16204fd306cSNickeau        if ($interWikiUrlTemplate === $originalInterWikiUrlTemplate) {
16304fd306cSNickeau
16404fd306cSNickeau            $interWikiUrlTemplate = $interWikiUrlTemplate . rawurlencode($this->urlWithoutFragment);
16504fd306cSNickeau
16604fd306cSNickeau        }
16704fd306cSNickeau
16804fd306cSNickeau
16904fd306cSNickeau        if ($this->fragment) $interWikiUrlTemplate .= '#' . rawurlencode($this->fragment);
17004fd306cSNickeau
17104fd306cSNickeau        return Url::createFromString($interWikiUrlTemplate);
17204fd306cSNickeau    }
17304fd306cSNickeau
17404fd306cSNickeau    /**
17504fd306cSNickeau     * @param string $interWikiRef
17604fd306cSNickeau     * @return InterWiki
17704fd306cSNickeau     */
17804fd306cSNickeau    public static function createLinkInterWikiFromString(string $interWikiRef): InterWiki
17904fd306cSNickeau    {
18004fd306cSNickeau        return new InterWiki($interWikiRef, MarkupRef::LINK_TYPE);
18104fd306cSNickeau    }
18204fd306cSNickeau
18304fd306cSNickeau    /**
18404fd306cSNickeau     * @throws ExceptionNotFound
18504fd306cSNickeau     */
18604fd306cSNickeau    private function getTemplateUrlString(): string
18704fd306cSNickeau    {
18804fd306cSNickeau        $interWikis = $this->getInterWikis();
18904fd306cSNickeau
19070bbd7f1Sgerardnico        $urlTemplate = $interWikis[$this->name] ?? null;
19104fd306cSNickeau        if ($urlTemplate !== null) {
19204fd306cSNickeau            return $urlTemplate;
19304fd306cSNickeau        }
19404fd306cSNickeau        throw new ExceptionNotFound("No Wiki ($this->name) found");
19504fd306cSNickeau
19604fd306cSNickeau    }
19704fd306cSNickeau
19804fd306cSNickeau    private static function getInterWikis(): array
19904fd306cSNickeau    {
20004fd306cSNickeau        return ExecutionContext::getActualOrCreateFromEnv()
20104fd306cSNickeau            ->getConfig()
20204fd306cSNickeau            ->getInterWikis();
20304fd306cSNickeau    }
20404fd306cSNickeau
20504fd306cSNickeau    /**
20604fd306cSNickeau     * @throws ExceptionNotFound
20704fd306cSNickeau     */
20804fd306cSNickeau    private function getTemplateUrlStringOrDefault()
20904fd306cSNickeau    {
21004fd306cSNickeau        try {
21104fd306cSNickeau            return $this->getTemplateUrlString();
21204fd306cSNickeau        } catch (ExceptionNotFound $e) {
21304fd306cSNickeau            $interWikis = $this->getInterWikis();
21404fd306cSNickeau            if (isset($interWikis[self::DEFAULT_INTERWIKI_NAME])) {
21504fd306cSNickeau                $this->name = self::DEFAULT_INTERWIKI_NAME;
21604fd306cSNickeau                return $interWikis[self::DEFAULT_INTERWIKI_NAME];
21704fd306cSNickeau            }
21804fd306cSNickeau        }
21904fd306cSNickeau        throw new ExceptionNotFound("The inter-wiki ({$this->getWiki()}) does not exist and there is no default inter-wiki defined.");
22004fd306cSNickeau
22104fd306cSNickeau    }
22204fd306cSNickeau
22304fd306cSNickeau    public function getWiki()
22404fd306cSNickeau    {
22504fd306cSNickeau        return $this->name;
22604fd306cSNickeau    }
22704fd306cSNickeau
22804fd306cSNickeau    /**
22904fd306cSNickeau     * @return string
23004fd306cSNickeau     */
23104fd306cSNickeau    public function getRef(): string
23204fd306cSNickeau    {
23304fd306cSNickeau        return $this->ref;
23404fd306cSNickeau    }
23504fd306cSNickeau
23604fd306cSNickeau    /**
23704fd306cSNickeau     * @return string - the class for this specific interwiki
23804fd306cSNickeau     */
23904fd306cSNickeau    public function getSubComponentClass(): string
24004fd306cSNickeau    {
24104fd306cSNickeau        return self::IW_PREFIX . preg_replace('/[^_\-a-z0-9]+/i', '_', $this->getWiki());
24204fd306cSNickeau    }
24304fd306cSNickeau
24404fd306cSNickeau    /**
24504fd306cSNickeau     * @throws ExceptionNotFound
24604fd306cSNickeau     */
24704fd306cSNickeau    public function getSpecificCssRules(): string
24804fd306cSNickeau    {
24904fd306cSNickeau
25004fd306cSNickeau        /**
25104fd306cSNickeau         * Adapted from {@link css_interwiki()}
25204fd306cSNickeau         */
25304fd306cSNickeau        foreach (['svg', 'png', 'gif'] as $ext) {
25404fd306cSNickeau            $file = 'lib/images/interwiki/' . $this->name . '.' . $ext;
25504fd306cSNickeau            $urlFile = DOKU_BASE . $file;
25604fd306cSNickeau            $class = $this->getSubComponentClass();
25704fd306cSNickeau            if (file_exists(DOKU_INC . $file)) {
25804fd306cSNickeau                return <<<EOF
25904fd306cSNickeaua.$class {
26004fd306cSNickeau    background-image: url($urlFile)
26104fd306cSNickeau}
26204fd306cSNickeauEOF;
26304fd306cSNickeau            }
26404fd306cSNickeau        }
26504fd306cSNickeau        throw new ExceptionNotFound("No interwiki file found");
26604fd306cSNickeau    }
26704fd306cSNickeau
26804fd306cSNickeau    public
26904fd306cSNickeau    function getDefaultCssRules(): string
27004fd306cSNickeau    {
27104fd306cSNickeau        $url = DOKU_BASE . 'lib/images/interwiki.svg';
27204fd306cSNickeau        return <<<EOF
27304fd306cSNickeaua.interwiki {
27404fd306cSNickeau    background: transparent url($url) 0 0 no-repeat;
27504fd306cSNickeau    background-size: 1.2em;
27604fd306cSNickeau    padding: 0 0 0 1.4em;
27704fd306cSNickeau}
27804fd306cSNickeauEOF;
27904fd306cSNickeau    }
28004fd306cSNickeau
28104fd306cSNickeau
28204fd306cSNickeau}
283