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