1<?php 2/** 3 * Common DokuWiki functions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); 10 require_once(DOKU_INC.'conf/dokuwiki.php'); 11 require_once(DOKU_INC.'inc/io.php'); 12 require_once(DOKU_INC.'inc/utf8.php'); 13 require_once(DOKU_INC.'inc/mail.php'); 14 15/** 16 * Return info about the current document as associative 17 * array. 18 * 19 * @author Andreas Gohr <andi@splitbrain.org> 20 */ 21function pageinfo(){ 22 global $ID; 23 global $REV; 24 global $USERINFO; 25 global $conf; 26 27 if($_SERVER['REMOTE_USER']){ 28 $info['user'] = $_SERVER['REMOTE_USER']; 29 $info['userinfo'] = $USERINFO; 30 $info['perm'] = auth_quickaclcheck($ID); 31 }else{ 32 $info['user'] = ''; 33 $info['perm'] = auth_aclcheck($ID,'',null); 34 } 35 36 $info['namespace'] = getNS($ID); 37 $info['locked'] = checklock($ID); 38 $info['filepath'] = realpath(wikiFN($ID,$REV)); 39 $info['exists'] = @file_exists($info['filepath']); 40 if($REV && !$info['exists']){ 41 //check if current revision was meant 42 $cur = wikiFN($ID); 43 if(@file_exists($cur) && (@filemtime($cur) == $REV)){ 44 $info['filepath'] = realpath($cur); 45 $info['exists'] = true; 46 $REV = ''; 47 } 48 } 49 if($info['exists']){ 50 $info['writable'] = (is_writable($info['filepath']) && 51 ($info['perm'] >= AUTH_EDIT)); 52 }else{ 53 $info['writable'] = ($info['perm'] >= AUTH_CREATE); 54 } 55 $info['editable'] = ($info['writable'] && empty($info['lock'])); 56 $info['lastmod'] = @filemtime($info['filepath']); 57 58 //who's the editor 59 if($REV){ 60 $revinfo = getRevisionInfo($ID,$REV); 61 }else{ 62 $revinfo = getRevisionInfo($ID,$info['lastmod']); 63 } 64 $info['ip'] = $revinfo['ip']; 65 $info['user'] = $revinfo['user']; 66 $info['sum'] = $revinfo['sum']; 67 $info['editor'] = $revinfo['ip']; 68 if($revinfo['user']) $info['editor'].= ' ('.$revinfo['user'].')'; 69 70 return $info; 71} 72 73/** 74 * print a message 75 * 76 * If HTTP headers were not sent yet the message is added 77 * to the global message array else it's printed directly 78 * using html_msgarea() 79 * 80 * 81 * Levels can be: 82 * 83 * -1 error 84 * 0 info 85 * 1 success 86 * 87 * @author Andreas Gohr <andi@splitbrain.org> 88 * @see html_msgarea 89 */ 90function msg($message,$lvl=0){ 91 global $MSG; 92 $errors[-1] = 'error'; 93 $errors[0] = 'info'; 94 $errors[1] = 'success'; 95 96 if(!headers_sent()){ 97 if(!isset($MSG)) $MSG = array(); 98 $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message); 99 }else{ 100 $MSG = array(); 101 $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message); 102 html_msgarea(); 103 } 104} 105 106/** 107 * This builds the breadcrumb trail and returns it as array 108 * 109 * @author Andreas Gohr <andi@splitbrain.org> 110 */ 111function breadcrumbs(){ 112 global $ID; 113 global $ACT; 114 global $conf; 115 $crumbs = $_SESSION[$conf['title']]['bc']; 116 117 //first visit? 118 if (!is_array($crumbs)){ 119 $crumbs = array(); 120 } 121 //we only save on show and existing wiki documents 122 $file = wikiFN($ID); 123 if($ACT != 'show' || !@file_exists($file)){ 124 $_SESSION[$conf['title']]['bc'] = $crumbs; 125 return $crumbs; 126 } 127 128 // page names 129 $name = noNS($ID); 130 if ($conf['useheading']) { 131 // get page title 132 $title = getFirstHeading(io_readFile($file)); 133 if ($title) { 134 $name = $title; 135 } 136 } 137 138 //remove ID from array 139 if (isset($crumbs[$ID])) { 140 unset($crumbs[$ID]); 141 } 142 143 //add to array 144 $crumbs[$ID] = $name; 145 //reduce size 146 while(count($crumbs) > $conf['breadcrumbs']){ 147 array_shift($crumbs); 148 } 149 //save to session 150 $_SESSION[$conf['title']]['bc'] = $crumbs; 151 return $crumbs; 152} 153 154/** 155 * Filter for page IDs 156 * 157 * This is run on a ID before it is outputted somewhere 158 * currently used to replace the colon with something else 159 * on Windows systems and to have proper URL encoding 160 * 161 * Urlencoding is ommitted when the second parameter is false 162 * 163 * @author Andreas Gohr <andi@splitbrain.org> 164 */ 165function idfilter($id,$ue=true){ 166 global $conf; 167 if ($conf['useslash'] && $conf['userewrite']){ 168 $id = strtr($id,':','/'); 169 }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && 170 $conf['userewrite']) { 171 $id = strtr($id,':',';'); 172 } 173 if($ue){ 174 $id = urlencode($id); 175 $id = str_replace('%3A',':',$id); //keep as colon 176 $id = str_replace('%2F','/',$id); //keep as slash 177 } 178 return $id; 179} 180 181/** 182 * This builds a link to a wikipage 183 * 184 * @author Andreas Gohr <andi@splitbrain.org> 185 */ 186function wl($id='',$more='',$abs=false){ 187 global $conf; 188 $more = str_replace(',','&',$more); 189 190 $id = idfilter($id); 191 if($abs){ 192 $xlink = DOKU_URL; 193 }else{ 194 $xlink = DOKU_BASE; 195 } 196 197 if(!$conf['userewrite']){ 198 $xlink .= DOKU_SCRIPT.'?id='.$id; 199 if($more) $xlink .= '&'.$more; 200 }else{ 201 $xlink .= $id; 202 if($more) $xlink .= '?'.$more; 203 } 204 205 return $xlink; 206} 207 208/** 209 * Just builds a link to a script 210 * 211 * @todo maybe obsolete 212 * @author Andreas Gohr <andi@splitbrain.org> 213 */ 214function script($script='doku.php'){ 215# $link = getBaseURL(); 216# $link .= $script; 217# return $link; 218 return DOKU_BASE.DOKU_SCRIPT; 219} 220 221/** 222 * Return namespacepart of a wiki ID 223 * 224 * @author Andreas Gohr <andi@splitbrain.org> 225 */ 226function getNS($id){ 227 if(strpos($id,':')!==false){ 228 return substr($id,0,strrpos($id,':')); 229 } 230 return false; 231} 232 233/** 234 * Returns the ID without the namespace 235 * 236 * @author Andreas Gohr <andi@splitbrain.org> 237 */ 238function noNS($id){ 239 return preg_replace('/.*:/','',$id); 240} 241 242/** 243 * Spamcheck against wordlist 244 * 245 * Checks the wikitext against a list of blocked expressions 246 * returns true if the text contains any bad words 247 * 248 * @author Andreas Gohr <andi@splitbrain.org> 249 */ 250function checkwordblock(){ 251 global $TEXT; 252 global $conf; 253 254 if(!$conf['usewordblock']) return false; 255 256 $blockfile = file('conf/wordblock.conf'); 257 //how many lines to read at once (to work around some PCRE limits) 258 if(version_compare(phpversion(),'4.3.0','<')){ 259 //old versions of PCRE define a maximum of parenthesises even if no 260 //backreferences are used - the maximum is 99 261 //this is very bad performancewise and may even be too high still 262 $chunksize = 40; 263 }else{ 264 //read file in chunks of 600 - this should work around the 265 //MAX_PATTERN_SIZE in modern PCRE 266 $chunksize = 600; 267 } 268 while($blocks = array_splice($blockfile,0,$chunksize)){ 269 $re = array(); 270 #build regexp from blocks 271 foreach($blocks as $block){ 272 $block = preg_replace('/#.*$/','',$block); 273 $block = trim($block); 274 if(empty($block)) continue; 275 $re[] = $block; 276 } 277 if(preg_match('#('.join('|',$re).')#si',$TEXT)) return true; 278 } 279 return false; 280} 281 282/** 283 * Return the IP of the client 284 * 285 * Honours X-Forwarded-For Proxy Headers 286 * 287 * @author Andreas Gohr <andi@splitbrain.org> 288 */ 289function clientIP(){ 290 $my = $_SERVER['REMOTE_ADDR']; 291 if($_SERVER['HTTP_X_FORWARDED_FOR']){ 292 $my .= ' ('.$_SERVER['HTTP_X_FORWARDED_FOR'].')'; 293 } 294 return $my; 295} 296 297/** 298 * Checks if a given page is currently locked. 299 * 300 * removes stale lockfiles 301 * 302 * @author Andreas Gohr <andi@splitbrain.org> 303 */ 304function checklock($id){ 305 global $conf; 306 $lock = wikiFN($id).'.lock'; 307 308 //no lockfile 309 if(!@file_exists($lock)) return false; 310 311 //lockfile expired 312 if((time() - filemtime($lock)) > $conf['locktime']){ 313 unlink($lock); 314 return false; 315 } 316 317 //my own lock 318 $ip = io_readFile($lock); 319 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 320 return false; 321 } 322 323 return $ip; 324} 325 326/** 327 * Lock a page for editing 328 * 329 * @author Andreas Gohr <andi@splitbrain.org> 330 */ 331function lock($id){ 332 $lock = wikiFN($id).'.lock'; 333 if($_SERVER['REMOTE_USER']){ 334 io_saveFile($lock,$_SERVER['REMOTE_USER']); 335 }else{ 336 io_saveFile($lock,clientIP()); 337 } 338} 339 340/** 341 * Unlock a page if it was locked by the user 342 * 343 * @author Andreas Gohr <andi@splitbrain.org> 344 * @return bool true if a lock was removed 345 */ 346function unlock($id){ 347 $lock = wikiFN($id).'.lock'; 348 if(@file_exists($lock)){ 349 $ip = io_readFile($lock); 350 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 351 @unlink($lock); 352 return true; 353 } 354 } 355 return false; 356} 357 358/** 359 * Remove unwanted chars from ID 360 * 361 * Cleans a given ID to only use allowed characters. Accented characters are 362 * converted to unaccented ones 363 * 364 * @author Andreas Gohr <andi@splitbrain.org> 365 */ 366function cleanID($id){ 367 global $conf; 368 global $lang; 369 $id = trim($id); 370 $id = utf8_strtolower($id); 371 372 //alternative namespace seperator 373 $id = strtr($id,';',':'); 374 if($conf['useslash']){ 375 $id = strtr($id,'/',':'); 376 }else{ 377 $id = strtr($id,'/','_'); 378 } 379 380 if($conf['deaccent']) $id = utf8_deaccent($id,-1); 381 382 //remove specials 383 $id = utf8_stripspecials($id,'_','_:.-'); 384 385 //clean up 386 $id = preg_replace('#__#','_',$id); 387 $id = preg_replace('#:+#',':',$id); 388 $id = trim($id,':._-'); 389 $id = preg_replace('#:[:\._\-]+#',':',$id); 390 391 return($id); 392} 393 394/** 395 * returns the full path to the datafile specified by ID and 396 * optional revision 397 * 398 * The filename is URL encoded to protect Unicode chars 399 * 400 * @author Andreas Gohr <andi@splitbrain.org> 401 */ 402function wikiFN($id,$rev=''){ 403 global $conf; 404 $id = cleanID($id); 405 $id = str_replace(':','/',$id); 406 if(empty($rev)){ 407 $fn = $conf['datadir'].'/'.utf8_encodeFN($id).'.txt'; 408 }else{ 409 $fn = $conf['olddir'].'/'.utf8_encodeFN($id).'.'.$rev.'.txt'; 410 if($conf['usegzip'] && !@file_exists($fn)){ 411 //return gzip if enabled and plaintext doesn't exist 412 $fn .= '.gz'; 413 } 414 } 415 return $fn; 416} 417 418/** 419 * Returns the full filepath to a localized textfile if local 420 * version isn't found the english one is returned 421 * 422 * @author Andreas Gohr <andi@splitbrain.org> 423 */ 424function localeFN($id){ 425 global $conf; 426 $file = './lang/'.$conf['lang'].'/'.$id.'.txt'; 427 if(!@file_exists($file)){ 428 //fall back to english 429 $file = './lang/en/'.$id.'.txt'; 430 } 431 return cleanText($file); 432} 433 434/** 435 * convert line ending to unix format 436 * 437 * @see formText() for 2crlf conversion 438 * @author Andreas Gohr <andi@splitbrain.org> 439 */ 440function cleanText($text){ 441 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 442 return $text; 443} 444 445/** 446 * Prepares text for print in Webforms by encoding special chars. 447 * It also converts line endings to Windows format which is 448 * pseudo standard for webforms. 449 * 450 * @see cleanText() for 2unix conversion 451 * @author Andreas Gohr <andi@splitbrain.org> 452 */ 453function formText($text){ 454 $text = preg_replace("/\012/","\015\012",$text); 455 return htmlspecialchars($text); 456} 457 458/** 459 * Returns the specified local text in parsed format 460 * 461 * @author Andreas Gohr <andi@splitbrain.org> 462 */ 463function parsedLocale($id){ 464 //disable section editing 465 global $parser; 466 $se = $parser['secedit']; 467 $parser['secedit'] = false; 468 //fetch parsed locale 469 $html = io_cacheParse(localeFN($id)); 470 //reset section editing 471 $parser['secedit'] = $se; 472 return $html; 473} 474 475/** 476 * Returns the specified local text in raw format 477 * 478 * @author Andreas Gohr <andi@splitbrain.org> 479 */ 480function rawLocale($id){ 481 return io_readFile(localeFN($id)); 482} 483 484 485/** 486 * Returns the parsed Wikitext for the given id and revision. 487 * 488 * If $excuse is true an explanation is returned if the file 489 * wasn't found 490 * 491 * @author Andreas Gohr <andi@splitbrain.org> 492 */ 493function parsedWiki($id,$rev='',$excuse=true){ 494 $file = wikiFN($id,$rev); 495 $ret = ''; 496 497 //ensure $id is in global $ID (needed for parsing) 498 global $ID; 499 $ID = $id; 500 501 if($rev){ 502 if(@file_exists($file)){ 503 $ret = parse(io_readFile($file)); 504 }elseif($excuse){ 505 $ret = parsedLocale('norev'); 506 } 507 }else{ 508 if(@file_exists($file)){ 509 if(!defined('DOKU_USENEWPARSER')){ 510 $ret = io_cacheParse($file); 511 }else{ 512 msg('using new parser'); 513 require_once(DOKU_INC.'inc/parser/action.php'); 514 $ret = render_as_xhtml(parse_to_instructions(io_readFile($file))); 515 } 516 }elseif($excuse){ 517 $ret = parsedLocale('newpage'); 518 } 519 } 520 521 return $ret; 522} 523 524/** 525 * Returns the raw WikiText 526 * 527 * @author Andreas Gohr <andi@splitbrain.org> 528 */ 529function rawWiki($id,$rev=''){ 530 return io_readFile(wikiFN($id,$rev)); 531} 532 533/** 534 * Returns the raw Wiki Text in three slices. 535 * 536 * The range parameter needs to have the form "from-to" 537 * and gives the range of the section. 538 * The returned order is prefix, section and suffix. 539 * 540 * @author Andreas Gohr <andi@splitbrain.org> 541 */ 542function rawWikiSlices($range,$id,$rev=''){ 543 list($from,$to) = split('-',$range,2); 544 $text = io_readFile(wikiFN($id,$rev)); 545 $text = split("\n",$text); 546 if(!$from) $from = 0; 547 if(!$to) $to = count($text); 548 549 $slices[0] = join("\n",array_slice($text,0,$from)); 550 $slices[1] = join("\n",array_slice($text,$from,$to + 1 - $from)); 551 $slices[2] = join("\n",array_slice($text,$to+1)); 552 553 return $slices; 554} 555 556/** 557 * Joins wiki text slices 558 * 559 * function to join the text slices with correct lineendings again. 560 * When the pretty parameter is set to true it adds additional empty 561 * lines between sections if needed (used on saving). 562 * 563 * @author Andreas Gohr <andi@splitbrain.org> 564 */ 565function con($pre,$text,$suf,$pretty=false){ 566 567 if($pretty){ 568 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 569 if($suf && substr($text,-1) != "\n") $text .= "\n"; 570 } 571 572 if($pre) $pre .= "\n"; 573 if($suf) $text .= "\n"; 574 return $pre.$text.$suf; 575} 576 577/** 578 * print debug messages 579 * 580 * little function to print the content of a var 581 * 582 * @author Andreas Gohr <andi@splitbrain.org> 583 */ 584function dbg($msg,$hidden=false){ 585 (!$hidden) ? print '<pre class="dbg">' : print "<!--\n"; 586 print_r($msg); 587 (!$hidden) ? print '</pre>' : print "\n-->"; 588} 589 590/** 591 * Add's an entry to the changelog 592 * 593 * @author Andreas Gohr <andi@splitbrain.org> 594 */ 595function addLogEntry($date,$id,$summary=""){ 596 global $conf; 597 $id = cleanID($id);//FIXME not needed anymore? 598 599 if(!@is_writable($conf['changelog'])){ 600 msg($conf['changelog'].' is not writable!',-1); 601 return; 602 } 603 604 if(!$date) $date = time(); //use current time if none supplied 605 $remote = $_SERVER['REMOTE_ADDR']; 606 $user = $_SERVER['REMOTE_USER']; 607 608 $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n"; 609 610 //FIXME: use adjusted io_saveFile instead 611 $fh = fopen($conf['changelog'],'a'); 612 if($fh){ 613 fwrite($fh,$logline); 614 fclose($fh); 615 } 616} 617 618/** 619 * returns an array of recently changed files using the 620 * changelog 621 * 622 * @author Andreas Gohr <andi@splitbrain.org> 623 */ 624function getRecents($num=0,$incdel=false){ 625 global $conf; 626 $recent = array(); 627 if(!$num) $num = $conf['recent']; 628 629 if(!@is_readable($conf['changelog'])){ 630 msg($conf['changelog'].' is not readable',-1); 631 return $recent; 632 } 633 634 $loglines = file($conf['changelog']); 635 rsort($loglines); //reverse sort on timestamp 636 637 foreach ($loglines as $line){ 638 $line = rtrim($line); //remove newline 639 if(empty($line)) continue; //skip empty lines 640 $info = split("\t",$line); //split into parts 641 //add id if not in yet and file still exists and is allowed to read 642 if(!$recent[$info[2]] && 643 (@file_exists(wikiFN($info[2])) || $incdel) && 644 (auth_quickaclcheck($info[2]) >= AUTH_READ) 645 ){ 646 $recent[$info[2]]['date'] = $info[0]; 647 $recent[$info[2]]['ip'] = $info[1]; 648 $recent[$info[2]]['user'] = $info[3]; 649 $recent[$info[2]]['sum'] = $info[4]; 650 $recent[$info[2]]['del'] = !@file_exists(wikiFN($info[2])); 651 } 652 if(count($recent) >= $num){ 653 break; //finish if enough items found 654 } 655 } 656 return $recent; 657} 658 659/** 660 * gets additonal informations for a certain pagerevison 661 * from the changelog 662 * 663 * @author Andreas Gohr <andi@splitbrain.org> 664 */ 665function getRevisionInfo($id,$rev){ 666 global $conf; 667 668 if(!$rev) return(null); 669 670 $info = array(); 671 if(!@is_readable($conf['changelog'])){ 672 msg($conf['changelog'].' is not readable',-1); 673 return $recent; 674 } 675 $loglines = file($conf['changelog']); 676 $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines); 677 rsort($loglines); //reverse sort on timestamp (shouldn't be needed) 678 $line = split("\t",$loglines[0]); 679 $info['date'] = $line[0]; 680 $info['ip'] = $line[1]; 681 $info['user'] = $line[3]; 682 $info['sum'] = $line[4]; 683 return $info; 684} 685 686/** 687 * Saves a wikitext by calling io_saveFile 688 * 689 * @author Andreas Gohr <andi@splitbrain.org> 690 */ 691function saveWikiText($id,$text,$summary){ 692 global $conf; 693 global $lang; 694 umask($conf['umask']); 695 // ignore if no changes were made 696 if($text == rawWiki($id,'')){ 697 return; 698 } 699 700 $file = wikiFN($id); 701 $old = saveOldRevision($id); 702 703 if (empty($text)){ 704 // remove empty files 705 @unlink($file); 706 $del = true; 707 //autoset summary on deletion 708 if(empty($summary)) $summary = $lang['deleted']; 709 //remove empty namespaces 710 io_sweepNS($id); 711 }else{ 712 // save file (datadir is created in io_saveFile) 713 io_saveFile($file,$text); 714 $del = false; 715 } 716 717 addLogEntry(@filemtime($file),$id,$summary); 718 notify($id,$old,$summary); 719 720 //purge cache on add by updating the purgefile 721 if($conf['purgeonadd'] && (!$old || $del)){ 722 io_saveFile($conf['datadir'].'/_cache/purgefile',time()); 723 } 724} 725 726/** 727 * moves the current version to the attic and returns its 728 * revision date 729 * 730 * @author Andreas Gohr <andi@splitbrain.org> 731 */ 732function saveOldRevision($id){ 733 global $conf; 734 umask($conf['umask']); 735 $oldf = wikiFN($id); 736 if(!@file_exists($oldf)) return ''; 737 $date = filemtime($oldf); 738 $newf = wikiFN($id,$date); 739 if(substr($newf,-3)=='.gz'){ 740 io_saveFile($newf,rawWiki($id)); 741 }else{ 742 io_makeFileDir($newf); 743 copy($oldf, $newf); 744 } 745 return $date; 746} 747 748/** 749 * Sends a notify mail to the wikiadmin when a page was 750 * changed 751 * 752 * @author Andreas Gohr <andi@splitbrain.org> 753 */ 754function notify($id,$rev="",$summary=""){ 755 global $lang; 756 global $conf; 757 $hdrs =''; 758 if(empty($conf['notify'])) return; //notify enabled? 759 760 $text = rawLocale('mailtext'); 761 $text = str_replace('@DATE@',date($conf['dformat']),$text); 762 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 763 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 764 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 765 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 766 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 767 $text = str_replace('@SUMMARY@',$summary,$text); 768 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 769 770 if($rev){ 771 $subject = $lang['mail_changed'].' '.$id; 772 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 773 require_once("inc/DifferenceEngine.php"); 774 $df = new Diff(split("\n",rawWiki($id,$rev)), 775 split("\n",rawWiki($id))); 776 $dformat = new UnifiedDiffFormatter(); 777 $diff = $dformat->format($df); 778 }else{ 779 $subject=$lang['mail_newpage'].' '.$id; 780 $text = str_replace('@OLDPAGE@','none',$text); 781 $diff = rawWiki($id); 782 } 783 $text = str_replace('@DIFF@',$diff,$text); 784 785 mail_send($conf['notify'],$subject,$text,$conf['mailfrom']); 786} 787 788/** 789 * Return a list of available page revisons 790 * 791 * @author Andreas Gohr <andi@splitbrain.org> 792 */ 793function getRevisions($id){ 794 $revd = dirname(wikiFN($id,'foo')); 795 $revs = array(); 796 $clid = cleanID($id); 797 if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path 798 799 if (is_dir($revd) && $dh = opendir($revd)) { 800 while (($file = readdir($dh)) !== false) { 801 if (is_dir($revd.'/'.$file)) continue; 802 if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){ 803 $revs[]=$match[1]; 804 } 805 } 806 closedir($dh); 807 } 808 rsort($revs); 809 return $revs; 810} 811 812/** 813 * downloads a file from the net and saves it to the given location 814 * 815 * @author Andreas Gohr <andi@splitbrain.org> 816 */ 817function download($url,$file){ 818 $fp = @fopen($url,"rb"); 819 if(!$fp) return false; 820 821 while(!feof($fp)){ 822 $cont.= fread($fp,1024); 823 } 824 fclose($fp); 825 826 $fp2 = @fopen($file,"w"); 827 if(!$fp2) return false; 828 fwrite($fp2,$cont); 829 fclose($fp2); 830 return true; 831} 832 833/** 834 * extracts the query from a google referer 835 * 836 * @todo should be more generic and support yahoo et al 837 * @author Andreas Gohr <andi@splitbrain.org> 838 */ 839function getGoogleQuery(){ 840 $url = parse_url($_SERVER['HTTP_REFERER']); 841 842 if(!preg_match("#google\.#i",$url['host'])) return ''; 843 $query = array(); 844 parse_str($url['query'],$query); 845 846 return $query['q']; 847} 848 849/** 850 * Try to set correct locale 851 * 852 * @deprecated No longer used 853 * @author Andreas Gohr <andi@splitbrain.org> 854 */ 855function setCorrectLocale(){ 856 global $conf; 857 global $lang; 858 859 $enc = strtoupper($lang['encoding']); 860 foreach ($lang['locales'] as $loc){ 861 //try locale 862 if(@setlocale(LC_ALL,$loc)) return; 863 //try loceale with encoding 864 if(@setlocale(LC_ALL,"$loc.$enc")) return; 865 } 866 //still here? try to set from environment 867 @setlocale(LC_ALL,""); 868} 869 870/** 871 * Return the human readable size of a file 872 * 873 * @param int $size A file size 874 * @param int $dec A number of decimal places 875 * @author Martin Benjamin <b.martin@cybernet.ch> 876 * @author Aidan Lister <aidan@php.net> 877 * @version 1.0.0 878 */ 879function filesize_h($size, $dec = 1){ 880 $sizes = array('B', 'KB', 'MB', 'GB'); 881 $count = count($sizes); 882 $i = 0; 883 884 while ($size >= 1024 && ($i < $count - 1)) { 885 $size /= 1024; 886 $i++; 887 } 888 889 return round($size, $dec) . ' ' . $sizes[$i]; 890} 891 892/** 893 * Run a few sanity checks 894 * 895 * @author Andreas Gohr <andi@splitbrain.org> 896 */ 897function getVersion(){ 898 //import version string 899 if(@file_exists('VERSION')){ 900 //official release 901 return 'Release '.io_readfile('VERSION'); 902 }elseif(is_dir('_darcs')){ 903 //darcs checkout 904 $inv = file('_darcs/inventory'); 905 $inv = preg_grep('#andi@splitbrain\.org\*\*\d{14}#',$inv); 906 $cur = array_pop($inv); 907 preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches); 908 return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3]; 909 }else{ 910 return 'snapshot?'; 911 } 912} 913 914/** 915 * Run a few sanity checks 916 * 917 * @author Andreas Gohr <andi@splitbrain.org> 918 */ 919function check(){ 920 global $conf; 921 global $INFO; 922 923 msg('DokuWiki version: '.getVersion(),1); 924 925 if(version_compare(phpversion(),'4.3.0','<')){ 926 msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1); 927 }elseif(version_compare(phpversion(),'4.3.10','<')){ 928 msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0); 929 }else{ 930 msg('PHP version '.phpversion(),1); 931 } 932 933 if(is_writable($conf['changelog'])){ 934 msg('Changelog is writable',1); 935 }else{ 936 msg('Changelog is not writable',-1); 937 } 938 939 if(is_writable($conf['datadir'])){ 940 msg('Datadir is writable',1); 941 }else{ 942 msg('Datadir is not writable',-1); 943 } 944 945 if(is_writable($conf['olddir'])){ 946 msg('Attic is writable',1); 947 }else{ 948 msg('Attic is not writable',-1); 949 } 950 951 if(is_writable($conf['mediadir'])){ 952 msg('Mediadir is writable',1); 953 }else{ 954 msg('Mediadir is not writable',-1); 955 } 956 957 if(is_writable('conf/users.auth')){ 958 msg('conf/users.auth is writable',1); 959 }else{ 960 msg('conf/users.auth is not writable',0); 961 } 962 963 if(function_exists('mb_strpos')){ 964 if(defined('UTF8_NOMBSTRING')){ 965 msg('mb_string extension is available but will not be used',0); 966 }else{ 967 msg('mb_string extension is available and will be used',1); 968 } 969 }else{ 970 msg('mb_string extension not available - PHP only replacements will be used',0); 971 } 972 973 msg('Your current permission for this page is '.$INFO['perm'],0); 974 975 if(is_writable($INFO['filepath'])){ 976 msg('The current page is writable by the webserver',0); 977 }else{ 978 msg('The current page is not writable by the webserver',0); 979 } 980 981 if($INFO['writable']){ 982 msg('The current page is writable by you',0); 983 }else{ 984 msg('The current page is not writable you',0); 985 } 986} 987?> 988