1<?php 2/** 3 * Renderer for metadata 4 * 5 * @author Esther Brunner <wikidesign@gmail.com> 6 */ 7if(!defined('DOKU_INC')) die('meh.'); 8 9if ( !defined('DOKU_LF') ) { 10 // Some whitespace to help View > Source 11 define ('DOKU_LF',"\n"); 12} 13 14if ( !defined('DOKU_TAB') ) { 15 // Some whitespace to help View > Source 16 define ('DOKU_TAB',"\t"); 17} 18 19require_once DOKU_INC . 'inc/parser/renderer.php'; 20 21/** 22 * The Renderer 23 */ 24class Doku_Renderer_metadata extends Doku_Renderer { 25 26 var $doc = ''; 27 var $meta = array(); 28 var $persistent = array(); 29 30 var $headers = array(); 31 var $capture = true; 32 var $store = ''; 33 var $firstimage = ''; 34 35 function getFormat(){ 36 return 'metadata'; 37 } 38 39 function document_start(){ 40 // reset metadata to persistent values 41 $this->meta = $this->persistent; 42 } 43 44 function document_end(){ 45 // store internal info in metadata (notoc,nocache) 46 $this->meta['internal'] = $this->info; 47 48 if (!$this->meta['description']['abstract']){ 49 // cut off too long abstracts 50 $this->doc = trim($this->doc); 51 if (strlen($this->doc) > 500) 52 $this->doc = utf8_substr($this->doc, 0, 500).'…'; 53 $this->meta['description']['abstract'] = $this->doc; 54 } 55 56 $this->meta['relation']['firstimage'] = $this->firstimage; 57 } 58 59 function toc_additem($id, $text, $level) { 60 global $conf; 61 62 //only add items within configured levels 63 if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){ 64 // the TOC is one of our standard ul list arrays ;-) 65 $this->meta['description']['tableofcontents'][] = array( 66 'hid' => $id, 67 'title' => $text, 68 'type' => 'ul', 69 'level' => $level-$conf['toptoclevel']+1 70 ); 71 } 72 73 } 74 75 function header($text, $level, $pos) { 76 if (!$this->meta['title']) $this->meta['title'] = $text; 77 78 // add the header to the TOC 79 $hid = $this->_headerToLink($text,'true'); 80 $this->toc_additem($hid, $text, $level); 81 82 // add to summary 83 if ($this->capture && ($level > 1)) $this->doc .= DOKU_LF.$text.DOKU_LF; 84 } 85 86 function section_open($level){} 87 function section_close(){} 88 89 function cdata($text){ 90 if ($this->capture) $this->doc .= $text; 91 } 92 93 function p_open(){ 94 if ($this->capture) $this->doc .= DOKU_LF; 95 } 96 97 function p_close(){ 98 if ($this->capture){ 99 if (strlen($this->doc) > 250) $this->capture = false; 100 else $this->doc .= DOKU_LF; 101 } 102 } 103 104 function linebreak(){ 105 if ($this->capture) $this->doc .= DOKU_LF; 106 } 107 108 function hr(){ 109 if ($this->capture){ 110 if (strlen($this->doc) > 250) $this->capture = false; 111 else $this->doc .= DOKU_LF.'----------'.DOKU_LF; 112 } 113 } 114 115 function strong_open(){} 116 function strong_close(){} 117 118 function emphasis_open(){} 119 function emphasis_close(){} 120 121 function underline_open(){} 122 function underline_close(){} 123 124 function monospace_open(){} 125 function monospace_close(){} 126 127 function subscript_open(){} 128 function subscript_close(){} 129 130 function superscript_open(){} 131 function superscript_close(){} 132 133 function deleted_open(){} 134 function deleted_close(){} 135 136 /** 137 * Callback for footnote start syntax 138 * 139 * All following content will go to the footnote instead of 140 * the document. To achieve this the previous rendered content 141 * is moved to $store and $doc is cleared 142 * 143 * @author Andreas Gohr <andi@splitbrain.org> 144 */ 145 function footnote_open() { 146 if ($this->capture){ 147 // move current content to store and record footnote 148 $this->store = $this->doc; 149 $this->doc = ''; 150 } 151 } 152 153 /** 154 * Callback for footnote end syntax 155 * 156 * All rendered content is moved to the $footnotes array and the old 157 * content is restored from $store again 158 * 159 * @author Andreas Gohr 160 */ 161 function footnote_close() { 162 if ($this->capture){ 163 // restore old content 164 $this->doc = $this->store; 165 $this->store = ''; 166 } 167 } 168 169 function listu_open(){ 170 if ($this->capture) $this->doc .= DOKU_LF; 171 } 172 173 function listu_close(){ 174 if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false; 175 } 176 177 function listo_open(){ 178 if ($this->capture) $this->doc .= DOKU_LF; 179 } 180 181 function listo_close(){ 182 if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false; 183 } 184 185 function listitem_open($level){ 186 if ($this->capture) $this->doc .= str_repeat(DOKU_TAB, $level).'* '; 187 } 188 189 function listitem_close(){ 190 if ($this->capture) $this->doc .= DOKU_LF; 191 } 192 193 function listcontent_open(){} 194 function listcontent_close(){} 195 196 function unformatted($text){ 197 if ($this->capture) $this->doc .= $text; 198 } 199 200 function php($text){} 201 202 function phpblock($text){} 203 204 function html($text){} 205 206 function htmlblock($text){} 207 208 function preformatted($text){ 209 if ($this->capture) $this->doc .= $text; 210 } 211 212 function file($text){ 213 if ($this->capture){ 214 $this->doc .= DOKU_LF.$text; 215 if (strlen($this->doc) > 250) $this->capture = false; 216 else $this->doc .= DOKU_LF; 217 } 218 } 219 220 function quote_open(){ 221 if ($this->capture) $this->doc .= DOKU_LF.DOKU_TAB.'"'; 222 } 223 224 function quote_close(){ 225 if ($this->capture){ 226 $this->doc .= '"'; 227 if (strlen($this->doc) > 250) $this->capture = false; 228 else $this->doc .= DOKU_LF; 229 } 230 } 231 232 function code($text, $language = NULL){ 233 if ($this->capture){ 234 $this->doc .= DOKU_LF.$text; 235 if (strlen($this->doc) > 250) $this->capture = false; 236 else $this->doc .= DOKU_LF; 237 } 238 } 239 240 function acronym($acronym){ 241 if ($this->capture) $this->doc .= $acronym; 242 } 243 244 function smiley($smiley){ 245 if ($this->capture) $this->doc .= $smiley; 246 } 247 248 function entity($entity){ 249 if ($this->capture) $this->doc .= $entity; 250 } 251 252 function multiplyentity($x, $y){ 253 if ($this->capture) $this->doc .= $x.'×'.$y; 254 } 255 256 function singlequoteopening(){ 257 global $lang; 258 if ($this->capture) $this->doc .= $lang['singlequoteopening']; 259 } 260 261 function singlequoteclosing(){ 262 global $lang; 263 if ($this->capture) $this->doc .= $lang['singlequoteclosing']; 264 } 265 266 function apostrophe() { 267 global $lang; 268 if ($this->capture) $this->doc .= $lang['apostrophe']; 269 } 270 271 function doublequoteopening(){ 272 global $lang; 273 if ($this->capture) $this->doc .= $lang['doublequoteopening']; 274 } 275 276 function doublequoteclosing(){ 277 global $lang; 278 if ($this->capture) $this->doc .= $lang['doublequoteclosing']; 279 } 280 281 function camelcaselink($link) { 282 $this->internallink($link, $link); 283 } 284 285 function locallink($hash, $name = NULL){} 286 287 /** 288 * keep track of internal links in $this->meta['relation']['references'] 289 */ 290 function internallink($id, $name = NULL){ 291 global $ID; 292 293 if(is_array($name)) 294 $this->_firstimage($name['src']); 295 296 $default = $this->_simpleTitle($id); 297 298 // first resolve and clean up the $id 299 resolve_pageid(getNS($ID), $id, $exists); 300 list($page, $hash) = split('#', $id, 2); 301 302 // set metadata 303 $this->meta['relation']['references'][$page] = $exists; 304 // $data = array('relation' => array('isreferencedby' => array($ID => true))); 305 // p_set_metadata($id, $data); 306 307 // add link title to summary 308 if ($this->capture){ 309 $name = $this->_getLinkTitle($name, $default, $id); 310 $this->doc .= $name; 311 } 312 } 313 314 function externallink($url, $name = NULL){ 315 if(is_array($name)) 316 $this->_firstimage($name['src']); 317 318 if ($this->capture){ 319 if ($name) $this->doc .= $name; 320 else $this->doc .= '<'.$url.'>'; 321 } 322 } 323 324 function interwikilink($match, $name = NULL, $wikiName, $wikiUri){ 325 if(is_array($name)) 326 $this->_firstimage($name['src']); 327 328 if ($this->capture){ 329 list($wikiUri, $hash) = explode('#', $wikiUri, 2); 330 $name = $this->_getLinkTitle($name, $wikiName.'>'.$wikiUri); 331 $this->doc .= $name; 332 } 333 } 334 335 function windowssharelink($url, $name = NULL){ 336 if(is_array($name)) 337 $this->_firstimage($name['src']); 338 339 if ($this->capture){ 340 if ($name) $this->doc .= $name; 341 else $this->doc .= '<'.$url.'>'; 342 } 343 } 344 345 function emaillink($address, $name = NULL){ 346 if(is_array($name)) 347 $this->_firstimage($name['src']); 348 349 if ($this->capture){ 350 if ($name) $this->doc .= $name; 351 else $this->doc .= '<'.$address.'>'; 352 } 353 } 354 355 function internalmedia($src, $title=NULL, $align=NULL, $width=NULL, 356 $height=NULL, $cache=NULL, $linking=NULL){ 357 if ($this->capture && $title) $this->doc .= '['.$title.']'; 358 $this->_firstimage($src); 359 } 360 361 function externalmedia($src, $title=NULL, $align=NULL, $width=NULL, 362 $height=NULL, $cache=NULL, $linking=NULL){ 363 if ($this->capture && $title) $this->doc .= '['.$title.']'; 364 $this->_firstimage($src); 365 } 366 367 function rss($url,$params) { 368 $this->meta['relation']['haspart'][$url] = true; 369 370 $this->meta['date']['valid']['age'] = 371 isset($this->meta['date']['valid']['age']) ? 372 min($this->meta['date']['valid']['age'],$params['refresh']) : 373 $params['refresh']; 374 } 375 376 function table_open($maxcols = NULL, $numrows = NULL){} 377 function table_close(){} 378 379 function tablerow_open(){} 380 function tablerow_close(){} 381 382 function tableheader_open($colspan = 1, $align = NULL){} 383 function tableheader_close(){} 384 385 function tablecell_open($colspan = 1, $align = NULL){} 386 function tablecell_close(){} 387 388 //---------------------------------------------------------- 389 // Utils 390 391 /** 392 * Removes any Namespace from the given name but keeps 393 * casing and special chars 394 * 395 * @author Andreas Gohr <andi@splitbrain.org> 396 */ 397 function _simpleTitle($name){ 398 global $conf; 399 400 if(is_array($name)) return ''; 401 402 if($conf['useslash']){ 403 $nssep = '[:;/]'; 404 }else{ 405 $nssep = '[:;]'; 406 } 407 $name = preg_replace('!.*'.$nssep.'!','',$name); 408 //if there is a hash we use the anchor name only 409 $name = preg_replace('!.*#!','',$name); 410 return $name; 411 } 412 413 /** 414 * Creates a linkid from a headline 415 * 416 * @param string $title The headline title 417 * @param boolean $create Create a new unique ID? 418 * @author Andreas Gohr <andi@splitbrain.org> 419 */ 420 function _headerToLink($title, $create=false) { 421 $title = str_replace(':','',cleanID($title)); 422 $title = ltrim($title,'0123456789._-'); 423 if(empty($title)) $title='section'; 424 425 if($create){ 426 // make sure tiles are unique 427 $num = ''; 428 while(in_array($title.$num,$this->headers)){ 429 ($num) ? $num++ : $num = 1; 430 } 431 $title = $title.$num; 432 $this->headers[] = $title; 433 } 434 435 return $title; 436 } 437 438 /** 439 * Construct a title and handle images in titles 440 * 441 * @author Harry Fuecks <hfuecks@gmail.com> 442 */ 443 function _getLinkTitle($title, $default, $id=NULL) { 444 global $conf; 445 446 $isImage = false; 447 if (is_null($title)){ 448 if (useHeading('content') && $id){ 449 $heading = p_get_first_heading($id,false); 450 if ($heading) return $heading; 451 } 452 return $default; 453 } else if (is_string($title)){ 454 return $title; 455 } else if (is_array($title)){ 456 return '['.$title['title'].']'; 457 } 458 } 459 460 function _firstimage($src){ 461 if($this->firstimage) return; 462 global $ID; 463 464 list($src,$hash) = explode('#',$src,2); 465 if(!preg_match('/^https?:\/\//i',$src)){ 466 resolve_mediaid(getNS($ID),$src, $exists); 467 } 468 if(preg_match('/.(jpe?g|gif|png)$/i',$src)){ 469 $this->firstimage = $src; 470 } 471 } 472} 473 474//Setup VIM: ex: et ts=4 enc=utf-8 : 475