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