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 #FIXME not sure if this is the intended thing: 290 if(!is_array($meta[$key])) $meta[$key] = array($meta[$key]); 291 $meta[$key] = array_merge($meta[$key], $value); 292 } 293 294 // no special treatment for the rest 295 } else { 296 $meta[$key] = $value; 297 } 298 } 299 300 // save only if metadata changed 301 if ($meta == $orig) return true; 302 return io_saveFile(metaFN($id, '.meta'), serialize($meta)); 303} 304 305/** 306 * renders the metadata of a page 307 * 308 * @author Esther Brunner <esther@kaffeehaus.ch> 309 */ 310function p_render_metadata($id, $orig){ 311 require_once DOKU_INC."inc/parser/metadata.php"; 312 313 // get instructions 314 $instructions = p_cached_instructions(wikiFN($id)); 315 316 // set up the renderer 317 $renderer = & new Doku_Renderer_metadata(); 318 $renderer->meta = $orig; 319 320 // loop through the instructions 321 foreach ($instructions as $instruction){ 322 // execute the callback against the renderer 323 call_user_func_array(array(&$renderer, $instruction[0]), $instruction[1]); 324 } 325 326 return $renderer->meta; 327} 328 329/** 330 * returns all available parser syntax modes in correct order 331 * 332 * @author Andreas Gohr <andi@splitbrain.org> 333 */ 334function p_get_parsermodes(){ 335 global $conf; 336 337 //reuse old data 338 static $modes = null; 339 if($modes != null){ 340 return $modes; 341 } 342 343 //import parser classes and mode definitions 344 require_once DOKU_INC . 'inc/parser/parser.php'; 345 346 // we now collect all syntax modes and their objects, then they will 347 // be sorted and added to the parser in correct order 348 $modes = array(); 349 350 // add syntax plugins 351 $pluginlist = plugin_list('syntax'); 352 if(count($pluginlist)){ 353 global $PARSER_MODES; 354 $obj = null; 355 foreach($pluginlist as $p){ 356 if(!$obj =& plugin_load('syntax',$p)) continue; //attempt to load plugin into $obj 357 $PARSER_MODES[$obj->getType()][] = "plugin_$p"; //register mode type 358 //add to modes 359 $modes[] = array( 360 'sort' => $obj->getSort(), 361 'mode' => "plugin_$p", 362 'obj' => $obj, 363 ); 364 unset($obj); //remove the reference 365 } 366 } 367 368 // add default modes 369 $std_modes = array('listblock','preformatted','notoc','nocache', 370 'header','table','linebreak','footnote','hr', 371 'unformatted','php','html','code','file','quote', 372 'internallink','rss','media','externallink', 373 'emaillink','windowssharelink','eol'); 374 if($conf['typography']){ 375 $std_modes[] = 'quotes'; 376 $std_modes[] = 'multiplyentity'; 377 } 378 foreach($std_modes as $m){ 379 $class = "Doku_Parser_Mode_$m"; 380 $obj = new $class(); 381 $modes[] = array( 382 'sort' => $obj->getSort(), 383 'mode' => $m, 384 'obj' => $obj 385 ); 386 } 387 388 // add formatting modes 389 $fmt_modes = array('strong','emphasis','underline','monospace', 390 'subscript','superscript','deleted'); 391 foreach($fmt_modes as $m){ 392 $obj = new Doku_Parser_Mode_formatting($m); 393 $modes[] = array( 394 'sort' => $obj->getSort(), 395 'mode' => $m, 396 'obj' => $obj 397 ); 398 } 399 400 // add modes which need files 401 $obj = new Doku_Parser_Mode_smiley(array_keys(getSmileys())); 402 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'smiley','obj' => $obj ); 403 $obj = new Doku_Parser_Mode_acronym(array_keys(getAcronyms())); 404 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'acronym','obj' => $obj ); 405 $obj = new Doku_Parser_Mode_entity(array_keys(getEntities())); 406 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'entity','obj' => $obj ); 407 408 409 // add optional camelcase mode 410 if($conf['camelcase']){ 411 $obj = new Doku_Parser_Mode_camelcaselink(); 412 $modes[] = array('sort' => $obj->getSort(), 'mode' => 'camelcaselink','obj' => $obj ); 413 } 414 415 //sort modes 416 usort($modes,'p_sort_modes'); 417 418 return $modes; 419} 420 421/** 422 * Callback function for usort 423 * 424 * @author Andreas Gohr <andi@splitbrain.org> 425 */ 426function p_sort_modes($a, $b){ 427 if($a['sort'] == $b['sort']) return 0; 428 return ($a['sort'] < $b['sort']) ? -1 : 1; 429} 430 431/** 432 * Renders a list of instruction to the specified output mode 433 * 434 * In the $info array are informations from the renderer returned 435 * 436 * @author Harry Fuecks <hfuecks@gmail.com> 437 * @author Andreas Gohr <andi@splitbrain.org> 438 */ 439function p_render($mode,$instructions,& $info){ 440 if(is_null($instructions)) return ''; 441 442 if ($mode=='wiki') { msg("Renderer for $mode not valid",-1); return null; } //FIXME!! remove this line when inc/parser/wiki.php works. 443 444 // Create the renderer 445 if(!@file_exists(DOKU_INC."inc/parser/$mode.php")){ 446 msg("No renderer for $mode found",-1); 447 return null; 448 } 449 450 require_once DOKU_INC."inc/parser/$mode.php"; 451 $rclass = "Doku_Renderer_$mode"; 452 if ( !class_exists($rclass) ) { 453 trigger_error("Unable to resolve render class $rclass",E_USER_WARNING); 454 msg("Renderer for $mode not valid",-1); 455 return null; 456 } 457 $Renderer = & new $rclass(); #FIXME any way to check for class existance? 458 459 $Renderer->smileys = getSmileys(); 460 $Renderer->entities = getEntities(); 461 $Renderer->acronyms = getAcronyms(); 462 $Renderer->interwiki = getInterwiki(); 463 #$Renderer->badwords = getBadWords(); 464 465 // Loop through the instructions 466 foreach ( $instructions as $instruction ) { 467 // Execute the callback against the Renderer 468 call_user_func_array(array(&$Renderer, $instruction[0]),$instruction[1]); 469 } 470 471 //set info array 472 $info = $Renderer->info; 473 474 // Return the output 475 return $Renderer->doc; 476} 477 478/** 479 * Gets the first heading from a file 480 * 481 * @author Andreas Gohr <andi@splitbrain.org> 482 */ 483function p_get_first_heading($id){ 484 $meta = p_get_metadata($id); 485 if($meta['title']) return $meta['title']; 486 return NULL; 487} 488 489//Setup VIM: ex: et ts=2 enc=utf-8 : 490