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