1<?php 2if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../../').'/'); 3if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 4if(!defined('DOKU_REL')) define('DOKU_REL', '/dokuwiki/'); 5require_once(DOKU_PLUGIN.'syntax.php'); 6require_once(DOKU_PLUGIN.'autolink4/consts.php'); 7 8//TODO: Bugs: 9// - lexer combines all plugin search strings. If you have too many links in a namespace (something between 500 and 1000), 10// the regex gets too long. This has to be changed to an action plugin to fix that, and we can combined regexes in chunks. 11// - How does this play with ORPHANSWANTED? 12//TODO: Document: 13// - Regexes always match the thing that started first, or the longest string if there are two matches. 14// 'Mother in law' - [in law][Mother] => Mother 15// 'The Mother in law' - [Mother in law][Mother] => Mother in law 16 17/** 18 * Autolink 4 DokuWiki plugin 19 * 20 * @license MIT 21 * @author Eli Fenton 22 */ 23class syntax_plugin_autolink4_regex extends DokuWiki_Syntax_Plugin { 24 use autotooltip4_consts; 25 26 /** @type helper_plugin_autotooltip $tooltip */ 27 private $tooltip; 28 /** @type helper_plugin_autolink4 $tooltip */ 29 private $helper; 30 31 private $foundMatches; 32 33 public function __construct() { 34 if (!plugin_isdisabled('autotooltip')) { 35 $this->tooltip = plugin_load('helper', 'autotooltip'); 36 } 37 38 $this->helper = plugin_load('helper', 'autolink4'); 39 $this->helper->loadAndProcessConfigFile(); 40 } 41 42 43 /** 44 * @return string 45 */ 46 function getType() { 47 return 'substition'; 48 } 49 50 51 /** 52 * @return string 53 */ 54 function getPType() { 55 return 'normal'; 56 } 57 58 59 /** 60 * @return int 61 */ 62 function getSort() { 63 // Try not to interfere with any other lexer patterns. 64 return 999; 65 } 66 67 68 /** 69 * @param $mode 70 */ 71 function connectTo($mode) { 72 global $ID; 73 $ns = getNS($ID); 74 75 foreach ($this->helper->getSubs() as $s) { 76 // Check that it's in the right namespace, and skip links to the current page. 77 if ($this->_inNS($ns, $s[self::$IN]) && !$this->_isSamePage($s[self::$TO], $ID)) { 78 $this->Lexer->addSpecialPattern($s[self::$MATCH], $mode, 'plugin_autolink4_regex'); 79 } 80 } 81 } 82 83 84 /** 85 * Handle the found text, and send it off to render(). 86 * 87 * @param string $match - The found text, from addSpecialPattern. 88 * @param int $state - The DokuWiki event state. 89 * @param int $pos - The position in the full text. 90 * @param Doku_Handler $handler 91 * @return array|string 92 */ 93 function handle($match, $state, $pos, Doku_Handler $handler) { 94 if ($this->foundMatches[$match]) { 95 return $match; 96 } 97 98 // Load from cache 99 if (isset($this->helper->getSimpleSubs()[$match])) { 100 $s = $this->helper->getSimpleSubs()[$match]; 101 if ($s[self::$ONCE]) { 102 $this->foundMatches[$match] = true; 103 } 104 105 return $s; 106 } 107 108 // Annoyingly, there's no way (I know of) to determine which match sent us here, so we have to loop through the 109 // whole list. 110 foreach ($this->helper->getRegexSubs() as &$s) { 111 if ($s[self::$ONCE]) { 112 $this->foundMatches[$match] = true; 113 } 114 115 if (preg_match('/^' . $s[self::$MATCH] . '$/', $match)) { 116 // Add all found matches to simpleSubs, so we don't have to loop more than once for the same string. 117 $mod = null; 118 if (!isset($this->helper->getSimpleSubs()[$match])) { 119 $mod = $s; 120 $mod[self::$TEXT] = $match; 121 //TODO: Do this differently (cache locally). 122 $this->helper->getSimpleSubs()[$match] = $mod; 123 } 124 125 return $mod; 126 } 127 } 128 129 return $match; 130 } 131 132 133 /** 134 * Render the replaced links. 135 * 136 * @param string $mode 137 * @param Doku_Renderer|Doku_Renderer_metadata $renderer 138 * @param array|string $data - Data from handle() 139 * @return bool 140 */ 141 function render($mode, Doku_Renderer $renderer, $data) { 142 if (is_string($data)) { 143 $renderer->doc .= $data; 144 } 145 else if ($mode == 'xhtml') { 146 if ($this->tooltip && $data[self::$TOOLTIP]) { 147 $renderer->doc .= $this->tooltip->forWikilink($data[self::$TO], $data[self::$TEXT]); 148 } 149 else { 150 $renderer->internallink($data[self::$TO], $data[self::$TEXT]); 151 } 152 } 153 else { 154 if (!$renderer->capture) { 155 return false; 156 } 157 $renderer->doc .= $data[self::$TEXT]; 158 } 159 return true; 160 } 161 162 163 /** 164 * Is one namespace inside another. 165 * 166 * @param string $ns - Search inside this namespace. 167 * @param string $test - Look for this namespace. 168 * @return bool 169 */ 170 function _inNS($ns, $test) { 171 $len = strlen($test); 172 return !$len || substr($ns, 0, $len) == $test; 173 } 174 175 176 /** 177 * Are these two the same page> 178 * 179 * @param string $p1 - One page. 180 * @param string $p2 - Another page. 181 * @return bool 182 */ 183 function _isSamePage($p1, $p2) { 184 return $p1 == $p2; 185 } 186} 187