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