1<?php 2/** 3 * DokuWiki Actions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9if(!defined('DOKU_INC')) die('meh.'); 10require_once(DOKU_INC.'inc/template.php'); 11 12 13/** 14 * Call the needed action handlers 15 * 16 * @author Andreas Gohr <andi@splitbrain.org> 17 * @triggers ACTION_ACT_PREPROCESS 18 * @triggers ACTION_HEADERS_SEND 19 */ 20function act_dispatch(){ 21 global $INFO; 22 global $ACT; 23 global $ID; 24 global $QUERY; 25 global $lang; 26 global $conf; 27 global $license; 28 29 $preact = $ACT; 30 31 // give plugins an opportunity to process the action 32 $evt = new Doku_Event('ACTION_ACT_PREPROCESS',$ACT); 33 if ($evt->advise_before()) { 34 35 //sanitize $ACT 36 $ACT = act_clean($ACT); 37 38 //check if searchword was given - else just show 39 $s = cleanID($QUERY); 40 if($ACT == 'search' && empty($s)){ 41 $ACT = 'show'; 42 } 43 44 //login stuff 45 if(in_array($ACT,array('login','logout'))){ 46 $ACT = act_auth($ACT); 47 } 48 49 //check if user is asking to (un)subscribe a page 50 if($ACT == 'subscribe' || $ACT == 'unsubscribe') 51 $ACT = act_subscription($ACT); 52 53 //check if user is asking to (un)subscribe a namespace 54 if($ACT == 'subscribens' || $ACT == 'unsubscribens') 55 $ACT = act_subscriptionns($ACT); 56 57 //check permissions 58 $ACT = act_permcheck($ACT); 59 60 //register 61 $nil = array(); 62 if($ACT == 'register' && $_POST['save'] && register()){ 63 $ACT = 'login'; 64 } 65 66 if ($ACT == 'resendpwd' && act_resendpwd()) { 67 $ACT = 'login'; 68 } 69 70 //update user profile 71 if ($ACT == 'profile') { 72 if(!$_SERVER['REMOTE_USER']) { 73 $ACT = 'login'; 74 } else { 75 if(updateprofile()) { 76 msg($lang['profchanged'],1); 77 $ACT = 'show'; 78 } 79 } 80 } 81 82 //save 83 if($ACT == 'save'){ 84 if(checkSecurityToken()){ 85 $ACT = act_save($ACT); 86 }else{ 87 $ACT = 'show'; 88 } 89 } 90 91 //cancel conflicting edit 92 if($ACT == 'cancel') 93 $ACT = 'show'; 94 95 //draft deletion 96 if($ACT == 'draftdel') 97 $ACT = act_draftdel($ACT); 98 99 //draft saving on preview 100 if($ACT == 'preview') 101 $ACT = act_draftsave($ACT); 102 103 //edit 104 if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){ 105 $ACT = act_edit($ACT); 106 }else{ 107 unlock($ID); //try to unlock 108 } 109 110 //handle export 111 if(substr($ACT,0,7) == 'export_') 112 $ACT = act_export($ACT); 113 114 //display some infos 115 if($ACT == 'check'){ 116 check(); 117 $ACT = 'show'; 118 } 119 120 //handle admin tasks 121 if($ACT == 'admin'){ 122 // retrieve admin plugin name from $_REQUEST['page'] 123 if (!empty($_REQUEST['page'])) { 124 $pluginlist = plugin_list('admin'); 125 if (in_array($_REQUEST['page'], $pluginlist)) { 126 // attempt to load the plugin 127 if ($plugin =& plugin_load('admin',$_REQUEST['page']) !== NULL) 128 $plugin->handle(); 129 } 130 } 131 } 132 133 // check permissions again - the action may have changed 134 $ACT = act_permcheck($ACT); 135 } // end event ACTION_ACT_PREPROCESS default action 136 $evt->advise_after(); 137 unset($evt); 138 139 // when action 'show', the intial not 'show' and POST, do a redirect 140 if($ACT == 'show' && $preact != 'show' && strtolower($_SERVER['REQUEST_METHOD']) == 'post'){ 141 act_redirect($ID,$preact); 142 } 143 144 //call template FIXME: all needed vars available? 145 $headers[] = 'Content-Type: text/html; charset=utf-8'; 146 trigger_event('ACTION_HEADERS_SEND',$headers,'act_sendheaders'); 147 148 include(template('main.php')); 149 // output for the commands is now handled in inc/templates.php 150 // in function tpl_content() 151} 152 153function act_sendheaders($headers) { 154 foreach ($headers as $hdr) header($hdr); 155} 156 157/** 158 * Sanitize the action command 159 * 160 * Add all allowed commands here. 161 * 162 * @author Andreas Gohr <andi@splitbrain.org> 163 */ 164function act_clean($act){ 165 global $lang; 166 global $conf; 167 168 // check if the action was given as array key 169 if(is_array($act)){ 170 list($act) = array_keys($act); 171 } 172 173 //remove all bad chars 174 $act = strtolower($act); 175 $act = preg_replace('/[^1-9a-z_]+/','',$act); 176 177 if($act == 'export_html') $act = 'export_xhtml'; 178 if($act == 'export_htmlbody') $act = 'export_xhtmlbody'; 179 180 // check if action is disabled 181 if(!actionOK($act)){ 182 msg('Command disabled: '.htmlspecialchars($act),-1); 183 return 'show'; 184 } 185 186 //disable all acl related commands if ACL is disabled 187 if(!$conf['useacl'] && in_array($act,array('login','logout','register','admin', 188 'subscribe','unsubscribe','profile', 189 'resendpwd','subscribens','unsubscribens',))){ 190 msg('Command unavailable: '.htmlspecialchars($act),-1); 191 return 'show'; 192 } 193 194 if(!in_array($act,array('login','logout','register','save','cancel','edit','draft', 195 'preview','search','show','check','index','revisions', 196 'diff','recent','backlink','admin','subscribe', 197 'unsubscribe','profile','resendpwd','recover','wordblock', 198 'draftdel','subscribens','unsubscribens',)) && substr($act,0,7) != 'export_' ) { 199 msg('Command unknown: '.htmlspecialchars($act),-1); 200 return 'show'; 201 } 202 return $act; 203} 204 205/** 206 * Run permissionchecks 207 * 208 * @author Andreas Gohr <andi@splitbrain.org> 209 */ 210function act_permcheck($act){ 211 global $INFO; 212 global $conf; 213 214 if(in_array($act,array('save','preview','edit','recover'))){ 215 if($INFO['exists']){ 216 if($act == 'edit'){ 217 //the edit function will check again and do a source show 218 //when no AUTH_EDIT available 219 $permneed = AUTH_READ; 220 }else{ 221 $permneed = AUTH_EDIT; 222 } 223 }else{ 224 $permneed = AUTH_CREATE; 225 } 226 }elseif(in_array($act,array('login','search','recent','profile'))){ 227 $permneed = AUTH_NONE; 228 }elseif($act == 'register'){ 229 $permneed = AUTH_NONE; 230 }elseif($act == 'resendpwd'){ 231 $permneed = AUTH_NONE; 232 }elseif($act == 'admin'){ 233 if($INFO['ismanager']){ 234 // if the manager has the needed permissions for a certain admin 235 // action is checked later 236 $permneed = AUTH_READ; 237 }else{ 238 $permneed = AUTH_ADMIN; 239 } 240 }else{ 241 $permneed = AUTH_READ; 242 } 243 if($INFO['perm'] >= $permneed) return $act; 244 245 return 'denied'; 246} 247 248/** 249 * Handle 'draftdel' 250 * 251 * Deletes the draft for the current page and user 252 */ 253function act_draftdel($act){ 254 global $INFO; 255 @unlink($INFO['draft']); 256 $INFO['draft'] = null; 257 return 'show'; 258} 259 260/** 261 * Saves a draft on preview 262 * 263 * @todo this currently duplicates code from ajax.php :-/ 264 */ 265function act_draftsave($act){ 266 global $INFO; 267 global $ID; 268 global $conf; 269 if($conf['usedraft'] && $_POST['wikitext']){ 270 $draft = array('id' => $ID, 271 'prefix' => $_POST['prefix'], 272 'text' => $_POST['wikitext'], 273 'suffix' => $_POST['suffix'], 274 'date' => $_POST['date'], 275 'client' => $INFO['client'], 276 ); 277 $cname = getCacheName($draft['client'].$ID,'.draft'); 278 if(io_saveFile($cname,serialize($draft))){ 279 $INFO['draft'] = $cname; 280 } 281 } 282 return $act; 283} 284 285/** 286 * Handle 'save' 287 * 288 * Checks for spam and conflicts and saves the page. 289 * Does a redirect to show the page afterwards or 290 * returns a new action. 291 * 292 * @author Andreas Gohr <andi@splitbrain.org> 293 */ 294function act_save($act){ 295 global $ID; 296 global $DATE; 297 global $PRE; 298 global $TEXT; 299 global $SUF; 300 global $SUM; 301 302 //spam check 303 if(checkwordblock()) 304 return 'wordblock'; 305 //conflict check //FIXME use INFO 306 if($DATE != 0 && @filemtime(wikiFN($ID)) > $DATE ) 307 return 'conflict'; 308 309 //save it 310 saveWikiText($ID,con($PRE,$TEXT,$SUF,1),$SUM,$_REQUEST['minor']); //use pretty mode for con 311 //unlock it 312 unlock($ID); 313 314 //delete draft 315 act_draftdel($act); 316 session_write_close(); 317 318 // when done, show page 319 return 'show'; 320} 321 322/** 323 * Do a redirect after receiving post data 324 * 325 * Tries to add the section id as hash mark after section editing 326 */ 327function act_redirect($id,$preact){ 328 global $PRE; 329 global $TEXT; 330 global $MSG; 331 332 //are there any undisplayed messages? keep them in session for display 333 //on the next page 334 if(isset($MSG) && count($MSG)){ 335 //reopen session, store data and close session again 336 @session_start(); 337 $_SESSION[DOKU_COOKIE]['msg'] = $MSG; 338 session_write_close(); 339 } 340 341 //get section name when coming from section edit 342 if($PRE && preg_match('/^\s*==+([^=\n]+)/',$TEXT,$match)){ 343 $check = false; 344 $title = sectionID($match[0],$check); 345 } 346 347 $opts = array( 348 'id' => $id, 349 'fragment' => $title, 350 'preact' => $preact 351 ); 352 trigger_event('ACTION_SHOW_REDIRECT',$opts,'act_redirect_execute'); 353} 354 355function act_redirect_execute($opts){ 356 $go = wl($opts['id'],'',true); 357 if($opts['fragment']) $go .= '#'.$opts['fragment']; 358 359 //show it 360 header("Location: $go"); 361 exit(); 362} 363 364/** 365 * Handle 'login', 'logout' 366 * 367 * @author Andreas Gohr <andi@splitbrain.org> 368 */ 369function act_auth($act){ 370 global $ID; 371 global $INFO; 372 373 //already logged in? 374 if($_SERVER['REMOTE_USER'] && $act=='login'){ 375 return 'show'; 376 } 377 378 //handle logout 379 if($act=='logout'){ 380 $lockedby = checklock($ID); //page still locked? 381 if($lockedby == $_SERVER['REMOTE_USER']) 382 unlock($ID); //try to unlock 383 384 // do the logout stuff 385 auth_logoff(); 386 387 // rebuild info array 388 $INFO = pageinfo(); 389 390 act_redirect($ID,'login'); 391 } 392 393 return $act; 394} 395 396/** 397 * Handle 'edit', 'preview' 398 * 399 * @author Andreas Gohr <andi@splitbrain.org> 400 */ 401function act_edit($act){ 402 global $ID; 403 global $INFO; 404 405 //check if locked by anyone - if not lock for my self 406 $lockedby = checklock($ID); 407 if($lockedby) return 'locked'; 408 409 lock($ID); 410 return $act; 411} 412 413/** 414 * Export a wiki page for various formats 415 * 416 * Triggers ACTION_EXPORT_POSTPROCESS 417 * 418 * Event data: 419 * data['id'] -- page id 420 * data['mode'] -- requested export mode 421 * data['headers'] -- export headers 422 * data['output'] -- export output 423 * 424 * @author Andreas Gohr <andi@splitbrain.org> 425 * @author Michael Klier <chi@chimeric.de> 426 */ 427function act_export($act){ 428 global $ID; 429 global $REV; 430 global $conf; 431 global $lang; 432 433 $pre = ''; 434 $post = ''; 435 $output = ''; 436 $headers = array(); 437 438 // search engines: never cache exported docs! (Google only currently) 439 $headers['X-Robots-Tag'] = 'noindex'; 440 441 $mode = substr($act,7); 442 switch($mode) { 443 case 'raw': 444 $headers['Content-Type'] = 'text/plain; charset=utf-8'; 445 $output = rawWiki($ID,$REV); 446 break; 447 case 'xhtml': 448 $pre .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' . DOKU_LF; 449 $pre .= ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . DOKU_LF; 450 $pre .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$conf['lang'].'"' . DOKU_LF; 451 $pre .= ' lang="'.$conf['lang'].'" dir="'.$lang['direction'].'">' . DOKU_LF; 452 $pre .= '<head>' . DOKU_LF; 453 $pre .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . DOKU_LF; 454 $pre .= ' <title>'.$ID.'</title>' . DOKU_LF; 455 456 // get metaheaders 457 ob_start(); 458 tpl_metaheaders(); 459 $pre .= ob_get_clean(); 460 461 $pre .= '</head>' . DOKU_LF; 462 $pre .= '<body>' . DOKU_LF; 463 $pre .= '<div class="dokuwiki export">' . DOKU_LF; 464 465 // get toc 466 $pre .= tpl_toc(true); 467 468 $headers['Content-Type'] = 'text/html; charset=utf-8'; 469 $output = p_wiki_xhtml($ID,$REV,false); 470 471 $post .= '</div>' . DOKU_LF; 472 $post .= '</body>' . DOKU_LF; 473 $post .= '</html>' . DOKU_LF; 474 break; 475 case 'xhtmlbody': 476 $headers['Content-Type'] = 'text/html; charset=utf-8'; 477 $output = p_wiki_xhtml($ID,$REV,false); 478 break; 479 default: 480 $headers = p_get_metadata($ID,"format $mode"); 481 $output = p_cached_output(wikiFN($ID,$REV), $mode); 482 break; 483 } 484 485 // prepare event data 486 $data = array(); 487 $data['id'] = $ID; 488 $data['mode'] = $mode; 489 $data['headers'] = $headers; 490 $data['output'] =& $output; 491 492 trigger_event('ACTION_EXPORT_POSTPROCESS', $data); 493 494 if(!empty($data['output'])){ 495 if(is_array($data['headers'])) foreach($data['headers'] as $key => $val){ 496 header("$key: $val"); 497 } 498 print $pre.$data['output'].$post; 499 exit; 500 } 501 return 'show'; 502} 503 504/** 505 * Handle page 'subscribe', 'unsubscribe' 506 * 507 * @author Steven Danz <steven-danz@kc.rr.com> 508 * @todo localize 509 */ 510function act_subscription($act){ 511 global $ID; 512 global $INFO; 513 global $lang; 514 515 $file=metaFN($ID,'.mlist'); 516 if ($act=='subscribe' && !$INFO['subscribed']){ 517 if ($INFO['userinfo']['mail']){ 518 if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) { 519 $INFO['subscribed'] = true; 520 msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1); 521 } else { 522 msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1); 523 } 524 } else { 525 msg($lang['subscribe_noaddress']); 526 } 527 } elseif ($act=='unsubscribe' && $INFO['subscribed']){ 528 if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) { 529 $INFO['subscribed'] = false; 530 msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1); 531 } else { 532 msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1); 533 } 534 } 535 536 return 'show'; 537} 538 539/** 540 * Handle namespace 'subscribe', 'unsubscribe' 541 * 542 */ 543function act_subscriptionns($act){ 544 global $ID; 545 global $INFO; 546 global $lang; 547 548 if(!getNS($ID)) { 549 $file = metaFN(getNS($ID),'.mlist'); 550 $ns = "root"; 551 } else { 552 $file = metaFN(getNS($ID),'/.mlist'); 553 $ns = getNS($ID); 554 } 555 556 // reuse strings used to display the status of the subscribe action 557 $act_msg = rtrim($act, 'ns'); 558 559 if ($act=='subscribens' && !$INFO['subscribedns']){ 560 if ($INFO['userinfo']['mail']){ 561 if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) { 562 $INFO['subscribedns'] = true; 563 msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1); 564 } else { 565 msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1); 566 } 567 } else { 568 msg($lang['subscribe_noaddress']); 569 } 570 } elseif ($act=='unsubscribens' && $INFO['subscribedns']){ 571 if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) { 572 $INFO['subscribedns'] = false; 573 msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1); 574 } else { 575 msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1); 576 } 577 } 578 579 return 'show'; 580} 581 582//Setup VIM: ex: et ts=2 enc=utf-8 : 583