1<?php 2/** 3 * linksenhanced Plugin - Render Link Title using Wiki Markup 4 * Heavily based on the standard linking code. 5 * 6 * Usage: 7 * 8 * [[check^http://www.dokuwiki.org|www.dokuwiki.org]] 9 * [[render noparagraph^http://www.dokuwiki.org|<faicon fa fe-euro> FontAwesome Code]] 10 * [[render^http://www.dokuwiki.org|//Formatted Output, probably within a paragraph//]] 11 * 12 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 13 * @author Andreas Böhler <dev@aboehler.at> 14 */ 15 16if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 17if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 18require_once(DOKU_PLUGIN.'syntax.php'); 19 20/** 21 * All DokuWiki plugins to extend the parser/rendering mechanism 22 * need to inherit from this class 23 */ 24class syntax_plugin_linksenhanced_link extends DokuWiki_Syntax_Plugin { 25 26 function getType() { 27 return 'substition'; 28 } 29 30 function getPType() { 31 return 'normal'; 32 } 33 34 function getAllowedTypes() { 35 return array('container','substition','protected','disabled','paragraphs','formatting'); 36 } 37 38 function getSort() { 39 return 202; 40 } 41 42 function connectTo($mode) { 43 $this->Lexer->addSpecialPattern('\[\[(?:(?:[^[\]]*?\[.*?\])|.*?)\]\]',$mode,'plugin_linksenhanced_link'); 44 } 45 46 /** 47 * Handle the match. Use either the standard linking mechanism or, when enabled, 48 * pass the title through the parser 49 */ 50 function handle($match, $state, $pos, Doku_Handler $handler) { 51 global $ID; 52 53 $match = substr($match, 2, -2); 54 $link = explode('|',$match,2); 55 $nopara = false; 56 $optionsSet = explode('^', $link[0], 2); 57 $options = array('render' => false, 58 'noparagraph' => false, 59 'check' => false, 60 'class' => false, 61 'target' => false); 62 if(isset($optionsSet[1])) 63 { 64 $link[0] = $optionsSet[1]; 65 $opts = explode(' ', $optionsSet[0]); 66 foreach($opts as $opt) 67 { 68 if(strpos($opt, 'class') === 0) 69 $options['class'] = explode('=', $opt)[1]; 70 elseif(strpos($opt, 'target') === 0) 71 $options['target'] = explode('=', $opt)[1]; 72 else 73 $options[$opt] = true; 74 } 75 } 76 77 if($this->getConf('check_all_external') == 1) 78 { 79 if($this->getConf('check_only_namespace') != '') 80 { 81 $namespaces = explode(',', $this->getConf('check_only_namespace')); 82 foreach($namespaces as $namespace) 83 { 84 if(getNS($ID) == $namespace) 85 $options['check'] = true; 86 } 87 } 88 else 89 { 90 $options['check'] = true; 91 } 92 } 93 94 if($options['render'] && isset($link[1])) 95 { 96 $link[1] = $this->render_text($link[1]); 97 if($options['noparagraph']) 98 { 99 $link[1] = str_replace('</p>', '', str_replace('<p>', '', $link[1])); 100 } 101 if($link[1] === '') 102 $link[1] = null; 103 } 104 else if ( isset($link[1]) && preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) { 105 // If the title is an image, convert it to an array containing the image details 106 $link[1] = Doku_Handler_Parse_Media($link[1]); 107 } 108 else if(!isset($link[1])) 109 { 110 $link[1] = null; 111 } 112 113 $link[0] = trim($link[0]); 114 115 //decide which kind of link it is 116 117 if ( preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link[0]) ) { 118 $type = 'interwiki'; 119 }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) { 120 $type = 'windowsshare'; 121 }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) { 122 $type = 'external'; 123 }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) { 124 $type = 'email'; 125 }elseif ( preg_match('!^#.+!',$link[0]) ){ 126 $type = 'local'; 127 $link[0] = substr($link[0],1); 128 }else{ 129 $type = 'internal'; 130 } 131 132 return array($type, $link, $options); 133 } 134 135 /** 136 * Create output. This is largely based on the internal linking mechanism. 137 */ 138 function render($mode, Doku_Renderer $renderer, $data) { 139 if (empty($data)) return false; 140 global $conf; 141 global $ID; 142 global $INFO; 143 global $lang; 144 145 list($type, $link, $options) = $data; 146 147 $url = $link[0]; 148 $name = $link[1]; 149 150 if($mode == 'metadata') 151 { 152 if($type == 'external') 153 { 154 $schemes = getSchemes(); 155 list($scheme) = explode('://',$url); 156 $scheme = strtolower($scheme); 157 if(!in_array($scheme,$schemes)) $url = ''; 158 159 // is there still an URL? 160 if(!$url){ 161 return true; 162 } 163 164 $renderer->meta['plugin_linksenhanced']['links'][] = $url; 165 } 166 return true; 167 } 168 169 if($mode == 'xhtml') { 170 switch($type) 171 { 172 case 'interwiki': 173 $interwiki = explode('>',$link[0],2); 174 $wikiName = strtolower($interwiki[0]); 175 $wikiUri = $interwiki[1]; 176 $link = array(); 177 $link['target'] = $conf['target']['interwiki']; 178 $link['pre'] = ''; 179 $link['suf'] = ''; 180 $link['more'] = ''; 181 if($name === null || !$options['render']) 182 $link['name'] = $renderer->_getLinkTitle($name, $wikiUri, $isImage); 183 else 184 $link['name'] = $name; 185 186 //get interwiki URL 187 $exists = null; 188 $url = $renderer->_resolveInterWiki($wikiName, $wikiUri, $exists); 189 190 if(!$isImage) { 191 $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 192 $link['class'] = "interwiki iw_$class"; 193 } else { 194 $link['class'] = 'media'; 195 } 196 197 //do we stay at the same server? Use local target 198 if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 199 $link['target'] = $conf['target']['wiki']; 200 } 201 202 if($options['target'] !== false) 203 $link['target'] = $options['target']; 204 205 if($exists !== null && !$isImage) { 206 if($exists) { 207 $link['class'] .= ' wikilink1'; 208 } else { 209 $link['class'] .= ' wikilink2'; 210 $link['rel'] = 'nofollow'; 211 } 212 } 213 if($options['class'] !== false) 214 $link['class'] = $options['class']; 215 216 $link['url'] = $url; 217 $link['title'] = htmlspecialchars($link['url']); 218 219 //output formatted 220 $renderer->doc .= $renderer->_formatLink($link); 221 break; 222 case 'windowsshare': 223 //simple setup 224 $link['target'] = $conf['target']['windows']; 225 $link['pre'] = ''; 226 $link['suf'] = ''; 227 $link['style'] = ''; 228 229 if($options['target'] !== false) 230 $link['target'] = $options['target']; 231 232 if($name === null || !$options['render']) 233 $link['name'] = $renderer->_getLinkTitle($name, $url, $isImage); 234 else 235 $link['name'] = $name; 236 if ( !$isImage ) { 237 $link['class'] = 'windows'; 238 } else { 239 $link['class'] = 'media'; 240 } 241 242 if($options['class'] !== false) 243 $link['class'] = $options['class']; 244 245 $link['title'] = $renderer->_xmlEntities($url); 246 $url = str_replace('\\','/',$url); 247 $url = 'file:///'.$url; 248 $link['url'] = $url; 249 250 //output formatted 251 $renderer->doc .= $renderer->_formatLink($link); 252 break; 253 case 'external': 254 if($name === null || !$options['render']) 255 $name = $renderer->_getLinkTitle($name, $url, $isImage); 256 257 // url might be an attack vector, only allow registered protocols 258 $schemes = getSchemes(); 259 list($scheme) = explode('://',$url); 260 $scheme = strtolower($scheme); 261 if(!in_array($scheme,$schemes)) $url = ''; 262 263 // is there still an URL? 264 if(!$url){ 265 $renderer->doc .= $name; 266 return; 267 } 268 269 // set class 270 if ( !$isImage ) { 271 $class='urlextern'; 272 } else { 273 $class='media'; 274 } 275 276 if($options['class'] !== false) 277 $class = $options['class']; 278 279 //prepare for formating 280 $link['target'] = $conf['target']['extern']; 281 $link['style'] = ''; 282 $link['pre'] = ''; 283 $link['suf'] = ''; 284 $link['more'] = ''; 285 $link['class'] = $class; 286 $link['url'] = $url; 287 288 if($options['target'] !== false) 289 $link['target'] = $options['target']; 290 291 if($options['check'] !== false) 292 { 293 $link['class'] = 'plugin_linksenhanced_pending'; 294 } 295 $link['name'] = $name; 296 $link['title'] = $renderer->_xmlEntities($url); 297 if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 298 299 //output formatted 300 $renderer->doc .= $renderer->_formatLink($link); 301 break; 302 case 'email': 303 $link = array(); 304 $link['target'] = ''; 305 $link['pre'] = ''; 306 $link['suf'] = ''; 307 $link['style'] = ''; 308 $link['more'] = ''; 309 310 if($name === null || !$options['render']) 311 $name = $renderer->_getLinkTitle($name, '', $isImage); 312 if ( !$isImage ) { 313 $link['class']='mail'; 314 } else { 315 $link['class']='media'; 316 } 317 318 if($options['class'] !== false) 319 $link['class'] = $options['class']; 320 321 $url = $renderer->_xmlEntities($url); 322 $url = obfuscate($url); 323 $title = $url; 324 325 if(empty($name)){ 326 $name = $url; 327 } 328 329 if($conf['mailguard'] == 'visible') $url = rawurlencode($url); 330 331 $link['url'] = 'mailto:'.$url; 332 $link['name'] = $name; 333 $link['title'] = $title; 334 335 //output formatted 336 $renderer->doc .= $renderer->_formatLink($link); 337 break; 338 case 'local': 339 if($name === null || !$options['render']) 340 $name = $renderer->_getLinkTitle($name, $url, $isImage); 341 $url = $renderer->_headerToLink($url); 342 $title = $ID.' ↵'; 343 if($options['class'] !== false) 344 $class = $options['class']; 345 else 346 $class = "wikilink1"; 347 $renderer->doc .= '<a href="#'.$url.'" title="'.$title.'" class="'.$class.'">'; 348 $renderer->doc .= $name; 349 $renderer->doc .= '</a>'; 350 break; 351 case 'internal': 352 $id = $url; 353 $params = ''; 354 $linktype = 'content'; 355 $parts = explode('?', $id, 2); 356 if (count($parts) === 2) { 357 $id = $parts[0]; 358 $params = $parts[1]; 359 } 360 361 // For empty $id we need to know the current $ID 362 // We need this check because _simpleTitle needs 363 // correct $id and resolve_pageid() use cleanID($id) 364 // (some things could be lost) 365 if ($id === '') { 366 $id = $ID; 367 } 368 369 // default name is based on $id as given 370 $default = $renderer->_simpleTitle($id); 371 372 // now first resolve and clean up the $id 373 resolve_pageid(getNS($ID),$id,$exists); 374 375 if($name === null || !$options['render']) 376 $name = $renderer->_getLinkTitle($name, $default, $isImage, $id, $linktype); 377 if ( !$isImage ) { 378 if ( $exists ) { 379 $class='wikilink1'; 380 } else { 381 $class='wikilink2'; 382 $link['rel']='nofollow'; 383 } 384 } else { 385 $class='media'; 386 } 387 388 if($options['class'] !== false) 389 $class = $options['class']; 390 391 //keep hash anchor 392 @list($id,$hash) = explode('#',$id,2); 393 if(!empty($hash)) $hash = $renderer->_headerToLink($hash); 394 395 //prepare for formating 396 $link['target'] = $conf['target']['wiki']; 397 $link['style'] = ''; 398 $link['pre'] = ''; 399 $link['suf'] = ''; 400 // highlight link to current page 401 if ($id == $INFO['id']) { 402 $link['pre'] = '<span class="curid">'; 403 $link['suf'] = '</span>'; 404 } 405 $link['more'] = ''; 406 $link['class'] = $class; 407 $link['url'] = wl($id, $params); 408 $link['name'] = $name; 409 $link['title'] = $id; 410 411 if($options['target'] !== false) 412 $link['target'] = $options['target']; 413 //add search string 414 if($search){ 415 ($conf['userewrite']) ? $link['url'].='?' : $link['url'].='&'; 416 if(is_array($search)){ 417 $search = array_map('rawurlencode',$search); 418 $link['url'] .= 's[]='.join('&s[]=',$search); 419 }else{ 420 $link['url'] .= 's='.rawurlencode($search); 421 } 422 } 423 424 //keep hash 425 if($hash) $link['url'].='#'.$hash; 426 427 //output formatted 428 if($returnonly){ 429 return $renderer->_formatLink($link); 430 }else{ 431 $renderer->doc .= $renderer->_formatLink($link); 432 } 433 break; 434 } 435 } 436 return false; 437 } 438} 439