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 42 $this->headers = array(); 43 44 // external pages are missing create date 45 if(!$this->persistent['date']['created']){ 46 $this->persistent['date']['created'] = filectime(wikiFN($ID)); 47 } 48 if(!isset($this->persistent['user'])){ 49 $this->persistent['user'] = ''; 50 } 51 if(!isset($this->persistent['creator'])){ 52 $this->persistent['creator'] = ''; 53 } 54 // reset metadata to persistent values 55 $this->meta = $this->persistent; 56 } 57 58 function document_end(){ 59 global $ID; 60 61 // store internal info in metadata (notoc,nocache) 62 $this->meta['internal'] = $this->info; 63 64 if (!isset($this->meta['description']['abstract'])){ 65 // cut off too long abstracts 66 $this->doc = trim($this->doc); 67 if (strlen($this->doc) > 500) 68 $this->doc = utf8_substr($this->doc, 0, 500).'…'; 69 $this->meta['description']['abstract'] = $this->doc; 70 } 71 72 $this->meta['relation']['firstimage'] = $this->firstimage; 73 74 if(!isset($this->meta['date']['modified'])){ 75 $this->meta['date']['modified'] = filemtime(wikiFN($ID)); 76 } 77 78 } 79 80 function toc_additem($id, $text, $level) { 81 global $conf; 82 83 //only add items within configured levels 84 if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){ 85 // the TOC is one of our standard ul list arrays ;-) 86 $this->meta['description']['tableofcontents'][] = array( 87 'hid' => $id, 88 'title' => $text, 89 'type' => 'ul', 90 'level' => $level-$conf['toptoclevel']+1 91 ); 92 } 93 94 } 95 96 function header($text, $level, $pos) { 97 if (!isset($this->meta['title'])) $this->meta['title'] = $text; 98 99 // add the header to the TOC 100 $hid = $this->_headerToLink($text,'true'); 101 $this->toc_additem($hid, $text, $level); 102 103 // add to summary 104 if ($this->capture && ($level > 1)) $this->doc .= DOKU_LF.$text.DOKU_LF; 105 } 106 107 function section_open($level){} 108 function section_close(){} 109 110 function cdata($text){ 111 if ($this->capture) $this->doc .= $text; 112 } 113 114 function p_open(){ 115 if ($this->capture) $this->doc .= DOKU_LF; 116 } 117 118 function p_close(){ 119 if ($this->capture){ 120 if (strlen($this->doc) > 250) $this->capture = false; 121 else $this->doc .= DOKU_LF; 122 } 123 } 124 125 function linebreak(){ 126 if ($this->capture) $this->doc .= DOKU_LF; 127 } 128 129 function hr(){ 130 if ($this->capture){ 131 if (strlen($this->doc) > 250) $this->capture = false; 132 else $this->doc .= DOKU_LF.'----------'.DOKU_LF; 133 } 134 } 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 preformatted($text){ 201 if ($this->capture) $this->doc .= $text; 202 } 203 204 function file($text, $lang = null, $file = null){ 205 if ($this->capture){ 206 $this->doc .= DOKU_LF.$text; 207 if (strlen($this->doc) > 250) $this->capture = false; 208 else $this->doc .= DOKU_LF; 209 } 210 } 211 212 function quote_open(){ 213 if ($this->capture) $this->doc .= DOKU_LF.DOKU_TAB.'"'; 214 } 215 216 function quote_close(){ 217 if ($this->capture){ 218 $this->doc .= '"'; 219 if (strlen($this->doc) > 250) $this->capture = false; 220 else $this->doc .= DOKU_LF; 221 } 222 } 223 224 function code($text, $language = null, $file = null){ 225 if ($this->capture){ 226 $this->doc .= DOKU_LF.$text; 227 if (strlen($this->doc) > 250) $this->capture = false; 228 else $this->doc .= DOKU_LF; 229 } 230 } 231 232 function acronym($acronym){ 233 if ($this->capture) $this->doc .= $acronym; 234 } 235 236 function smiley($smiley){ 237 if ($this->capture) $this->doc .= $smiley; 238 } 239 240 function entity($entity){ 241 if ($this->capture) $this->doc .= $entity; 242 } 243 244 function multiplyentity($x, $y){ 245 if ($this->capture) $this->doc .= $x.'×'.$y; 246 } 247 248 function singlequoteopening(){ 249 global $lang; 250 if ($this->capture) $this->doc .= $lang['singlequoteopening']; 251 } 252 253 function singlequoteclosing(){ 254 global $lang; 255 if ($this->capture) $this->doc .= $lang['singlequoteclosing']; 256 } 257 258 function apostrophe() { 259 global $lang; 260 if ($this->capture) $this->doc .= $lang['apostrophe']; 261 } 262 263 function doublequoteopening(){ 264 global $lang; 265 if ($this->capture) $this->doc .= $lang['doublequoteopening']; 266 } 267 268 function doublequoteclosing(){ 269 global $lang; 270 if ($this->capture) $this->doc .= $lang['doublequoteclosing']; 271 } 272 273 function camelcaselink($link) { 274 $this->internallink($link, $link); 275 } 276 277 function locallink($hash, $name = null){} 278 279 /** 280 * keep track of internal links in $this->meta['relation']['references'] 281 */ 282 function internallink($id, $name = null){ 283 global $ID; 284 285 if(is_array($name)) { 286 $this->_firstimage($name['src']); 287 if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']); 288 } 289 290 $parts = explode('?', $id, 2); 291 if (count($parts) === 2) { 292 $id = $parts[0]; 293 } 294 295 $default = $this->_simpleTitle($id); 296 297 // first resolve and clean up the $id 298 resolve_pageid(getNS($ID), $id, $exists); 299 list($page, $hash) = explode('#', $id, 2); 300 301 // set metadata 302 $this->meta['relation']['references'][$page] = $exists; 303 // $data = array('relation' => array('isreferencedby' => array($ID => true))); 304 // p_set_metadata($id, $data); 305 306 // add link title to summary 307 if ($this->capture){ 308 $name = $this->_getLinkTitle($name, $default, $id); 309 $this->doc .= $name; 310 } 311 } 312 313 function externallink($url, $name = null){ 314 if(is_array($name)) { 315 $this->_firstimage($name['src']); 316 if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']); 317 } 318 319 if ($this->capture){ 320 $this->doc .= $this->_getLinkTitle($name, '<' . $url . '>'); 321 } 322 } 323 324 function interwikilink($match, $name = null, $wikiName, $wikiUri){ 325 if(is_array($name)) { 326 $this->_firstimage($name['src']); 327 if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']); 328 } 329 330 if ($this->capture){ 331 list($wikiUri, $hash) = explode('#', $wikiUri, 2); 332 $name = $this->_getLinkTitle($name, $wikiUri); 333 $this->doc .= $name; 334 } 335 } 336 337 function windowssharelink($url, $name = null){ 338 if(is_array($name)) { 339 $this->_firstimage($name['src']); 340 if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']); 341 } 342 343 if ($this->capture){ 344 if ($name) $this->doc .= $name; 345 else $this->doc .= '<'.$url.'>'; 346 } 347 } 348 349 function emaillink($address, $name = null){ 350 if(is_array($name)) { 351 $this->_firstimage($name['src']); 352 if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']); 353 } 354 355 if ($this->capture){ 356 if ($name) $this->doc .= $name; 357 else $this->doc .= '<'.$address.'>'; 358 } 359 } 360 361 function internalmedia($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 $this->_recordMediaUsage($src); 366 } 367 368 function externalmedia($src, $title=null, $align=null, $width=null, 369 $height=null, $cache=null, $linking=null){ 370 if ($this->capture && $title) $this->doc .= '['.$title.']'; 371 $this->_firstimage($src); 372 } 373 374 function rss($url,$params) { 375 $this->meta['relation']['haspart'][$url] = true; 376 377 $this->meta['date']['valid']['age'] = 378 isset($this->meta['date']['valid']['age']) ? 379 min($this->meta['date']['valid']['age'],$params['refresh']) : 380 $params['refresh']; 381 } 382 383 //---------------------------------------------------------- 384 // Utils 385 386 /** 387 * Removes any Namespace from the given name but keeps 388 * casing and special chars 389 * 390 * @author Andreas Gohr <andi@splitbrain.org> 391 */ 392 function _simpleTitle($name){ 393 global $conf; 394 395 if(is_array($name)) return ''; 396 397 if($conf['useslash']){ 398 $nssep = '[:;/]'; 399 }else{ 400 $nssep = '[:;]'; 401 } 402 $name = preg_replace('!.*'.$nssep.'!','',$name); 403 //if there is a hash we use the anchor name only 404 $name = preg_replace('!.*#!','',$name); 405 return $name; 406 } 407 408 /** 409 * Creates a linkid from a headline 410 * 411 * @param string $title The headline title 412 * @param boolean $create Create a new unique ID? 413 * @author Andreas Gohr <andi@splitbrain.org> 414 */ 415 function _headerToLink($title, $create=false) { 416 if($create){ 417 return sectionID($title,$this->headers); 418 }else{ 419 $check = false; 420 return sectionID($title,$check); 421 } 422 } 423 424 /** 425 * Construct a title and handle images in titles 426 * 427 * @author Harry Fuecks <hfuecks@gmail.com> 428 */ 429 function _getLinkTitle($title, $default, $id=null) { 430 global $conf; 431 432 $isImage = false; 433 if (is_array($title)){ 434 if($title['title']) return '['.$title['title'].']'; 435 } else if (is_null($title) || trim($title)==''){ 436 if (useHeading('content') && $id){ 437 $heading = p_get_first_heading($id,METADATA_DONT_RENDER); 438 if ($heading) return $heading; 439 } 440 return $default; 441 } else { 442 return $title; 443 } 444 } 445 446 function _firstimage($src){ 447 if($this->firstimage) return; 448 global $ID; 449 450 list($src,$hash) = explode('#',$src,2); 451 if(!media_isexternal($src)){ 452 resolve_mediaid(getNS($ID),$src, $exists); 453 } 454 if(preg_match('/.(jpe?g|gif|png)$/i',$src)){ 455 $this->firstimage = $src; 456 } 457 } 458 459 function _recordMediaUsage($src) { 460 global $ID; 461 462 list ($src, $hash) = explode('#', $src, 2); 463 if (media_isexternal($src)) return; 464 resolve_mediaid(getNS($ID), $src, $exists); 465 $this->meta['relation']['media'][$src] = $exists; 466 } 467} 468 469//Setup VIM: ex: et ts=4 : 470