xref: /plugin/combo/ComboStrap/InterWiki.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeauuse ComboStrap\Web\Url;
6*04fd306cSNickeau
7*04fd306cSNickeauclass InterWiki
8*04fd306cSNickeau{
9*04fd306cSNickeau
10*04fd306cSNickeau
11*04fd306cSNickeau    const DEFAULT_INTERWIKI_NAME = 'default';
12*04fd306cSNickeau
13*04fd306cSNickeau    /**
14*04fd306cSNickeau     * The pattern that select the characters to encode in URL
15*04fd306cSNickeau     */
16*04fd306cSNickeau    const CHARACTERS_TO_ENCODE = '/[[\\\\\]^`{|}#%]/';
17*04fd306cSNickeau    const IW_PREFIX = "iw_";
18*04fd306cSNickeau
19*04fd306cSNickeau
20*04fd306cSNickeau    private static ?array $INTERWIKI_URL_TEMPLATES = null;
21*04fd306cSNickeau
22*04fd306cSNickeau    private string $name;
23*04fd306cSNickeau    private string $ref;
24*04fd306cSNickeau
25*04fd306cSNickeau    private ?string $urlWithoutFragment = null;
26*04fd306cSNickeau
27*04fd306cSNickeau    private ?string $fragment = null;
28*04fd306cSNickeau
29*04fd306cSNickeau    private string $markupType;
30*04fd306cSNickeau
31*04fd306cSNickeau    /**
32*04fd306cSNickeau     * @param string $interWikiRef - The interwiki
33*04fd306cSNickeau     * @param string $markupType - The {@link MarkupRef::getSchemeType()} ie media or link
34*04fd306cSNickeau     */
35*04fd306cSNickeau    public function __construct(string $interWikiRef, string $markupType)
36*04fd306cSNickeau    {
37*04fd306cSNickeau
38*04fd306cSNickeau        $this->ref = $interWikiRef;
39*04fd306cSNickeau        $this->markupType = $markupType;
40*04fd306cSNickeau        [$this->name, $this->urlWithoutFragment] = explode(">", $interWikiRef, 2);
41*04fd306cSNickeau
42*04fd306cSNickeau
43*04fd306cSNickeau        $hash = strrchr($this->urlWithoutFragment, '#');
44*04fd306cSNickeau        if ($hash) {
45*04fd306cSNickeau            $this->urlWithoutFragment = substr($this->urlWithoutFragment, 0, -strlen($hash));
46*04fd306cSNickeau            $this->fragment = substr($hash, 1);
47*04fd306cSNickeau        }
48*04fd306cSNickeau
49*04fd306cSNickeau    }
50*04fd306cSNickeau
51*04fd306cSNickeau    public static function addInterWiki(string $name, string $value)
52*04fd306cSNickeau    {
53*04fd306cSNickeau        ExecutionContext::getActualOrCreateFromEnv()
54*04fd306cSNickeau            ->getConfig()
55*04fd306cSNickeau            ->addInterWiki($name, $value);
56*04fd306cSNickeau
57*04fd306cSNickeau    }
58*04fd306cSNickeau
59*04fd306cSNickeau
60*04fd306cSNickeau    public static function createMediaInterWikiFromString(string $ref): InterWiki
61*04fd306cSNickeau    {
62*04fd306cSNickeau        return new InterWiki($ref, MarkupRef::MEDIA_TYPE);
63*04fd306cSNickeau    }
64*04fd306cSNickeau
65*04fd306cSNickeau    /**
66*04fd306cSNickeau     * @return string - the general component class
67*04fd306cSNickeau     */
68*04fd306cSNickeau    public static function getComponentClass(): string
69*04fd306cSNickeau    {
70*04fd306cSNickeau        $oldClassName = SiteConfig::getConfValue(LinkMarkup::CONF_USE_DOKUWIKI_CLASS_NAME);
71*04fd306cSNickeau        if ($oldClassName) {
72*04fd306cSNickeau            return "interwiki";
73*04fd306cSNickeau        } else {
74*04fd306cSNickeau            return "link-interwiki";
75*04fd306cSNickeau        }
76*04fd306cSNickeau    }
77*04fd306cSNickeau
78*04fd306cSNickeau    /**
79*04fd306cSNickeau     * @throws ExceptionNotFound
80*04fd306cSNickeau     * @throws ExceptionBadSyntax
81*04fd306cSNickeau     * @throws ExceptionBadArgument
82*04fd306cSNickeau     * Adapted  from {@link Doku_Renderer_xhtml::_resolveInterWiki()}
83*04fd306cSNickeau     */
84*04fd306cSNickeau    public function toUrl(): Url
85*04fd306cSNickeau    {
86*04fd306cSNickeau
87*04fd306cSNickeau        $originalInterWikiUrlTemplate = $this->getTemplateUrlStringOrDefault();
88*04fd306cSNickeau        $interWikiUrlTemplate = $originalInterWikiUrlTemplate;
89*04fd306cSNickeau
90*04fd306cSNickeau        /**
91*04fd306cSNickeau         * Dokuwiki Id template
92*04fd306cSNickeau         */
93*04fd306cSNickeau        if ($interWikiUrlTemplate[0] === ':') {
94*04fd306cSNickeau            $interWikiUrlTemplate = str_replace(
95*04fd306cSNickeau                '{NAME}',
96*04fd306cSNickeau                $this->urlWithoutFragment,
97*04fd306cSNickeau                $interWikiUrlTemplate
98*04fd306cSNickeau            );
99*04fd306cSNickeau            if ($this->fragment !== null) {
100*04fd306cSNickeau                $interWikiUrlTemplate = "$interWikiUrlTemplate#$this->fragment";
101*04fd306cSNickeau            }
102*04fd306cSNickeau            switch ($this->markupType) {
103*04fd306cSNickeau                case MarkupRef::MEDIA_TYPE:
104*04fd306cSNickeau                    return MarkupRef::createMediaFromRef($interWikiUrlTemplate)->getUrl();
105*04fd306cSNickeau                case MarkupRef::LINK_TYPE:
106*04fd306cSNickeau                default:
107*04fd306cSNickeau                    return MarkupRef::createLinkFromRef($interWikiUrlTemplate)->getUrl();
108*04fd306cSNickeau            }
109*04fd306cSNickeau
110*04fd306cSNickeau        }
111*04fd306cSNickeau
112*04fd306cSNickeau        // Replace placeholder if any
113*04fd306cSNickeau        if (preg_match('#{URL}#', $interWikiUrlTemplate)) {
114*04fd306cSNickeau
115*04fd306cSNickeau            // Replace the Url
116*04fd306cSNickeau            $interWikiUrlTemplate = str_replace(
117*04fd306cSNickeau                '{URL}',
118*04fd306cSNickeau                rawurlencode($this->urlWithoutFragment),
119*04fd306cSNickeau                $interWikiUrlTemplate
120*04fd306cSNickeau            );
121*04fd306cSNickeau
122*04fd306cSNickeau        }
123*04fd306cSNickeau
124*04fd306cSNickeau        // Name placeholder means replace with URL encoding
125*04fd306cSNickeau        if (preg_match('#{NAME}#', $interWikiUrlTemplate)) {
126*04fd306cSNickeau
127*04fd306cSNickeau            $interWikiUrlTemplate = str_replace(
128*04fd306cSNickeau                '{NAME}',
129*04fd306cSNickeau                preg_replace_callback(
130*04fd306cSNickeau                    self::CHARACTERS_TO_ENCODE,
131*04fd306cSNickeau                    function ($match) {
132*04fd306cSNickeau                        return rawurlencode($match[0]);
133*04fd306cSNickeau                    },
134*04fd306cSNickeau                    $this->urlWithoutFragment
135*04fd306cSNickeau                ),
136*04fd306cSNickeau                $interWikiUrlTemplate
137*04fd306cSNickeau            );
138*04fd306cSNickeau
139*04fd306cSNickeau        }
140*04fd306cSNickeau
141*04fd306cSNickeau        // Url replacement
142*04fd306cSNickeau        if (preg_match('#{(SCHEME|HOST|PORT|PATH|QUERY)}#', $interWikiUrlTemplate)) {
143*04fd306cSNickeau
144*04fd306cSNickeau            $parsed = parse_url($this->urlWithoutFragment);
145*04fd306cSNickeau            if (empty($parsed['scheme'])) $parsed['scheme'] = '';
146*04fd306cSNickeau            if (empty($parsed['host'])) $parsed['host'] = '';
147*04fd306cSNickeau            if (empty($parsed['port'])) $parsed['port'] = 80;
148*04fd306cSNickeau            if (empty($parsed['path'])) $parsed['path'] = '';
149*04fd306cSNickeau            if (empty($parsed['query'])) $parsed['query'] = '';
150*04fd306cSNickeau            $interWikiUrlTemplate = strtr($interWikiUrlTemplate, [
151*04fd306cSNickeau                '{SCHEME}' => $parsed['scheme'],
152*04fd306cSNickeau                '{HOST}' => $parsed['host'],
153*04fd306cSNickeau                '{PORT}' => $parsed['port'],
154*04fd306cSNickeau                '{PATH}' => $parsed['path'],
155*04fd306cSNickeau                '{QUERY}' => $parsed['query'],
156*04fd306cSNickeau            ]);
157*04fd306cSNickeau        }
158*04fd306cSNickeau
159*04fd306cSNickeau        // If no replacement
160*04fd306cSNickeau        if ($interWikiUrlTemplate === $originalInterWikiUrlTemplate) {
161*04fd306cSNickeau
162*04fd306cSNickeau            $interWikiUrlTemplate = $interWikiUrlTemplate . rawurlencode($this->urlWithoutFragment);
163*04fd306cSNickeau
164*04fd306cSNickeau        }
165*04fd306cSNickeau
166*04fd306cSNickeau
167*04fd306cSNickeau        if ($this->fragment) $interWikiUrlTemplate .= '#' . rawurlencode($this->fragment);
168*04fd306cSNickeau
169*04fd306cSNickeau        return Url::createFromString($interWikiUrlTemplate);
170*04fd306cSNickeau    }
171*04fd306cSNickeau
172*04fd306cSNickeau    /**
173*04fd306cSNickeau     * @param string $interWikiRef
174*04fd306cSNickeau     * @return InterWiki
175*04fd306cSNickeau     */
176*04fd306cSNickeau    public static function createLinkInterWikiFromString(string $interWikiRef): InterWiki
177*04fd306cSNickeau    {
178*04fd306cSNickeau        return new InterWiki($interWikiRef, MarkupRef::LINK_TYPE);
179*04fd306cSNickeau    }
180*04fd306cSNickeau
181*04fd306cSNickeau    /**
182*04fd306cSNickeau     * @throws ExceptionNotFound
183*04fd306cSNickeau     */
184*04fd306cSNickeau    private function getTemplateUrlString(): string
185*04fd306cSNickeau    {
186*04fd306cSNickeau        $interWikis = $this->getInterWikis();
187*04fd306cSNickeau
188*04fd306cSNickeau        $urlTemplate = $interWikis[$this->name];
189*04fd306cSNickeau        if ($urlTemplate !== null) {
190*04fd306cSNickeau            return $urlTemplate;
191*04fd306cSNickeau        }
192*04fd306cSNickeau        throw new ExceptionNotFound("No Wiki ($this->name) found");
193*04fd306cSNickeau
194*04fd306cSNickeau    }
195*04fd306cSNickeau
196*04fd306cSNickeau    private static function getInterWikis(): array
197*04fd306cSNickeau    {
198*04fd306cSNickeau        return ExecutionContext::getActualOrCreateFromEnv()
199*04fd306cSNickeau            ->getConfig()
200*04fd306cSNickeau            ->getInterWikis();
201*04fd306cSNickeau    }
202*04fd306cSNickeau
203*04fd306cSNickeau    /**
204*04fd306cSNickeau     * @throws ExceptionNotFound
205*04fd306cSNickeau     */
206*04fd306cSNickeau    private function getTemplateUrlStringOrDefault()
207*04fd306cSNickeau    {
208*04fd306cSNickeau        try {
209*04fd306cSNickeau            return $this->getTemplateUrlString();
210*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
211*04fd306cSNickeau            $interWikis = $this->getInterWikis();
212*04fd306cSNickeau            if (isset($interWikis[self::DEFAULT_INTERWIKI_NAME])) {
213*04fd306cSNickeau                $this->name = self::DEFAULT_INTERWIKI_NAME;
214*04fd306cSNickeau                return $interWikis[self::DEFAULT_INTERWIKI_NAME];
215*04fd306cSNickeau            }
216*04fd306cSNickeau        }
217*04fd306cSNickeau        throw new ExceptionNotFound("The inter-wiki ({$this->getWiki()}) does not exist and there is no default inter-wiki defined.");
218*04fd306cSNickeau
219*04fd306cSNickeau    }
220*04fd306cSNickeau
221*04fd306cSNickeau    public function getWiki()
222*04fd306cSNickeau    {
223*04fd306cSNickeau        return $this->name;
224*04fd306cSNickeau    }
225*04fd306cSNickeau
226*04fd306cSNickeau    /**
227*04fd306cSNickeau     * @return string
228*04fd306cSNickeau     */
229*04fd306cSNickeau    public function getRef(): string
230*04fd306cSNickeau    {
231*04fd306cSNickeau        return $this->ref;
232*04fd306cSNickeau    }
233*04fd306cSNickeau
234*04fd306cSNickeau    /**
235*04fd306cSNickeau     * @return string - the class for this specific interwiki
236*04fd306cSNickeau     */
237*04fd306cSNickeau    public function getSubComponentClass(): string
238*04fd306cSNickeau    {
239*04fd306cSNickeau        return self::IW_PREFIX . preg_replace('/[^_\-a-z0-9]+/i', '_', $this->getWiki());
240*04fd306cSNickeau    }
241*04fd306cSNickeau
242*04fd306cSNickeau    /**
243*04fd306cSNickeau     * @throws ExceptionNotFound
244*04fd306cSNickeau     */
245*04fd306cSNickeau    public function getSpecificCssRules(): string
246*04fd306cSNickeau    {
247*04fd306cSNickeau
248*04fd306cSNickeau        /**
249*04fd306cSNickeau         * Adapted from {@link css_interwiki()}
250*04fd306cSNickeau         */
251*04fd306cSNickeau        foreach (['svg', 'png', 'gif'] as $ext) {
252*04fd306cSNickeau            $file = 'lib/images/interwiki/' . $this->name . '.' . $ext;
253*04fd306cSNickeau            $urlFile = DOKU_BASE . $file;
254*04fd306cSNickeau            $class = $this->getSubComponentClass();
255*04fd306cSNickeau            if (file_exists(DOKU_INC . $file)) {
256*04fd306cSNickeau                return <<<EOF
257*04fd306cSNickeaua.$class {
258*04fd306cSNickeau    background-image: url($urlFile)
259*04fd306cSNickeau}
260*04fd306cSNickeauEOF;
261*04fd306cSNickeau            }
262*04fd306cSNickeau        }
263*04fd306cSNickeau        throw new ExceptionNotFound("No interwiki file found");
264*04fd306cSNickeau    }
265*04fd306cSNickeau
266*04fd306cSNickeau    public
267*04fd306cSNickeau    function getDefaultCssRules(): string
268*04fd306cSNickeau    {
269*04fd306cSNickeau        $url = DOKU_BASE . 'lib/images/interwiki.svg';
270*04fd306cSNickeau        return <<<EOF
271*04fd306cSNickeaua.interwiki {
272*04fd306cSNickeau    background: transparent url($url) 0 0 no-repeat;
273*04fd306cSNickeau    background-size: 1.2em;
274*04fd306cSNickeau    padding: 0 0 0 1.4em;
275*04fd306cSNickeau}
276*04fd306cSNickeauEOF;
277*04fd306cSNickeau    }
278*04fd306cSNickeau
279*04fd306cSNickeau
280*04fd306cSNickeau}
281