1ab97758cSPeter Stumm<?php 2ab97758cSPeter Stumm/** 3ab97758cSPeter Stumm * Plugin Button : Add button with image support syntax for links 4ab97758cSPeter Stumm * 5ab97758cSPeter Stumm * To be run with Dokuwiki only 6ab97758cSPeter Stumm 7ab97758cSPeter Stumm * 8ab97758cSPeter Stumm * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 9ab97758cSPeter Stumm * @author Rémi Peyronnet <remi+xslt@via.ecp.fr> 10ab97758cSPeter Stumm 11ab97758cSPeter Stumm Full Syntax : 12ab97758cSPeter Stumm [[{namespace:image|extra css}wiki page|Title of the link]] 13ab97758cSPeter Stumm 14ab97758cSPeter Stumm All fields optional, minimal syntax: 15ab97758cSPeter Stumm [[{}Simple button]] 16ab97758cSPeter Stumm 17ab97758cSPeter Stumm Configuration : 18ab97758cSPeter Stumm [[{conf.styles}style|css]] 19ab97758cSPeter Stumm [[{conf.target}style|target]] 20ab97758cSPeter Stumm 21ab97758cSPeter Stumm 19/05/2013 : Initial release 22ab97758cSPeter Stumm 20/04/2014 : Added target support (feature request from Andrew St Hilaire) 23ab97758cSPeter Stumm 07/06/2014 : Added dokuwiki formatting support in title section (not working in wiki page section) (feature request from Willi Lethert) 24ab97758cSPeter Stumm 30/08/2014 : Added toolbar button (contribution from Xavier Decuyper) and fixed local anchor (bug reported by Andreas Kuzma) 254e3d9946SRémi Peyronnet 06/09/2014 : Refactored to add backlinks support (feature request from Schümmer Hans-Jürgen) 264e3d9946SRémi Peyronnet 28/04/2015 : Refactored global config handling, add internal media link support, add escaping of userinput (contribution from Peter Stumm https://github.com/lisps/plugin-button) 277e1bed43SRémi Peyronnet 05/08/2015 : Merged lisps default style option and added french translation 2803792051SRémi Peyronnet 12/09/2015 : Fixed PHP error 291c6a0c31SRemi Peyronnet 30/04/2020 : Fixed spaces in image field 30b7a374ddSRemi Peyronnet 04/08/2020 : Quick hack to add compatibility with hogfather 31e20774c3SRemi Peyronnet 07/02/2022 : Added Português do Brasil translation (PR by mac-sousa) 3252d067acSRemi Peyronnet 26/11/2022 : Fixed PHP8.1 warnings 33092313a5SRemi Peyronnet 13/12/2022 : Fixed PHP7 with str_contains polyfill 3467df9406SRemi Peyronnet 05/01/2025 : Added support for interwiki links (#35) 35ab97758cSPeter Stumm 36929f708eSThisNameIsNotAllowed @author ThisNameIsNotAllowed 37929f708eSThisNameIsNotAllowed 17/11/2016 : Added generation of metadata 38929f708eSThisNameIsNotAllowed 18/11/2016 : Added default target for external links 39e91d7e14SRémi Peyronnet 40e91d7e14SRémi Peyronnet @author lisps 41e91d7e14SRémi Peyronnet 05/03/2017 : Merged lisps move compatibility fixes 421eab5a12SRémi Peyronnet 431eab5a12SRémi Peyronnet @author nerun 441eab5a12SRémi Peyronnet 18/08/2023 : Fixed deprecation warnings in PHP 8+ (#33) 451eab5a12SRémi Peyronnet 46*ce70c671SRémi Peyronnet @author kmlucy 47*ce70c671SRémi Peyronnet 14/07/2026 : Fixed compatibility with DokuWiki Mort (#37) 48*ce70c671SRémi Peyronnet 491eab5a12SRémi Peyronnet Knwon bugs: 5067df9406SRemi Peyronnet - handle / render repartition is not optimal regarding caching, most of the processing should be moved (#14) 51ab97758cSPeter Stumm*/ 521eab5a12SRémi Peyronnet 531eab5a12SRémi Peyronnet 54eaddf009SDaniel Dias Rodriguesuse dokuwiki\File\PageResolver; 55eaddf009SDaniel Dias Rodriguesuse dokuwiki\File\MediaResolver; 56ab97758cSPeter Stumm 571eab5a12SRémi Peyronnetif (!defined('DOKU_INC')) 581eab5a12SRémi Peyronnet die(); 591eab5a12SRémi Peyronnetif (!defined('DOKU_PLUGIN')) 601eab5a12SRémi Peyronnet define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 61b7a374ddSRemi Peyronnet 62b7a374ddSRemi Peyronnet/* 2020-08-04 - This is a quick hack to fix compatibility issue with hogfather (see issue #13) : 63b7a374ddSRemi Peyronnet * It seems that the handler.php file is no more loaded when rendering cached contents, causing a crash. 64b7a374ddSRemi Peyronnet * This is due to a bad initial conception of this plugin that does not comply to dokuwiki's guidance of handle / render repartition. 65b7a374ddSRemi Peyronnet * 66b7a374ddSRemi Peyronnet * FIXME : refactor handle / render repartition ; most of the processing should be moved in the handle section. 67b7a374ddSRemi Peyronnet * /!\ to be able to do that (and thus, modify the cached content structure) need to find a way to clear the cache while upgrading the plugin... 68*ce70c671SRémi Peyronnet * 69*ce70c671SRémi Peyronnet * 2026-06-14 : This hack is no more needed, you may need to uncomment this line if you are using an old version of dokuwiki (2020-2025) 70*ce70c671SRémi Peyronnet * 71*ce70c671SRémi Peyronnet * require_once(DOKU_INC . 'inc/parser/handler.php'); 72b7a374ddSRemi Peyronnet */ 73ab97758cSPeter Stumm 74c3e9a88aSRemi Peyronnet// str_contains polyfill for PHP versions before PHP8 (by jnv - #26) 75c3e9a88aSRemi Peyronnetif (!function_exists('str_contains')) { 76c3e9a88aSRemi Peyronnet function str_contains(string $haystack, string $needle): bool 77c3e9a88aSRemi Peyronnet { 78c3e9a88aSRemi Peyronnet return '' === $needle || false !== strpos($haystack, $needle); 79c3e9a88aSRemi Peyronnet } 80c3e9a88aSRemi Peyronnet} 81c3e9a88aSRemi Peyronnet 821eab5a12SRémi Peyronnetclass syntax_plugin_button extends DokuWiki_Syntax_Plugin 831eab5a12SRémi Peyronnet{ 840166612cSunknown 851eab5a12SRémi Peyronnet function getType() 861eab5a12SRémi Peyronnet { 871eab5a12SRémi Peyronnet return 'substition'; 881eab5a12SRémi Peyronnet } 891eab5a12SRémi Peyronnet function getPType() 901eab5a12SRémi Peyronnet { 911eab5a12SRémi Peyronnet return 'normal'; 921eab5a12SRémi Peyronnet } 931eab5a12SRémi Peyronnet function getSort() 941eab5a12SRémi Peyronnet { 951eab5a12SRémi Peyronnet return 25; 961eab5a12SRémi Peyronnet } // Internal link is 300 971eab5a12SRémi Peyronnet 981eab5a12SRémi Peyronnet function connectTo($mode) 991eab5a12SRémi Peyronnet { 1000166612cSunknown $this->Lexer->addSpecialPattern('\[\[{conf[^}]*}[^\]]*\]\]', $mode, 'plugin_button'); 1010166612cSunknown $this->Lexer->addEntryPattern('\[\[{[^}]*}[^\]\|]*\|?', $mode, 'plugin_button'); 1020166612cSunknown $this->Lexer->addExitPattern(']]', 'plugin_button'); 1030166612cSunknown } 1041eab5a12SRémi Peyronnet function postConnect() 1051eab5a12SRémi Peyronnet { 1061eab5a12SRémi Peyronnet } 1071eab5a12SRémi Peyronnet function getAllowedTypes() 1081eab5a12SRémi Peyronnet { 1091eab5a12SRémi Peyronnet return array('formatting', 'substition'); 1101eab5a12SRémi Peyronnet } 1110166612cSunknown 1120166612cSunknown 1130166612cSunknown 11427360b96Slisps protected $confStyles; 1150166612cSunknown protected $styles = array(); 1160166612cSunknown protected $targets = array(); 1171eab5a12SRémi Peyronnet protected function setStyle($name, $value) 1181eab5a12SRémi Peyronnet { 1190166612cSunknown global $ID; 1200166612cSunknown $this->styles[$ID][$name] = $value; 1210166612cSunknown } 1221eab5a12SRémi Peyronnet protected function getStyle($name) 1231eab5a12SRémi Peyronnet { 1240166612cSunknown global $ID; 12527360b96Slisps return isset($this->styles[$ID][$name]) ? $this->styles[$ID][$name] : $this->getConfStyles($name); 1260166612cSunknown } 1271eab5a12SRémi Peyronnet protected function hasStyle($name) 1281eab5a12SRémi Peyronnet { 1290166612cSunknown global $ID; 130f044f88eSRemi Peyronnet return (array_key_exists($ID, $this->styles) && is_array($this->styles[$ID]) && array_key_exists($name, $this->styles[$ID])) 13127360b96Slisps || $this->getConfStyles($name) ? true : false; 13227360b96Slisps } 1331eab5a12SRémi Peyronnet protected function getConfStyles($name = null) 1341eab5a12SRémi Peyronnet { 13527360b96Slisps if ($this->confStyles === null) { 13627360b96Slisps $this->confStyles = array(); 13727360b96Slisps 13827360b96Slisps $styles = $this->getConf('styles'); 1391eab5a12SRémi Peyronnet if (!$styles) 1401eab5a12SRémi Peyronnet return; 14127360b96Slisps 14227360b96Slisps $styles = explode("\n", $styles); 1431eab5a12SRémi Peyronnet if (!is_array($styles)) 1441eab5a12SRémi Peyronnet return; 14527360b96Slisps 14627360b96Slisps foreach ($styles as $style) { 14727360b96Slisps $style = trim($style); 1481eab5a12SRémi Peyronnet if (!$style) 1491eab5a12SRémi Peyronnet continue; 15027360b96Slisps 151f044f88eSRemi Peyronnet if (str_contains($style, '|')) { 152f044f88eSRemi Peyronnet 15327360b96Slisps $style = explode('|', $style, 2); 1541eab5a12SRémi Peyronnet if (!is_array($style) || !$style[0] || !$style[1]) 1551eab5a12SRémi Peyronnet continue; 15627360b96Slisps 15727360b96Slisps $this->confStyles[trim($style[0])] = trim($style[1]); 15827360b96Slisps } 159f044f88eSRemi Peyronnet } 16027360b96Slisps //dbg($this->confStyles); 16127360b96Slisps 16227360b96Slisps } 16327360b96Slisps 16427360b96Slisps if ($name) { 1651eab5a12SRémi Peyronnet if (!isset($this->confStyles[$name])) 1661eab5a12SRémi Peyronnet return false; 16727360b96Slisps 16827360b96Slisps return $this->confStyles[$name]; 16927360b96Slisps } 17027360b96Slisps return $this->confStyles; 17127360b96Slisps 1720166612cSunknown } 1730166612cSunknown 1740166612cSunknown 1751eab5a12SRémi Peyronnet protected function setTarget($name, $value) 1761eab5a12SRémi Peyronnet { 1770166612cSunknown global $ID; 1780166612cSunknown $this->targets[$ID][$name] = $value; 1790166612cSunknown } 1801eab5a12SRémi Peyronnet protected function getTarget($name) 1811eab5a12SRémi Peyronnet { 1820166612cSunknown global $ID; 1830166612cSunknown return $this->targets[$ID][$name]; 1840166612cSunknown } 1851eab5a12SRémi Peyronnet protected function hasTarget($name) 1861eab5a12SRémi Peyronnet { 1870166612cSunknown global $ID; 188f044f88eSRemi Peyronnet return (array_key_exists($ID, $this->targets) && is_array($this->targets[$ID]) && array_key_exists($name, $this->targets[$ID])) ? true : false; 1890166612cSunknown } 1900166612cSunknown 1912387a734SAndreas Gohr function handle($match, $state, $pos, Doku_Handler $handler) 1920166612cSunknown { 1930166612cSunknown global $plugin_button_styles; 1940166612cSunknown global $plugin_button_target; 1950166612cSunknown 1960166612cSunknown switch ($state) { 1970166612cSunknown case DOKU_LEXER_SPECIAL: 1980166612cSunknown case DOKU_LEXER_ENTER: 1990166612cSunknown $data = ''; 2000166612cSunknown // Button 2011eab5a12SRémi Peyronnet if (preg_match('/\[\[{ *(?<image>[^}\|]*) *\|?(?<css>[^}]*)}(?<link>[^\]\|]*)\|?(?<title>[^\]]*)/', $match, $matches)) { 2020166612cSunknown $data = $matches; 2030166612cSunknown } 2041eab5a12SRémi Peyronnet if (is_array($data)) { 2051eab5a12SRémi Peyronnet if ($data['image'] == 'conf.styles') { 2060166612cSunknown $this->setStyle($data['link'], $data['title']); 2071eab5a12SRémi Peyronnet } else if ($data['image'] == 'conf.target') { 2080166612cSunknown $this->setTarget($data['link'], $data['title']); 2091eab5a12SRémi Peyronnet } else { 2100166612cSunknown $data['target'] = ""; 2111eab5a12SRémi Peyronnet if ($this->hasTarget($data['css'])) { 2120166612cSunknown $data['target'] = $this->getTarget($data['css']); 2131eab5a12SRémi Peyronnet } else if ($this->hasTarget('default')) { 2140166612cSunknown $data['target'] = $this->getTarget('default'); 2150166612cSunknown } 2160166612cSunknown 2170166612cSunknown 2181eab5a12SRémi Peyronnet if ($data['css'] != "" && $this->hasStyle($data['css'])) { 2190166612cSunknown $data['css'] = $this->getStyle($data['css']); 2200166612cSunknown } 2210166612cSunknown 2221eab5a12SRémi Peyronnet if ($this->hasStyle('default') && ($data['css'] != 'default')) { 2230166612cSunknown $data['css'] = $this->getStyle('default') . ' ; ' . $data['css']; 2240166612cSunknown } 2250166612cSunknown } 2260166612cSunknown } 2270166612cSunknown 2280166612cSunknown return array($state, $data); 2290166612cSunknown 2301eab5a12SRémi Peyronnet case DOKU_LEXER_UNMATCHED: 2311eab5a12SRémi Peyronnet return array($state, $match); 2321eab5a12SRémi Peyronnet case DOKU_LEXER_EXIT: 2331eab5a12SRémi Peyronnet return array($state, ''); 2340166612cSunknown } 2350166612cSunknown return array(); 2360166612cSunknown } 2370166612cSunknown 2382387a734SAndreas Gohr function render($mode, Doku_Renderer $renderer, $data) 2390166612cSunknown { 2400166612cSunknown global $plugin_button_styles; 2410166612cSunknown global $plugin_button_target; 242929f708eSThisNameIsNotAllowed global $ID; 243929f708eSThisNameIsNotAllowed global $conf; 2440166612cSunknown 2450166612cSunknown if ($mode == 'xhtml') { 2460166612cSunknown list($state, $match) = $data; 2470166612cSunknown switch ($state) { 2480166612cSunknown case DOKU_LEXER_SPECIAL: 2490166612cSunknown case DOKU_LEXER_ENTER: 2501eab5a12SRémi Peyronnet if (is_array($match)) { 2510166612cSunknown $image = $match['image']; 2521eab5a12SRémi Peyronnet if (($image != "conf.target") && ($image != "conf.styles")) { 2530166612cSunknown // Test if internal or external link (from handler.php / internallink) 254fddca096SRemi Peyronnet // 2020-07-09 : added special prefix '!' to allow other URI schemes without '//' in it (ex : apt,...) 255fddca096SRemi Peyronnet $force_uri_prefix = "!"; // "/" can be confused with url, "!" not working 2561eab5a12SRémi Peyronnet if ((substr($match['link'], 0, strlen($force_uri_prefix)) === $force_uri_prefix) || (preg_match('#^mailto:|^([a-z0-9\-\.+]+?)://#i', $match['link']))) { 2570166612cSunknown // External 2580166612cSunknown $link['url'] = $match['link']; 259fddca096SRemi Peyronnet // Strip trailing prefix 2601eab5a12SRémi Peyronnet if (substr($link['url'], 0, strlen($force_uri_prefix)) === $force_uri_prefix) { 2611eab5a12SRémi Peyronnet $link['url'] = substr($link['url'], strlen($force_uri_prefix)); 2621eab5a12SRémi Peyronnet } 263a52b82baSRemi Peyronnet // Check if it is an allowed protocol 264a52b82baSRemi Peyronnet $link_items = explode(":", $link['url']); 265a52b82baSRemi Peyronnet // Adds mailto as it is implicitely allowed wih mail syntax. 266a52b82baSRemi Peyronnet if (!in_array($link_items[0], getSchemes() + array('mailto'))) { 267a52b82baSRemi Peyronnet $link['url'] = "Unauthorized URI scheme"; 268a52b82baSRemi Peyronnet } 2690166612cSunknown $link['name'] = $match['title']; 2701eab5a12SRémi Peyronnet if ($link['name'] == "") 2711eab5a12SRémi Peyronnet $link['name'] = $match['link']; 2720166612cSunknown $link['class'] = 'urlextern'; 273929f708eSThisNameIsNotAllowed if (strlen($match['target']) == 0) { 274929f708eSThisNameIsNotAllowed $match['target'] = $conf['target']['extern']; 275929f708eSThisNameIsNotAllowed } 2761eab5a12SRémi Peyronnet } else { 2770166612cSunknown // Internal 2780166612cSunknown $link = $this->dokuwiki_get_link($renderer, $match['link'], $match['title']); 2790166612cSunknown } 2800166612cSunknown $target = $match['target']; 2811eab5a12SRémi Peyronnet if ($target) 2821eab5a12SRémi Peyronnet $target = " target ='" . hsc($target) . "' "; 2830166612cSunknown 2840166612cSunknown $link['name'] = str_replace('\\\\', '<br />', $link['name']); //textbreak support 2851eab5a12SRémi Peyronnet if ($image != '') { 28627360b96Slisps $image = Doku_Handler_Parse_Media("{{" . $image . "}}"); 28703792051SRémi Peyronnet $image = $this->internalmedia($renderer, $image['src'], null, null, $image['width'], $image['height']); 28803792051SRémi Peyronnet $image = "<span class='plugin_button_image'>" . $image['name'] . "</span>"; 2890166612cSunknown } 2900166612cSunknown $text = "<a " . $target . " href='" . $link['url'] . "'><span class='plugin_button' style='" . hsc($match['css']) . "'>$image<span class='plugin_button_text ${link['class']}'>"; 2911eab5a12SRémi Peyronnet if (substr($match[0], -1) != "|") 2921eab5a12SRémi Peyronnet $text .= $link['name']; 2937db634ddSRémi Peyronnet $renderer->doc .= $text; 2947db634ddSRémi Peyronnet // Update meta data for move 2951eab5a12SRémi Peyronnet p_set_metadata( 2961eab5a12SRémi Peyronnet $ID, 2971eab5a12SRémi Peyronnet array( 298929f708eSThisNameIsNotAllowed 'relation' => array( 299929f708eSThisNameIsNotAllowed 'references' => array( 300929f708eSThisNameIsNotAllowed $match['link'] => true, 301929f708eSThisNameIsNotAllowed ), 302d731382bSRémi Peyronnet 'media' => array( 3037db634ddSRémi Peyronnet $match['image'] => true, 3047db634ddSRémi Peyronnet ), 305929f708eSThisNameIsNotAllowed ), 306929f708eSThisNameIsNotAllowed 'plugin_move' => array( 307929f708eSThisNameIsNotAllowed 'pages' => array( 308929f708eSThisNameIsNotAllowed $match['link'], 309929f708eSThisNameIsNotAllowed ), 3107db634ddSRémi Peyronnet 'medias' => array( 3117db634ddSRémi Peyronnet $match['image'], 3127db634ddSRémi Peyronnet ), 313929f708eSThisNameIsNotAllowed ), 3141eab5a12SRémi Peyronnet ) 3151eab5a12SRémi Peyronnet ); 3160166612cSunknown } 3170166612cSunknown } 3180166612cSunknown break; 3190166612cSunknown 3201eab5a12SRémi Peyronnet case DOKU_LEXER_UNMATCHED: 3211eab5a12SRémi Peyronnet $renderer->doc .= $renderer->_xmlEntities($match); 3221eab5a12SRémi Peyronnet break; 3231eab5a12SRémi Peyronnet case DOKU_LEXER_EXIT: 3241eab5a12SRémi Peyronnet $renderer->doc .= "</span></span></a>"; 3251eab5a12SRémi Peyronnet break; 3260166612cSunknown } 3270166612cSunknown return true; 3281eab5a12SRémi Peyronnet } elseif ($mode == 'metadata') { 3290166612cSunknown list($state, $match) = $data; 3300166612cSunknown switch ($state) { 3310166612cSunknown case DOKU_LEXER_SPECIAL: 3320166612cSunknown case DOKU_LEXER_ENTER: 3331eab5a12SRémi Peyronnet if (is_array($match)) { 3340166612cSunknown /** @var Doku_Renderer_metadata $renderer */ 3350166612cSunknown $renderer->internallink($match['link']); 3360166612cSunknown // I am assuming that when processing in handle(), you have stored 3370166612cSunknown // the link destination in $data[0] 3380166612cSunknown return true; 3390166612cSunknown } 3400166612cSunknown break; 3411eab5a12SRémi Peyronnet case DOKU_LEXER_UNMATCHED: 3421eab5a12SRémi Peyronnet break; 3431eab5a12SRémi Peyronnet case DOKU_LEXER_EXIT: 3441eab5a12SRémi Peyronnet break; 3450166612cSunknown } 3460166612cSunknown return true; 3470166612cSunknown } 3480166612cSunknown return false; 3490166612cSunknown } 3500166612cSunknown 35167df9406SRemi Peyronnet // TODO: the way we get links from dokuwiki should be completely rewritten 35267df9406SRemi Peyronnet // - target: rework repartition between parser/renderer to match dokuwiki guidelines 35367df9406SRemi Peyronnet // - try to override $xhtml->_formatLink($link); to avoid the code duplication of the functions below 35467df9406SRemi Peyronnet 3551eab5a12SRémi Peyronnet function dokuwiki_get_link(&$xhtml, $id, $name = NULL) 3561eab5a12SRémi Peyronnet { 3570166612cSunknown global $ID; 35867df9406SRemi Peyronnet 35967df9406SRemi Peyronnet if (link_isinterwiki($id)) { 36067df9406SRemi Peyronnet [$wikiName, $wikiUri] = sexplode('>', $id, 2, ''); 36167df9406SRemi Peyronnet $exists = null; 36267df9406SRemi Peyronnet //$url = $xhtml->_resolveInterWiki($wikiName, $wikiUri, $exists); 36367df9406SRemi Peyronnet $link = $this->interwikilink($xhtml, $id, $name, $wikiName, $wikiUri, true); 36467df9406SRemi Peyronnet return $link; 36567df9406SRemi Peyronnet } 36667df9406SRemi Peyronnet 3679eb108f2SRémi Peyronnet $resolveid = $id; // To prevent resolve_pageid to change $id value 368eaddf009SDaniel Dias Rodrigues $resolveid = (new PageResolver($ID))->resolveId($resolveid); 369eaddf009SDaniel Dias Rodrigues $exists = page_exists($resolveid); 3701e14146cSlisps if ($exists) { 3711e14146cSlisps return $this->internallink($xhtml, $id, $name); 3721e14146cSlisps } 3739eb108f2SRémi Peyronnet $resolveid = $id; 374eaddf009SDaniel Dias Rodrigues $resolveid = (new MediaResolver($ID))->resolveId($resolveid); 375eaddf009SDaniel Dias Rodrigues $exists = media_exists($resolveid); 3760166612cSunknown if ($exists) { 3770166612cSunknown return $this->internalmedia($xhtml, $id, $name); 3780166612cSunknown } else { 3790166612cSunknown return $this->internallink($xhtml, $id, $name); 3800166612cSunknown } 3810166612cSunknown } 3820166612cSunknown 38338605053SRemi Peyronnet // Copied and adapted from inc/parser/xhtml.php, function internallink (see RPHACK) 384ab97758cSPeter Stumm // Should use wl instead (from commons), but this won't do the trick for the name 3850166612cSunknown function internallink(&$xhtml, $id, $name = NULL, $search = NULL, $returnonly = false, $linktype = 'content') 386ab97758cSPeter Stumm { 387ab97758cSPeter Stumm global $conf; 388ab97758cSPeter Stumm global $ID; 389ab97758cSPeter Stumm global $INFO; 390ab97758cSPeter Stumm 391ab97758cSPeter Stumm 392ab97758cSPeter Stumm $params = ''; 393ab97758cSPeter Stumm $parts = explode('?', $id, 2); 394ab97758cSPeter Stumm if (count($parts) === 2) { 395ab97758cSPeter Stumm $id = $parts[0]; 396ab97758cSPeter Stumm $params = $parts[1]; 397ab97758cSPeter Stumm } 398ab97758cSPeter Stumm 399ab97758cSPeter Stumm // For empty $id we need to know the current $ID 400ab97758cSPeter Stumm // We need this check because _simpleTitle needs 401ab97758cSPeter Stumm // correct $id and resolve_pageid() use cleanID($id) 402ab97758cSPeter Stumm // (some things could be lost) 403ab97758cSPeter Stumm if ($id === '') { 404ab97758cSPeter Stumm $id = $ID; 405ab97758cSPeter Stumm } 406ab97758cSPeter Stumm 407ab97758cSPeter Stumm // RPHACK for get_link to work with local links '#id' 408ab97758cSPeter Stumm if (substr($id, 0, 1) === '#') { 409ab97758cSPeter Stumm $id = $ID . $id; 410ab97758cSPeter Stumm } 411ab97758cSPeter Stumm // ------- 412ab97758cSPeter Stumm 413ab97758cSPeter Stumm // default name is based on $id as given 414ab97758cSPeter Stumm $default = $xhtml->_simpleTitle($id); 415ab97758cSPeter Stumm 416ab97758cSPeter Stumm // now first resolve and clean up the $id 417eaddf009SDaniel Dias Rodrigues $id = (new PageResolver($ID))->resolveId($id); 418eaddf009SDaniel Dias Rodrigues $exists = page_exists($id); 419ab97758cSPeter Stumm 420ab97758cSPeter Stumm $name = $xhtml->_getLinkTitle($name, $default, $isImage, $id, $linktype); 421ab97758cSPeter Stumm if (!$isImage) { 422ab97758cSPeter Stumm if ($exists) { 423ab97758cSPeter Stumm $class = 'wikilink1'; 424ab97758cSPeter Stumm } else { 425ab97758cSPeter Stumm $class = 'wikilink2'; 426ab97758cSPeter Stumm $link['rel'] = 'nofollow'; 427ab97758cSPeter Stumm } 428ab97758cSPeter Stumm } else { 429ab97758cSPeter Stumm $class = 'media'; 430ab97758cSPeter Stumm } 431ab97758cSPeter Stumm 432ab97758cSPeter Stumm //keep hash anchor 43338605053SRemi Peyronnet $hash = NULL; 4341eab5a12SRémi Peyronnet if (str_contains($id, '#')) 4351eab5a12SRémi Peyronnet list($id, $hash) = explode('#', $id, 2); 4361eab5a12SRémi Peyronnet if (!empty($hash)) 4371eab5a12SRémi Peyronnet $hash = $xhtml->_headerToLink($hash); 438ab97758cSPeter Stumm 439ab97758cSPeter Stumm //prepare for formating 440ab97758cSPeter Stumm $link['target'] = $conf['target']['wiki']; 441ab97758cSPeter Stumm $link['style'] = ''; 442ab97758cSPeter Stumm $link['pre'] = ''; 443ab97758cSPeter Stumm $link['suf'] = ''; 444ab97758cSPeter Stumm // highlight link to current page 445ab97758cSPeter Stumm if ($id == $INFO['id']) { 446ab97758cSPeter Stumm $link['pre'] = '<span class="curid">'; 447ab97758cSPeter Stumm $link['suf'] = '</span>'; 448ab97758cSPeter Stumm } 449ab97758cSPeter Stumm $link['more'] = ''; 450ab97758cSPeter Stumm $link['class'] = $class; 451ab97758cSPeter Stumm $link['url'] = wl($id, $params); 452ab97758cSPeter Stumm $link['name'] = $name; 453ab97758cSPeter Stumm $link['title'] = $id; 454ab97758cSPeter Stumm //add search string 455ab97758cSPeter Stumm if ($search) { 456ab97758cSPeter Stumm ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 457ab97758cSPeter Stumm if (is_array($search)) { 458ab97758cSPeter Stumm $search = array_map('rawurlencode', $search); 459ab97758cSPeter Stumm $link['url'] .= 's[]=' . join('&s[]=', $search); 460ab97758cSPeter Stumm } else { 461ab97758cSPeter Stumm $link['url'] .= 's=' . rawurlencode($search); 462ab97758cSPeter Stumm } 463ab97758cSPeter Stumm } 464ab97758cSPeter Stumm 465ab97758cSPeter Stumm //keep hash 4661eab5a12SRémi Peyronnet if ($hash) 4671eab5a12SRémi Peyronnet $link['url'] .= '#' . $hash; 468ab97758cSPeter Stumm 469ab97758cSPeter Stumm return $link; 470ab97758cSPeter Stumm //output formatted 471ab97758cSPeter Stumm //if($returnonly){ 472ab97758cSPeter Stumm // return $this->_formatLink($link); 473ab97758cSPeter Stumm //}else{ 474ab97758cSPeter Stumm // $this->doc .= $this->_formatLink($link); 475ab97758cSPeter Stumm //} 476ab97758cSPeter Stumm } 477ab97758cSPeter Stumm 478ab97758cSPeter Stumm 4791eab5a12SRémi Peyronnet function internalmedia( 4801eab5a12SRémi Peyronnet &$xhtml, 4811eab5a12SRémi Peyronnet $src, 4821eab5a12SRémi Peyronnet $title = NULL, 4831eab5a12SRémi Peyronnet $align = NULL, 4841eab5a12SRémi Peyronnet $width = NULL, 4851eab5a12SRémi Peyronnet $height = NULL, 4861eab5a12SRémi Peyronnet $cache = NULL, 4871eab5a12SRémi Peyronnet $linking = NULL 4881eab5a12SRémi Peyronnet ) { 4890166612cSunknown global $ID; 490f044f88eSRemi Peyronnet 491f044f88eSRemi Peyronnet $hash = NULL; 4921eab5a12SRémi Peyronnet if (str_contains($src, '#')) 4931eab5a12SRémi Peyronnet list($src, $hash) = explode('#', $src, 2); 494eaddf009SDaniel Dias Rodrigues $src = (new MediaResolver($ID))->resolveId($src); 495eaddf009SDaniel Dias Rodrigues $exists = media_exists($src); 496ab97758cSPeter Stumm 4970166612cSunknown $noLink = false; 4980166612cSunknown $render = ($linking == 'linkonly') ? false : true; 4990166612cSunknown $link = $xhtml->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 500ab97758cSPeter Stumm 5010166612cSunknown list($ext, $mime, $dl) = mimetype($src, false); 5020166612cSunknown if (substr($mime, 0, 5) == 'image' && $render) { 5030166612cSunknown $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache), ($linking == 'direct')); 5040166612cSunknown } elseif ($mime == 'application/x-shockwave-flash' && $render) { 5050166612cSunknown // don't link flash movies 5060166612cSunknown $noLink = true; 5070166612cSunknown } else { 5080166612cSunknown // add file icons 5090166612cSunknown $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 5100166612cSunknown $link['class'] .= ' mediafile mf_' . $class; 5110166612cSunknown $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache), true); 5121eab5a12SRémi Peyronnet if ($exists) 5131eab5a12SRémi Peyronnet $link['title'] .= ' (' . filesize_h(filesize(mediaFN($src))) . ')'; 514ab97758cSPeter Stumm } 515ab97758cSPeter Stumm 5161eab5a12SRémi Peyronnet if ($hash) 5171eab5a12SRémi Peyronnet $link['url'] .= '#' . $hash; 518ab97758cSPeter Stumm 5190166612cSunknown //markup non existing files 5200166612cSunknown if (!$exists) { 5210166612cSunknown $link['class'] .= ' wikilink2'; 522ab97758cSPeter Stumm } 523ab97758cSPeter Stumm 5240166612cSunknown return $link; 5250166612cSunknown //output formatted 5260166612cSunknown //if ($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 5270166612cSunknown //else $this->doc .= $this->_formatLink($link); 528ab97758cSPeter Stumm } 52967df9406SRemi Peyronnet 53067df9406SRemi Peyronnet public function interwikilink(&$xhtml, $match, $name, $wikiName, $wikiUri, $returnonly = false) 53167df9406SRemi Peyronnet { 53267df9406SRemi Peyronnet global $conf; 53367df9406SRemi Peyronnet 53467df9406SRemi Peyronnet $link = []; 53567df9406SRemi Peyronnet $link['target'] = $conf['target']['interwiki']; 53667df9406SRemi Peyronnet $link['pre'] = ''; 53767df9406SRemi Peyronnet $link['suf'] = ''; 53867df9406SRemi Peyronnet $link['more'] = ''; 53967df9406SRemi Peyronnet $link['name'] = $xhtml->_getLinkTitle($name, $wikiUri, $isImage); 54067df9406SRemi Peyronnet $link['rel'] = ''; 54167df9406SRemi Peyronnet 54267df9406SRemi Peyronnet //get interwiki URL 54367df9406SRemi Peyronnet $exists = null; 54467df9406SRemi Peyronnet $url = $xhtml->_resolveInterWiki($wikiName, $wikiUri, $exists); 54567df9406SRemi Peyronnet 54667df9406SRemi Peyronnet if (!$isImage) { 54767df9406SRemi Peyronnet $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 54867df9406SRemi Peyronnet $link['class'] = "interwiki iw_$class"; 54967df9406SRemi Peyronnet } else { 55067df9406SRemi Peyronnet $link['class'] = 'media'; 55167df9406SRemi Peyronnet } 55267df9406SRemi Peyronnet 55367df9406SRemi Peyronnet //do we stay at the same server? Use local target 55467df9406SRemi Peyronnet if (strpos($url, DOKU_URL) === 0 || strpos($url, DOKU_BASE) === 0) { 55567df9406SRemi Peyronnet $link['target'] = $conf['target']['wiki']; 55667df9406SRemi Peyronnet } 55767df9406SRemi Peyronnet if ($exists !== null && !$isImage) { 55867df9406SRemi Peyronnet if ($exists) { 55967df9406SRemi Peyronnet $link['class'] .= ' wikilink1'; 56067df9406SRemi Peyronnet } else { 56167df9406SRemi Peyronnet $link['class'] .= ' wikilink2'; 56267df9406SRemi Peyronnet $link['rel'] .= ' nofollow'; 56367df9406SRemi Peyronnet } 56467df9406SRemi Peyronnet } 56567df9406SRemi Peyronnet if ($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 56667df9406SRemi Peyronnet 56767df9406SRemi Peyronnet $link['url'] = $url; 56867df9406SRemi Peyronnet $link['title'] = $xhtml->_xmlEntities($link['url']); 56967df9406SRemi Peyronnet 57067df9406SRemi Peyronnet // return non formatted link 57167df9406SRemi Peyronnet return $link; 57267df9406SRemi Peyronnet 57367df9406SRemi Peyronnet /* 57467df9406SRemi Peyronnet // output formatted 57567df9406SRemi Peyronnet if ($returnonly) { 57667df9406SRemi Peyronnet if ($url == '') return $link['name']; 57767df9406SRemi Peyronnet return $this->_formatLink($link); 57867df9406SRemi Peyronnet } elseif ($url == '') { 57967df9406SRemi Peyronnet $this->doc .= $link['name']; 58067df9406SRemi Peyronnet } else $this->doc .= $this->_formatLink($link); 58167df9406SRemi Peyronnet */ 58267df9406SRemi Peyronnet } 583ab97758cSPeter Stumm} 5841eab5a12SRémi Peyronnet 585ab97758cSPeter Stumm?> 586