1<?php 2/** 3 * Utilities for collecting data from config files 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Harry Fuecks <hfuecks@gmail.com> 7 * @author Andreas Gohr <andi@splitbrain.org> 8 */ 9 10 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); 11 12 require_once(DOKU_INC.'inc/confutils.php'); 13 require_once(DOKU_INC.'inc/pageutils.php'); 14 require_once(DOKU_INC.'inc/pluginutils.php'); 15 16/** 17 * Returns the parsed Wikitext in XHTML for the given id and revision. 18 * 19 * If $excuse is true an explanation is returned if the file 20 * wasn't found 21 * 22 * @author Andreas Gohr <andi@splitbrain.org> 23 */ 24function p_wiki_xhtml($id, $rev='', $excuse=true){ 25 $file = wikiFN($id,$rev); 26 $ret = ''; 27 28 //ensure $id is in global $ID (needed for parsing) 29 global $ID; 30 $keep = $ID; 31 $ID = $id; 32 33 if($rev){ 34 if(@file_exists($file)){ 35 $ret = p_render('xhtml',p_get_instructions(io_readfile($file)),$info); //no caching on old revisions 36 }elseif($excuse){ 37 $ret = p_locale_xhtml('norev'); 38 } 39 }else{ 40 if(@file_exists($file)){ 41 $ret = p_cached_xhtml($file); 42 }elseif($excuse){ 43 $ret = p_locale_xhtml('newpage'); 44 } 45 } 46 47 //restore ID (just in case) 48 $ID = $keep; 49 50 return $ret; 51} 52 53/** 54 * Returns starting summary for a page (e.g. the first few 55 * paragraphs), marked up in XHTML. 56 * 57 * If $excuse is true an explanation is returned if the file 58 * wasn't found 59 * 60 * @param string wiki page id 61 * @param reference populated with page title from heading or page id 62 * @deprecated 63 * @author Harry Fuecks <hfuecks@gmail.com> 64 */ 65function p_wiki_xhtml_summary($id, &$title, $rev='', $excuse=true){ 66 $file = wikiFN($id,$rev); 67 $ret = ''; 68 69 //ensure $id is in global $ID (needed for parsing) 70 global $ID; 71 $keep = $ID; 72 $ID = $id; 73 74 if($rev){ 75 if(@file_exists($file)){ 76 //no caching on old revisions 77 $ins = p_get_instructions(io_readfile($file)); 78 }elseif($excuse){ 79 $ret = p_locale_xhtml('norev'); 80 //restore ID (just in case) 81 $ID = $keep; 82 return $ret; 83 } 84 85 }else{ 86 87 if(@file_exists($file)){ 88 // The XHTML for a summary is not cached so use the instruction cache 89 $ins = p_cached_instructions($file); 90 }elseif($excuse){ 91 $ret = p_locale_xhtml('newpage'); 92 //restore ID (just in case) 93 $ID = $keep; 94 return $ret; 95 } 96 } 97 98 $ret = p_render('xhtmlsummary',$ins,$info); 99 100 if ( $info['sum_pagetitle'] ) { 101 $title = $info['sum_pagetitle']; 102 } else { 103 $title = $id; 104 } 105 106 $ID = $keep; 107 return $ret; 108} 109 110/** 111 * Returns the specified local text in parsed format 112 * 113 * @author Andreas Gohr <andi@splitbrain.org> 114 */ 115function p_locale_xhtml($id){ 116 //fetch parsed locale 117 $html = p_cached_xhtml(localeFN($id)); 118 return $html; 119} 120 121/** 122 * Returns the given file parsed to XHTML 123 * 124 * Uses and creates a cachefile 125 * 126 * @author Andreas Gohr <andi@splitbrain.org> 127 * @todo rewrite to use mode instead of hardcoded XHTML 128 */ 129function p_cached_xhtml($file){ 130 global $conf; 131 $cache = getCacheName($file.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'],'.xhtml'); 132 $purge = $conf['cachedir'].'/purgefile'; 133 134 // check if cache can be used 135 $cachetime = @filemtime($cache); // 0 if not exists 136 137 if( @file_exists($file) // does the source exist 138 && $cachetime > @filemtime($file) // cache is fresh 139 && ((time() - $cachetime) < $conf['cachetime']) // and is cachefile young enough 140 && !isset($_REQUEST['purge']) // no purge param was set 141 && ($cachetime > @filemtime($purge)) // and newer than the purgefile 142 && ($cachetime > @filemtime(DOKU_CONF.'dokuwiki.php')) // newer than the config file 143 && ($cachetime > @filemtime(DOKU_CONF.'local.php')) // newer than the local config file 144 && ($cachetime > @filemtime(DOKU_INC.'inc/parser/xhtml.php')) // newer than the renderer 145 && ($cachetime > @filemtime(DOKU_INC.'inc/parser/parser.php')) // newer than the parser 146 && ($cachetime > @filemtime(DOKU_INC.'inc/parser/handler.php')))// newer than the handler 147 { 148 //well then use the cache 149 $parsed = io_readfile($cache); 150 if($conf['allowdebug']) $parsed .= "\n<!-- cachefile $cache used -->\n"; 151 }else{ 152 $parsed = p_render('xhtml', p_cached_instructions($file),$info); //try to use cached instructions 153 154 if($info['cache']){ 155 io_saveFile($cache,$parsed); //save cachefile 156 if($conf['allowdebug']) $parsed .= "\n<!-- no cachefile used, but created -->\n"; 157 }else{ 158 @unlink($cache); //try to delete cachefile 159 if($conf['allowdebug']) $parsed .= "\n<!-- no cachefile used, caching forbidden -->\n"; 160 } 161 } 162 163 return $parsed; 164} 165 166/** 167 * Returns the render instructions for a file 168 * 169 * Uses and creates a serialized cache file 170 * 171 * @author Andreas Gohr <andi@splitbrain.org> 172 */ 173function p_cached_instructions($file,$cacheonly=false){ 174 global $conf; 175 $cache = getCacheName($file.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'],'.i'); 176 177 // check if cache can be used 178 $cachetime = @filemtime($cache); // 0 if not exists 179 180 // cache forced? 181 if($cacheonly){ 182 if($cachetime){ 183 return unserialize(io_readfile($cache,false)); 184 }else{ 185 return array(); 186 } 187 } 188 189 if( @file_exists($file) // does the source exist 190 && $cachetime > @filemtime($file) // cache is fresh 191 && !isset($_REQUEST['purge']) // no purge param was set 192 && ($cachetime > @filemtime(DOKU_CONF.'dokuwiki.php')) // newer than the config file 193 && ($cachetime > @filemtime(DOKU_CONF.'local.php')) // newer than the local config file 194 && ($cachetime > @filemtime(DOKU_INC.'inc/parser/parser.php')) // newer than the parser 195 && ($cachetime > @filemtime(DOKU_INC.'inc/parser/handler.php')))// newer than the handler 196 { 197 //well then use the cache 198 return unserialize(io_readfile($cache,false)); 199 }elseif(@file_exists($file)){ 200 // no cache - do some work 201 $ins = p_get_instructions(io_readfile($file)); 202 io_savefile($cache,serialize($ins)); 203 return $ins; 204 } 205 206 return NULL; 207} 208 209/** 210 * turns a page into a list of instructions 211 * 212 * @author Harry Fuecks <hfuecks@gmail.com> 213 * @author Andreas Gohr <andi@splitbrain.org> 214 */ 215function p_get_instructions($text){ 216 217 $modes = p_get_parsermodes(); 218 219 // Create the parser 220 $Parser = & new Doku_Parser(); 221 222 // Add the Handler 223 $Parser->Handler = & new Doku_Handler(); 224 225 //add modes to parser 226 foreach($modes as $mode){ 227 $Parser->addMode($mode['mode'],$mode['obj']); 228 } 229 230 // Do the parsing 231 trigger_event('PARSER_WIKITEXT_PREPROCESS', $text); 232 $p = $Parser->parse($text); 233// dbg($p); 234 return $p; 235} 236 237/** 238 * returns the metadata of a page 239 * 240 * @author Esther Brunner <esther@kaffeehaus.ch> 241 */ 242function p_get_metadata($id, $key=false, $render=false){ 243 $file = metaFN($id, '.meta'); 244 245 if (@file_exists($file)) $meta = unserialize(io_readFile($file, false)); 246 else $meta = array(); 247 248 // metadata has never been rendered before - do it! 249 if ($render && !$meta['description']['abstract']){ 250 $meta = p_render_metadata($id, $meta); 251 io_saveFile($file, serialize($meta)); 252 } 253 254 // filter by $key 255 if ($key){ 256 list($key, $subkey) = explode(' ', $key, 2); 257 if (trim($subkey)) return $meta[$key][$subkey]; 258 else return $meta[$key]; 259 } 260 261 return $meta; 262} 263 264/** 265 * sets metadata elements of a page 266 * 267 * @author Esther Brunner <esther@kaffeehaus.ch> 268 */ 269function p_set_metadata($id, $data, $render=false){ 270 if (!is_array($data)) return false; 271 272 $orig = p_get_metadata($id); 273 274 // render metadata first? 275 if ($render) $meta = p_render_metadata($id, $orig); 276 else $meta = $orig; 277 278 // now add the passed metadata 279 $protected = array('description', 'date', 'contributor'); 280 foreach ($data as $key => $value){ 281 282 // be careful with sub-arrays of $meta['relation'] 283 if ($key == 'relation'){ 284 foreach ($value as $subkey => $subvalue){ 285 $meta[$key][$subkey] = array_merge($meta[$key][$subkey], $subvalue); 286 } 287 288 // be careful with some senisitive arrays of $meta 289 } elseif (in_array($key, $protected)){ 290 if (is_array($value)){ 291 #FIXME not sure if this is the intended thing: 292 if(!is_array($meta[$key])) $meta[$key] = array($meta[$key]); 293 $meta[$key] = array_merge($meta[$key], $value); 294 } 295 296 // no special treatment for the rest 297 } else { 298 $meta[$key] = $value; 299 } 300 } 301 302 // save only if metadata changed 303 if ($meta == $orig) return true; 304 return io_saveFile(metaFN($id, '.meta'), serialize($meta)); 305} 306 307/** 308 * renders the metadata of a page 309 * 310 * @author Esther Brunner <esther@kaffeehaus.ch> 311 */ 312function p_render_metadata($id, $orig){ 313 require_once DOKU_INC."inc/parser/metadata.php"; 314 315 // get instructions 316 $instructions = p_cached_instructions(wikiFN($id)); 317 318 // set up the renderer 319 $renderer = & new Doku_Renderer_metadata(); 320 $renderer->meta = $orig; 321 322 // loop through the instructions 323 foreach ($instructions as $instruction){ 324 // execute the callback against the renderer 325 call_user_func_array(array(&$renderer, $instruction[0]), $instruction[1]); 326 } 327 328 return $renderer->meta; 329} 330 331/** 332 * returns all available parser syntax modes in correct order 333 * 334 * @author Andreas Gohr <andi@splitbrain.org> 335 */ 336function p_get_parsermodes(){ 337 global $conf; 338 339 //reuse old data 340 static $modes = null; 341 if($modes != null){ 342 return $modes; 343 } 344 345 //import parser classes and mode definitions 346 require_once DOKU_INC . 'inc/parser/parser.php'; 347 348 // we now collect all syntax modes and their objects, then they will 349 // be sorted and added to the parser in correct order 350 $modes = array(); 351 352 // add syntax plugins 353 $pluginlist = plugin_list('syntax'); 354 if(count($pluginlist)){ 355 global $PARSER_MODES; 356 $obj = null; 357 foreach($pluginlist as $p){ 358 if(!$obj =& plugin_load('syntax',$p)) continue; //attempt to load plugin into $obj 359 $PARSER_MODES[$obj->getType()][] = "plugin_$p"; //register mode type 360 //add to modes 361 $modes[] = array( 362 'sort' => $obj->getSort(), 363 'mode' => "plugin_$p", 364 'obj' => $obj, 365 ); 366 unset($obj); //remove the reference 367 } 368 } 369 370 // add default modes 371 $std_modes = array('listblock','preformatted','notoc','nocache', 372 'header','table','linebreak','footnote','hr', 373 'unformatted','php','html','code','file','quote', 374 'internallink','rss','media','externallink', 375 'emaillink','windowssharelink','eol'); 376 if($conf['typography']){ 377 $std_modes[] = 'quotes'; 378 $std_modes[] = 'multiplyentity'; 379 } 380 foreach($std_modes as $m){ 381 $class = "Doku_Parser_Mode_$m"; 382 $obj = new $class(); 383 $modes[] = array( 384 'sort' => $obj->getSort(), 385 'mode' => $m, 386 'obj' => $obj 387 ); 388 } 389 390 // add formatting modes 391 $fmt_modes = array('strong','emphasis','underline','monospace', 392 'subscript','superscript','deleted'); 393 foreach($fmt_modes as $m){ 394 $obj = new Doku_Parser_Mode_formatting($m); 395 $modes[] = array( 396 'sort' => $obj->getSort(), 397 'mode' => $m, 398 'obj' => $obj 399 ); 400 } 401 402 // add modes which need files 403 $obj = new Doku_Parser_Mode_smiley(array_keys(getSmileys())); 404 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'smiley','obj' => $obj ); 405 $obj = new Doku_Parser_Mode_acronym(array_keys(getAcronyms())); 406 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'acronym','obj' => $obj ); 407 $obj = new Doku_Parser_Mode_entity(array_keys(getEntities())); 408 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'entity','obj' => $obj ); 409 410 411 // add optional camelcase mode 412 if($conf['camelcase']){ 413 $obj = new Doku_Parser_Mode_camelcaselink(); 414 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'camelcaselink','obj' => $obj ); 415 } 416 417 //sort modes 418 usort($modes,'p_sort_modes'); 419 420 return $modes; 421} 422 423/** 424 * Callback function for usort 425 * 426 * @author Andreas Gohr <andi@splitbrain.org> 427 */ 428function p_sort_modes($a, $b){ 429 if($a['sort'] == $b['sort']) return 0; 430 return ($a['sort'] < $b['sort']) ? -1 : 1; 431} 432 433/** 434 * Renders a list of instruction to the specified output mode 435 * 436 * In the $info array are informations from the renderer returned 437 * 438 * @author Harry Fuecks <hfuecks@gmail.com> 439 * @author Andreas Gohr <andi@splitbrain.org> 440 */ 441function p_render($mode,$instructions,& $info){ 442 if(is_null($instructions)) return ''; 443 444 if ($mode=='wiki') { msg("Renderer for $mode not valid",-1); return null; } //FIXME!! remove this line when inc/parser/wiki.php works. 445 446 // Create the renderer 447 if(!@file_exists(DOKU_INC."inc/parser/$mode.php")){ 448 msg("No renderer for $mode found",-1); 449 return null; 450 } 451 452 require_once DOKU_INC."inc/parser/$mode.php"; 453 $rclass = "Doku_Renderer_$mode"; 454 if ( !class_exists($rclass) ) { 455 trigger_error("Unable to resolve render class $rclass",E_USER_WARNING); 456 msg("Renderer for $mode not valid",-1); 457 return null; 458 } 459 $Renderer = & new $rclass(); #FIXME any way to check for class existance? 460 461 $Renderer->smileys = getSmileys(); 462 $Renderer->entities = getEntities(); 463 $Renderer->acronyms = getAcronyms(); 464 $Renderer->interwiki = getInterwiki(); 465 #$Renderer->badwords = getBadWords(); 466 467 // Loop through the instructions 468 foreach ( $instructions as $instruction ) { 469 // Execute the callback against the Renderer 470 call_user_func_array(array(&$Renderer, $instruction[0]),$instruction[1]); 471 } 472 473 //set info array 474 $info = $Renderer->info; 475 476 // Post process and return the output 477 $data = array($mode,& $Renderer->doc); 478 trigger_event('RENDERER_CONTENT_POSTPROCESS',$data); 479 return $Renderer->doc; 480} 481 482/** 483 * Gets the first heading from a file 484 * 485 * @author Andreas Gohr <andi@splitbrain.org> 486 */ 487function p_get_first_heading($id){ 488 global $conf; 489 if(!$conf['useheading']) return null; 490 491 $meta = p_get_metadata($id); 492 if($meta['title']) return $meta['title']; 493 return null; 494} 495 496/** 497 * Wrapper for GeSHi Code Highlighter, provides caching of its output 498 * 499 * @author Christopher Smith <chris@jalakai.co.uk> 500 */ 501function p_xhtml_cached_geshi($code, $language) { 502 $cache = getCacheName($language.$code,".code"); 503 504 if (@file_exists($cache)) { 505 506 $highlighted_code = io_readFile($cache, false); 507 @touch($cache); 508 509 } else { 510 511 require_once(DOKU_INC . 'inc/geshi.php'); 512 513 $geshi = new GeSHi($code, strtolower($language), DOKU_INC . 'inc/geshi'); 514 $geshi->set_encoding('utf-8'); 515 $geshi->enable_classes(); 516 $geshi->set_header_type(GESHI_HEADER_PRE); 517 $geshi->set_overall_class("code $language"); 518 $geshi->set_link_target($conf['target']['extern']); 519 520 $highlighted_code = $geshi->parse_code(); 521 522 io_saveFile($cache,$highlighted_code); 523 } 524 525 return $highlighted_code; 526} 527 528//Setup VIM: ex: et ts=2 enc=utf-8 : 529