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