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