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