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 send_redirect($go); 361} 362 363/** 364 * Handle 'login', 'logout' 365 * 366 * @author Andreas Gohr <andi@splitbrain.org> 367 */ 368function act_auth($act){ 369 global $ID; 370 global $INFO; 371 372 //already logged in? 373 if($_SERVER['REMOTE_USER'] && $act=='login'){ 374 return 'show'; 375 } 376 377 //handle logout 378 if($act=='logout'){ 379 $lockedby = checklock($ID); //page still locked? 380 if($lockedby == $_SERVER['REMOTE_USER']) 381 unlock($ID); //try to unlock 382 383 // do the logout stuff 384 auth_logoff(); 385 386 // rebuild info array 387 $INFO = pageinfo(); 388 389 act_redirect($ID,'login'); 390 } 391 392 return $act; 393} 394 395/** 396 * Handle 'edit', 'preview' 397 * 398 * @author Andreas Gohr <andi@splitbrain.org> 399 */ 400function act_edit($act){ 401 global $ID; 402 global $INFO; 403 404 //check if locked by anyone - if not lock for my self 405 $lockedby = checklock($ID); 406 if($lockedby) return 'locked'; 407 408 lock($ID); 409 return $act; 410} 411 412/** 413 * Export a wiki page for various formats 414 * 415 * Triggers ACTION_EXPORT_POSTPROCESS 416 * 417 * Event data: 418 * data['id'] -- page id 419 * data['mode'] -- requested export mode 420 * data['headers'] -- export headers 421 * data['output'] -- export output 422 * 423 * @author Andreas Gohr <andi@splitbrain.org> 424 * @author Michael Klier <chi@chimeric.de> 425 */ 426function act_export($act){ 427 global $ID; 428 global $REV; 429 global $conf; 430 global $lang; 431 432 $pre = ''; 433 $post = ''; 434 $output = ''; 435 $headers = array(); 436 437 // search engines: never cache exported docs! (Google only currently) 438 $headers['X-Robots-Tag'] = 'noindex'; 439 440 $mode = substr($act,7); 441 switch($mode) { 442 case 'raw': 443 $headers['Content-Type'] = 'text/plain; charset=utf-8'; 444 $output = rawWiki($ID,$REV); 445 break; 446 case 'xhtml': 447 $pre .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' . DOKU_LF; 448 $pre .= ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . DOKU_LF; 449 $pre .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$conf['lang'].'"' . DOKU_LF; 450 $pre .= ' lang="'.$conf['lang'].'" dir="'.$lang['direction'].'">' . DOKU_LF; 451 $pre .= '<head>' . DOKU_LF; 452 $pre .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . DOKU_LF; 453 $pre .= ' <title>'.$ID.'</title>' . DOKU_LF; 454 455 // get metaheaders 456 ob_start(); 457 tpl_metaheaders(); 458 $pre .= ob_get_clean(); 459 460 $pre .= '</head>' . DOKU_LF; 461 $pre .= '<body>' . DOKU_LF; 462 $pre .= '<div class="dokuwiki export">' . DOKU_LF; 463 464 // get toc 465 $pre .= tpl_toc(true); 466 467 $headers['Content-Type'] = 'text/html; charset=utf-8'; 468 $output = p_wiki_xhtml($ID,$REV,false); 469 470 $post .= '</div>' . DOKU_LF; 471 $post .= '</body>' . DOKU_LF; 472 $post .= '</html>' . DOKU_LF; 473 break; 474 case 'xhtmlbody': 475 $headers['Content-Type'] = 'text/html; charset=utf-8'; 476 $output = p_wiki_xhtml($ID,$REV,false); 477 break; 478 default: 479 $headers = p_get_metadata($ID,"format $mode"); 480 $output = p_cached_output(wikiFN($ID,$REV), $mode); 481 break; 482 } 483 484 // prepare event data 485 $data = array(); 486 $data['id'] = $ID; 487 $data['mode'] = $mode; 488 $data['headers'] = $headers; 489 $data['output'] =& $output; 490 491 trigger_event('ACTION_EXPORT_POSTPROCESS', $data); 492 493 if(!empty($data['output'])){ 494 if(is_array($data['headers'])) foreach($data['headers'] as $key => $val){ 495 header("$key: $val"); 496 } 497 print $pre.$data['output'].$post; 498 exit; 499 } 500 return 'show'; 501} 502 503/** 504 * Handle page 'subscribe', 'unsubscribe' 505 * 506 * @author Steven Danz <steven-danz@kc.rr.com> 507 * @todo localize 508 */ 509function act_subscription($act){ 510 global $ID; 511 global $INFO; 512 global $lang; 513 514 $file=metaFN($ID,'.mlist'); 515 if ($act=='subscribe' && !$INFO['subscribed']){ 516 if ($INFO['userinfo']['mail']){ 517 if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) { 518 $INFO['subscribed'] = true; 519 msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1); 520 } else { 521 msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1); 522 } 523 } else { 524 msg($lang['subscribe_noaddress']); 525 } 526 } elseif ($act=='unsubscribe' && $INFO['subscribed']){ 527 if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) { 528 $INFO['subscribed'] = false; 529 msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1); 530 } else { 531 msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1); 532 } 533 } 534 535 return 'show'; 536} 537 538/** 539 * Handle namespace 'subscribe', 'unsubscribe' 540 * 541 */ 542function act_subscriptionns($act){ 543 global $ID; 544 global $INFO; 545 global $lang; 546 547 if(!getNS($ID)) { 548 $file = metaFN(getNS($ID),'.mlist'); 549 $ns = "root"; 550 } else { 551 $file = metaFN(getNS($ID),'/.mlist'); 552 $ns = getNS($ID); 553 } 554 555 // reuse strings used to display the status of the subscribe action 556 $act_msg = rtrim($act, 'ns'); 557 558 if ($act=='subscribens' && !$INFO['subscribedns']){ 559 if ($INFO['userinfo']['mail']){ 560 if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) { 561 $INFO['subscribedns'] = true; 562 msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1); 563 } else { 564 msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1); 565 } 566 } else { 567 msg($lang['subscribe_noaddress']); 568 } 569 } elseif ($act=='unsubscribens' && $INFO['subscribedns']){ 570 if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) { 571 $INFO['subscribedns'] = false; 572 msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1); 573 } else { 574 msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1); 575 } 576 } 577 578 return 'show'; 579} 580 581//Setup VIM: ex: et ts=2 enc=utf-8 : 582