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 path to the mediafile specified by ID 420 * 421 * The filename is URL encoded to protect Unicode chars 422 * 423 * @author Andreas Gohr <andi@splitbrain.org> 424 */ 425function mediaFN($id){ 426 global $conf; 427 $id = cleanID($id); 428 $id = str_replace(':','/',$id); 429 $fn = $conf['mediadir'].'/'.utf8_encodeFN($id); 430 return $fn; 431} 432 433/** 434 * Returns the full filepath to a localized textfile if local 435 * version isn't found the english one is returned 436 * 437 * @author Andreas Gohr <andi@splitbrain.org> 438 */ 439function localeFN($id){ 440 global $conf; 441 $file = './lang/'.$conf['lang'].'/'.$id.'.txt'; 442 if(!@file_exists($file)){ 443 //fall back to english 444 $file = './lang/en/'.$id.'.txt'; 445 } 446 return cleanText($file); 447} 448 449/** 450 * convert line ending to unix format 451 * 452 * @see formText() for 2crlf conversion 453 * @author Andreas Gohr <andi@splitbrain.org> 454 */ 455function cleanText($text){ 456 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 457 return $text; 458} 459 460/** 461 * Prepares text for print in Webforms by encoding special chars. 462 * It also converts line endings to Windows format which is 463 * pseudo standard for webforms. 464 * 465 * @see cleanText() for 2unix conversion 466 * @author Andreas Gohr <andi@splitbrain.org> 467 */ 468function formText($text){ 469 $text = preg_replace("/\012/","\015\012",$text); 470 return htmlspecialchars($text); 471} 472 473/** 474 * Returns the specified local text in parsed format 475 * 476 * @author Andreas Gohr <andi@splitbrain.org> 477 */ 478function parsedLocale($id){ 479 //disable section editing 480 global $parser; 481 $se = $parser['secedit']; 482 $parser['secedit'] = false; 483 //fetch parsed locale 484 $html = io_cacheParse(localeFN($id)); 485 //reset section editing 486 $parser['secedit'] = $se; 487 return $html; 488} 489 490/** 491 * Returns the specified local text in raw format 492 * 493 * @author Andreas Gohr <andi@splitbrain.org> 494 */ 495function rawLocale($id){ 496 return io_readFile(localeFN($id)); 497} 498 499 500/** 501 * Returns the parsed Wikitext for the given id and revision. 502 * 503 * If $excuse is true an explanation is returned if the file 504 * wasn't found 505 * 506 * @author Andreas Gohr <andi@splitbrain.org> 507 */ 508function parsedWiki($id,$rev='',$excuse=true){ 509 $file = wikiFN($id,$rev); 510 $ret = ''; 511 512 //ensure $id is in global $ID (needed for parsing) 513 global $ID; 514 $ID = $id; 515 516 if($rev){ 517 if(@file_exists($file)){ 518 $ret = parse(io_readFile($file)); 519 }elseif($excuse){ 520 $ret = parsedLocale('norev'); 521 } 522 }else{ 523 if(@file_exists($file)){ 524 if(!defined('DOKU_USENEWPARSER')){ 525 $ret = io_cacheParse($file); 526 }else{ 527 msg('using new parser'); 528 require_once(DOKU_INC.'inc/parser/action.php'); 529 $ret = render_as_xhtml(parse_to_instructions(io_readFile($file))); 530 } 531 }elseif($excuse){ 532 $ret = parsedLocale('newpage'); 533 } 534 } 535 536 return $ret; 537} 538 539/** 540 * Returns the raw WikiText 541 * 542 * @author Andreas Gohr <andi@splitbrain.org> 543 */ 544function rawWiki($id,$rev=''){ 545 return io_readFile(wikiFN($id,$rev)); 546} 547 548/** 549 * Returns the raw Wiki Text in three slices. 550 * 551 * The range parameter needs to have the form "from-to" 552 * and gives the range of the section. 553 * The returned order is prefix, section and suffix. 554 * 555 * @author Andreas Gohr <andi@splitbrain.org> 556 */ 557function rawWikiSlices($range,$id,$rev=''){ 558 list($from,$to) = split('-',$range,2); 559 $text = io_readFile(wikiFN($id,$rev)); 560 $text = split("\n",$text); 561 if(!$from) $from = 0; 562 if(!$to) $to = count($text); 563 564 $slices[0] = join("\n",array_slice($text,0,$from)); 565 $slices[1] = join("\n",array_slice($text,$from,$to + 1 - $from)); 566 $slices[2] = join("\n",array_slice($text,$to+1)); 567 568 return $slices; 569} 570 571/** 572 * Joins wiki text slices 573 * 574 * function to join the text slices with correct lineendings again. 575 * When the pretty parameter is set to true it adds additional empty 576 * lines between sections if needed (used on saving). 577 * 578 * @author Andreas Gohr <andi@splitbrain.org> 579 */ 580function con($pre,$text,$suf,$pretty=false){ 581 582 if($pretty){ 583 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 584 if($suf && substr($text,-1) != "\n") $text .= "\n"; 585 } 586 587 if($pre) $pre .= "\n"; 588 if($suf) $text .= "\n"; 589 return $pre.$text.$suf; 590} 591 592/** 593 * print debug messages 594 * 595 * little function to print the content of a var 596 * 597 * @author Andreas Gohr <andi@splitbrain.org> 598 */ 599function dbg($msg,$hidden=false){ 600 (!$hidden) ? print '<pre class="dbg">' : print "<!--\n"; 601 print_r($msg); 602 (!$hidden) ? print '</pre>' : print "\n-->"; 603} 604 605/** 606 * Add's an entry to the changelog 607 * 608 * @author Andreas Gohr <andi@splitbrain.org> 609 */ 610function addLogEntry($date,$id,$summary=""){ 611 global $conf; 612 $id = cleanID($id);//FIXME not needed anymore? 613 614 if(!@is_writable($conf['changelog'])){ 615 msg($conf['changelog'].' is not writable!',-1); 616 return; 617 } 618 619 if(!$date) $date = time(); //use current time if none supplied 620 $remote = $_SERVER['REMOTE_ADDR']; 621 $user = $_SERVER['REMOTE_USER']; 622 623 $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n"; 624 625 //FIXME: use adjusted io_saveFile instead 626 $fh = fopen($conf['changelog'],'a'); 627 if($fh){ 628 fwrite($fh,$logline); 629 fclose($fh); 630 } 631} 632 633/** 634 * returns an array of recently changed files using the 635 * changelog 636 * 637 * @author Andreas Gohr <andi@splitbrain.org> 638 */ 639function getRecents($num=0,$incdel=false){ 640 global $conf; 641 $recent = array(); 642 if(!$num) $num = $conf['recent']; 643 644 if(!@is_readable($conf['changelog'])){ 645 msg($conf['changelog'].' is not readable',-1); 646 return $recent; 647 } 648 649 $loglines = file($conf['changelog']); 650 rsort($loglines); //reverse sort on timestamp 651 652 foreach ($loglines as $line){ 653 $line = rtrim($line); //remove newline 654 if(empty($line)) continue; //skip empty lines 655 $info = split("\t",$line); //split into parts 656 //add id if not in yet and file still exists and is allowed to read 657 if(!$recent[$info[2]] && 658 (@file_exists(wikiFN($info[2])) || $incdel) && 659 (auth_quickaclcheck($info[2]) >= AUTH_READ) 660 ){ 661 $recent[$info[2]]['date'] = $info[0]; 662 $recent[$info[2]]['ip'] = $info[1]; 663 $recent[$info[2]]['user'] = $info[3]; 664 $recent[$info[2]]['sum'] = $info[4]; 665 $recent[$info[2]]['del'] = !@file_exists(wikiFN($info[2])); 666 } 667 if(count($recent) >= $num){ 668 break; //finish if enough items found 669 } 670 } 671 return $recent; 672} 673 674/** 675 * gets additonal informations for a certain pagerevison 676 * from the changelog 677 * 678 * @author Andreas Gohr <andi@splitbrain.org> 679 */ 680function getRevisionInfo($id,$rev){ 681 global $conf; 682 683 if(!$rev) return(null); 684 685 $info = array(); 686 if(!@is_readable($conf['changelog'])){ 687 msg($conf['changelog'].' is not readable',-1); 688 return $recent; 689 } 690 $loglines = file($conf['changelog']); 691 $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines); 692 rsort($loglines); //reverse sort on timestamp (shouldn't be needed) 693 $line = split("\t",$loglines[0]); 694 $info['date'] = $line[0]; 695 $info['ip'] = $line[1]; 696 $info['user'] = $line[3]; 697 $info['sum'] = $line[4]; 698 return $info; 699} 700 701/** 702 * Saves a wikitext by calling io_saveFile 703 * 704 * @author Andreas Gohr <andi@splitbrain.org> 705 */ 706function saveWikiText($id,$text,$summary){ 707 global $conf; 708 global $lang; 709 umask($conf['umask']); 710 // ignore if no changes were made 711 if($text == rawWiki($id,'')){ 712 return; 713 } 714 715 $file = wikiFN($id); 716 $old = saveOldRevision($id); 717 718 if (empty($text)){ 719 // remove empty files 720 @unlink($file); 721 $del = true; 722 //autoset summary on deletion 723 if(empty($summary)) $summary = $lang['deleted']; 724 //remove empty namespaces 725 io_sweepNS($id); 726 }else{ 727 // save file (datadir is created in io_saveFile) 728 io_saveFile($file,$text); 729 $del = false; 730 } 731 732 addLogEntry(@filemtime($file),$id,$summary); 733 notify($id,$old,$summary); 734 735 //purge cache on add by updating the purgefile 736 if($conf['purgeonadd'] && (!$old || $del)){ 737 io_saveFile($conf['datadir'].'/_cache/purgefile',time()); 738 } 739} 740 741/** 742 * moves the current version to the attic and returns its 743 * revision date 744 * 745 * @author Andreas Gohr <andi@splitbrain.org> 746 */ 747function saveOldRevision($id){ 748 global $conf; 749 umask($conf['umask']); 750 $oldf = wikiFN($id); 751 if(!@file_exists($oldf)) return ''; 752 $date = filemtime($oldf); 753 $newf = wikiFN($id,$date); 754 if(substr($newf,-3)=='.gz'){ 755 io_saveFile($newf,rawWiki($id)); 756 }else{ 757 io_makeFileDir($newf); 758 copy($oldf, $newf); 759 } 760 return $date; 761} 762 763/** 764 * Sends a notify mail to the wikiadmin when a page was 765 * changed 766 * 767 * @author Andreas Gohr <andi@splitbrain.org> 768 */ 769function notify($id,$rev="",$summary=""){ 770 global $lang; 771 global $conf; 772 $hdrs =''; 773 if(empty($conf['notify'])) return; //notify enabled? 774 775 $text = rawLocale('mailtext'); 776 $text = str_replace('@DATE@',date($conf['dformat']),$text); 777 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 778 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 779 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 780 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 781 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 782 $text = str_replace('@SUMMARY@',$summary,$text); 783 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 784 785 if($rev){ 786 $subject = $lang['mail_changed'].' '.$id; 787 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 788 require_once("inc/DifferenceEngine.php"); 789 $df = new Diff(split("\n",rawWiki($id,$rev)), 790 split("\n",rawWiki($id))); 791 $dformat = new UnifiedDiffFormatter(); 792 $diff = $dformat->format($df); 793 }else{ 794 $subject=$lang['mail_newpage'].' '.$id; 795 $text = str_replace('@OLDPAGE@','none',$text); 796 $diff = rawWiki($id); 797 } 798 $text = str_replace('@DIFF@',$diff,$text); 799 800 mail_send($conf['notify'],$subject,$text,$conf['mailfrom']); 801} 802 803/** 804 * Return a list of available page revisons 805 * 806 * @author Andreas Gohr <andi@splitbrain.org> 807 */ 808function getRevisions($id){ 809 $revd = dirname(wikiFN($id,'foo')); 810 $revs = array(); 811 $clid = cleanID($id); 812 if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path 813 814 if (is_dir($revd) && $dh = opendir($revd)) { 815 while (($file = readdir($dh)) !== false) { 816 if (is_dir($revd.'/'.$file)) continue; 817 if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){ 818 $revs[]=$match[1]; 819 } 820 } 821 closedir($dh); 822 } 823 rsort($revs); 824 return $revs; 825} 826 827/** 828 * downloads a file from the net and saves it to the given location 829 * 830 * @author Andreas Gohr <andi@splitbrain.org> 831 */ 832function download($url,$file){ 833 $fp = @fopen($url,"rb"); 834 if(!$fp) return false; 835 836 while(!feof($fp)){ 837 $cont.= fread($fp,1024); 838 } 839 fclose($fp); 840 841 $fp2 = @fopen($file,"w"); 842 if(!$fp2) return false; 843 fwrite($fp2,$cont); 844 fclose($fp2); 845 return true; 846} 847 848/** 849 * extracts the query from a google referer 850 * 851 * @todo should be more generic and support yahoo et al 852 * @author Andreas Gohr <andi@splitbrain.org> 853 */ 854function getGoogleQuery(){ 855 $url = parse_url($_SERVER['HTTP_REFERER']); 856 857 if(!preg_match("#google\.#i",$url['host'])) return ''; 858 $query = array(); 859 parse_str($url['query'],$query); 860 861 return $query['q']; 862} 863 864/** 865 * Try to set correct locale 866 * 867 * @deprecated No longer used 868 * @author Andreas Gohr <andi@splitbrain.org> 869 */ 870function setCorrectLocale(){ 871 global $conf; 872 global $lang; 873 874 $enc = strtoupper($lang['encoding']); 875 foreach ($lang['locales'] as $loc){ 876 //try locale 877 if(@setlocale(LC_ALL,$loc)) return; 878 //try loceale with encoding 879 if(@setlocale(LC_ALL,"$loc.$enc")) return; 880 } 881 //still here? try to set from environment 882 @setlocale(LC_ALL,""); 883} 884 885/** 886 * Return the human readable size of a file 887 * 888 * @param int $size A file size 889 * @param int $dec A number of decimal places 890 * @author Martin Benjamin <b.martin@cybernet.ch> 891 * @author Aidan Lister <aidan@php.net> 892 * @version 1.0.0 893 */ 894function filesize_h($size, $dec = 1){ 895 $sizes = array('B', 'KB', 'MB', 'GB'); 896 $count = count($sizes); 897 $i = 0; 898 899 while ($size >= 1024 && ($i < $count - 1)) { 900 $size /= 1024; 901 $i++; 902 } 903 904 return round($size, $dec) . ' ' . $sizes[$i]; 905} 906 907/** 908 * Run a few sanity checks 909 * 910 * @author Andreas Gohr <andi@splitbrain.org> 911 */ 912function getVersion(){ 913 //import version string 914 if(@file_exists('VERSION')){ 915 //official release 916 return 'Release '.io_readfile('VERSION'); 917 }elseif(is_dir('_darcs')){ 918 //darcs checkout 919 $inv = file('_darcs/inventory'); 920 $inv = preg_grep('#andi@splitbrain\.org\*\*\d{14}#',$inv); 921 $cur = array_pop($inv); 922 preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches); 923 return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3]; 924 }else{ 925 return 'snapshot?'; 926 } 927} 928 929/** 930 * Run a few sanity checks 931 * 932 * @author Andreas Gohr <andi@splitbrain.org> 933 */ 934function check(){ 935 global $conf; 936 global $INFO; 937 938 msg('DokuWiki version: '.getVersion(),1); 939 940 if(version_compare(phpversion(),'4.3.0','<')){ 941 msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1); 942 }elseif(version_compare(phpversion(),'4.3.10','<')){ 943 msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0); 944 }else{ 945 msg('PHP version '.phpversion(),1); 946 } 947 948 if(is_writable($conf['changelog'])){ 949 msg('Changelog is writable',1); 950 }else{ 951 msg('Changelog is not writable',-1); 952 } 953 954 if(is_writable($conf['datadir'])){ 955 msg('Datadir is writable',1); 956 }else{ 957 msg('Datadir is not writable',-1); 958 } 959 960 if(is_writable($conf['olddir'])){ 961 msg('Attic is writable',1); 962 }else{ 963 msg('Attic is not writable',-1); 964 } 965 966 if(is_writable($conf['mediadir'])){ 967 msg('Mediadir is writable',1); 968 }else{ 969 msg('Mediadir is not writable',-1); 970 } 971 972 if(is_writable('conf/users.auth')){ 973 msg('conf/users.auth is writable',1); 974 }else{ 975 msg('conf/users.auth is not writable',0); 976 } 977 978 if(function_exists('mb_strpos')){ 979 if(defined('UTF8_NOMBSTRING')){ 980 msg('mb_string extension is available but will not be used',0); 981 }else{ 982 msg('mb_string extension is available and will be used',1); 983 } 984 }else{ 985 msg('mb_string extension not available - PHP only replacements will be used',0); 986 } 987 988 msg('Your current permission for this page is '.$INFO['perm'],0); 989 990 if(is_writable($INFO['filepath'])){ 991 msg('The current page is writable by the webserver',0); 992 }else{ 993 msg('The current page is not writable by the webserver',0); 994 } 995 996 if($INFO['writable']){ 997 msg('The current page is writable by you',0); 998 }else{ 999 msg('The current page is not writable you',0); 1000 } 1001} 1002 1003 1004//Setup VIM: ex: et ts=2 enc=utf-8 : 1005