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