French language update
[dokuwiki/radio.git] / inc / actions.php
blob1fda0584e1dd2353d450ddae424767cb01847c78
1 <?php
2 /**
3 * DokuWiki Actions
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Andreas Gohr <andi@splitbrain.org>
7 */
9 if(!defined('DOKU_INC')) die('meh.');
10 require_once(DOKU_INC.'inc/template.php');
13 /**
14 * Call the needed action handlers
16 * @author Andreas Gohr <andi@splitbrain.org>
17 * @triggers ACTION_ACT_PREPROCESS
18 * @triggers ACTION_HEADERS_SEND
20 function act_dispatch(){
21 global $INFO;
22 global $ACT;
23 global $ID;
24 global $QUERY;
25 global $lang;
26 global $conf;
27 global $license;
29 $preact = $ACT;
31 // give plugins an opportunity to process the action
32 $evt = new Doku_Event('ACTION_ACT_PREPROCESS',$ACT);
33 if ($evt->advise_before()) {
35 //sanitize $ACT
36 $ACT = act_clean($ACT);
38 //check if searchword was given - else just show
39 $s = cleanID($QUERY);
40 if($ACT == 'search' && empty($s)){
41 $ACT = 'show';
44 //login stuff
45 if(in_array($ACT,array('login','logout'))){
46 $ACT = act_auth($ACT);
49 //check if user is asking to (un)subscribe a page
50 if($ACT == 'subscribe') {
51 try {
52 $ACT = act_subscription($ACT);
53 } catch (Exception $e) {
54 msg($e->getMessage(), -1);
58 //check permissions
59 $ACT = act_permcheck($ACT);
61 //register
62 $nil = array();
63 if($ACT == 'register' && $_POST['save'] && register()){
64 $ACT = 'login';
67 if ($ACT == 'resendpwd' && act_resendpwd()) {
68 $ACT = 'login';
71 //update user profile
72 if ($ACT == 'profile') {
73 if(!$_SERVER['REMOTE_USER']) {
74 $ACT = 'login';
75 } else {
76 if(updateprofile()) {
77 msg($lang['profchanged'],1);
78 $ACT = 'show';
83 //revert
84 if($ACT == 'revert'){
85 if(checkSecurityToken()){
86 $ACT = act_revert($ACT);
87 }else{
88 $ACT = 'show';
92 //save
93 if($ACT == 'save'){
94 if(checkSecurityToken()){
95 $ACT = act_save($ACT);
96 }else{
97 $ACT = 'show';
101 //cancel conflicting edit
102 if($ACT == 'cancel')
103 $ACT = 'show';
105 //draft deletion
106 if($ACT == 'draftdel')
107 $ACT = act_draftdel($ACT);
109 //draft saving on preview
110 if($ACT == 'preview')
111 $ACT = act_draftsave($ACT);
113 //edit
114 if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){
115 $ACT = act_edit($ACT);
116 }else{
117 unlock($ID); //try to unlock
120 //handle export
121 if(substr($ACT,0,7) == 'export_')
122 $ACT = act_export($ACT);
124 //display some infos
125 if($ACT == 'check'){
126 check();
127 $ACT = 'show';
130 //handle admin tasks
131 if($ACT == 'admin'){
132 // retrieve admin plugin name from $_REQUEST['page']
133 if (!empty($_REQUEST['page'])) {
134 $pluginlist = plugin_list('admin');
135 if (in_array($_REQUEST['page'], $pluginlist)) {
136 // attempt to load the plugin
137 if ($plugin =& plugin_load('admin',$_REQUEST['page']) !== null)
138 $plugin->handle();
143 // check permissions again - the action may have changed
144 $ACT = act_permcheck($ACT);
145 } // end event ACTION_ACT_PREPROCESS default action
146 $evt->advise_after();
147 unset($evt);
149 // when action 'show', the intial not 'show' and POST, do a redirect
150 if($ACT == 'show' && $preact != 'show' && strtolower($_SERVER['REQUEST_METHOD']) == 'post'){
151 act_redirect($ID,$preact);
154 //call template FIXME: all needed vars available?
155 $headers[] = 'Content-Type: text/html; charset=utf-8';
156 trigger_event('ACTION_HEADERS_SEND',$headers,'act_sendheaders');
158 include(template('main.php'));
159 // output for the commands is now handled in inc/templates.php
160 // in function tpl_content()
163 function act_sendheaders($headers) {
164 foreach ($headers as $hdr) header($hdr);
168 * Sanitize the action command
170 * Add all allowed commands here.
172 * @author Andreas Gohr <andi@splitbrain.org>
174 function act_clean($act){
175 global $lang;
176 global $conf;
178 // check if the action was given as array key
179 if(is_array($act)){
180 list($act) = array_keys($act);
183 //remove all bad chars
184 $act = strtolower($act);
185 $act = preg_replace('/[^1-9a-z_]+/','',$act);
187 if($act == 'export_html') $act = 'export_xhtml';
188 if($act == 'export_htmlbody') $act = 'export_xhtmlbody';
190 // check if action is disabled
191 if(!actionOK($act)){
192 msg('Command disabled: '.htmlspecialchars($act),-1);
193 return 'show';
196 //disable all acl related commands if ACL is disabled
197 if(!$conf['useacl'] && in_array($act,array('login','logout','register','admin',
198 'subscribe','unsubscribe','profile','revert',
199 'resendpwd','subscribens','unsubscribens',))){
200 msg('Command unavailable: '.htmlspecialchars($act),-1);
201 return 'show';
204 if(!in_array($act,array('login','logout','register','save','cancel','edit','draft',
205 'preview','search','show','check','index','revisions',
206 'diff','recent','backlink','admin','subscribe','revert',
207 'unsubscribe','profile','resendpwd','recover','wordblock',
208 'draftdel','subscribens','unsubscribens',)) && substr($act,0,7) != 'export_' ) {
209 msg('Command unknown: '.htmlspecialchars($act),-1);
210 return 'show';
212 return $act;
216 * Run permissionchecks
218 * @author Andreas Gohr <andi@splitbrain.org>
220 function act_permcheck($act){
221 global $INFO;
222 global $conf;
224 if(in_array($act,array('save','preview','edit','recover'))){
225 if($INFO['exists']){
226 if($act == 'edit'){
227 //the edit function will check again and do a source show
228 //when no AUTH_EDIT available
229 $permneed = AUTH_READ;
230 }else{
231 $permneed = AUTH_EDIT;
233 }else{
234 $permneed = AUTH_CREATE;
236 }elseif(in_array($act,array('login','search','recent','profile'))){
237 $permneed = AUTH_NONE;
238 }elseif($act == 'revert'){
239 $permneed = AUTH_ADMIN;
240 if($INFO['ismanager']) $permneed = AUTH_EDIT;
241 }elseif($act == 'register'){
242 $permneed = AUTH_NONE;
243 }elseif($act == 'resendpwd'){
244 $permneed = AUTH_NONE;
245 }elseif($act == 'admin'){
246 if($INFO['ismanager']){
247 // if the manager has the needed permissions for a certain admin
248 // action is checked later
249 $permneed = AUTH_READ;
250 }else{
251 $permneed = AUTH_ADMIN;
253 }else{
254 $permneed = AUTH_READ;
256 if($INFO['perm'] >= $permneed) return $act;
258 return 'denied';
262 * Handle 'draftdel'
264 * Deletes the draft for the current page and user
266 function act_draftdel($act){
267 global $INFO;
268 @unlink($INFO['draft']);
269 $INFO['draft'] = null;
270 return 'show';
274 * Saves a draft on preview
276 * @todo this currently duplicates code from ajax.php :-/
278 function act_draftsave($act){
279 global $INFO;
280 global $ID;
281 global $conf;
282 if($conf['usedraft'] && $_POST['wikitext']){
283 $draft = array('id' => $ID,
284 'prefix' => $_POST['prefix'],
285 'text' => $_POST['wikitext'],
286 'suffix' => $_POST['suffix'],
287 'date' => $_POST['date'],
288 'client' => $INFO['client'],
290 $cname = getCacheName($draft['client'].$ID,'.draft');
291 if(io_saveFile($cname,serialize($draft))){
292 $INFO['draft'] = $cname;
295 return $act;
299 * Handle 'save'
301 * Checks for spam and conflicts and saves the page.
302 * Does a redirect to show the page afterwards or
303 * returns a new action.
305 * @author Andreas Gohr <andi@splitbrain.org>
307 function act_save($act){
308 global $ID;
309 global $DATE;
310 global $PRE;
311 global $TEXT;
312 global $SUF;
313 global $SUM;
315 //spam check
316 if(checkwordblock())
317 return 'wordblock';
318 //conflict check //FIXME use INFO
319 if($DATE != 0 && @filemtime(wikiFN($ID)) > $DATE )
320 return 'conflict';
322 //save it
323 saveWikiText($ID,con($PRE,$TEXT,$SUF,1),$SUM,$_REQUEST['minor']); //use pretty mode for con
324 //unlock it
325 unlock($ID);
327 //delete draft
328 act_draftdel($act);
329 session_write_close();
331 // when done, show page
332 return 'show';
336 * Revert to a certain revision
338 * @author Andreas Gohr <andi@splitbrain.org>
340 function act_revert($act){
341 global $ID;
342 global $REV;
343 global $lang;
345 // when no revision is given, delete current one
346 // FIXME this feature is not exposed in the GUI currently
347 $text = '';
348 $sum = $lang['deleted'];
349 if($REV){
350 $text = rawWiki($ID,$REV);
351 if(!$text) return 'show'; //something went wrong
352 $sum = $lang['restored'];
355 // spam check
356 if(checkwordblock($Text))
357 return 'wordblock';
359 saveWikiText($ID,$text,$sum,false);
360 msg($sum,1);
362 //delete any draft
363 act_draftdel($act);
364 session_write_close();
366 // when done, show current page
367 $_SERVER['REQUEST_METHOD'] = 'post'; //should force a redirect
368 $REV = '';
369 return 'show';
373 * Do a redirect after receiving post data
375 * Tries to add the section id as hash mark after section editing
377 function act_redirect($id,$preact){
378 global $PRE;
379 global $TEXT;
380 global $MSG;
382 //are there any undisplayed messages? keep them in session for display
383 //on the next page
384 if(isset($MSG) && count($MSG)){
385 //reopen session, store data and close session again
386 @session_start();
387 $_SESSION[DOKU_COOKIE]['msg'] = $MSG;
388 session_write_close();
391 $opts = array(
392 'id' => $id,
393 'preact' => $preact
395 //get section name when coming from section edit
396 if($PRE && preg_match('/^\s*==+([^=\n]+)/',$TEXT,$match)){
397 $check = false; //Byref
398 $opts['fragment'] = sectionID($match[0], $check);
401 trigger_event('ACTION_SHOW_REDIRECT',$opts,'act_redirect_execute');
404 function act_redirect_execute($opts){
405 $go = wl($opts['id'],'',true);
406 if(isset($opts['fragment'])) $go .= '#'.$opts['fragment'];
408 //show it
409 send_redirect($go);
413 * Handle 'login', 'logout'
415 * @author Andreas Gohr <andi@splitbrain.org>
417 function act_auth($act){
418 global $ID;
419 global $INFO;
421 //already logged in?
422 if(isset($_SERVER['REMOTE_USER']) && $act=='login'){
423 return 'show';
426 //handle logout
427 if($act=='logout'){
428 $lockedby = checklock($ID); //page still locked?
429 if($lockedby == $_SERVER['REMOTE_USER'])
430 unlock($ID); //try to unlock
432 // do the logout stuff
433 auth_logoff();
435 // rebuild info array
436 $INFO = pageinfo();
438 act_redirect($ID,'login');
441 return $act;
445 * Handle 'edit', 'preview'
447 * @author Andreas Gohr <andi@splitbrain.org>
449 function act_edit($act){
450 global $ID;
451 global $INFO;
453 //check if locked by anyone - if not lock for my self
454 $lockedby = checklock($ID);
455 if($lockedby) return 'locked';
457 lock($ID);
458 return $act;
462 * Export a wiki page for various formats
464 * Triggers ACTION_EXPORT_POSTPROCESS
466 * Event data:
467 * data['id'] -- page id
468 * data['mode'] -- requested export mode
469 * data['headers'] -- export headers
470 * data['output'] -- export output
472 * @author Andreas Gohr <andi@splitbrain.org>
473 * @author Michael Klier <chi@chimeric.de>
475 function act_export($act){
476 global $ID;
477 global $REV;
478 global $conf;
479 global $lang;
481 $pre = '';
482 $post = '';
483 $output = '';
484 $headers = array();
486 // search engines: never cache exported docs! (Google only currently)
487 $headers['X-Robots-Tag'] = 'noindex';
489 $mode = substr($act,7);
490 switch($mode) {
491 case 'raw':
492 $headers['Content-Type'] = 'text/plain; charset=utf-8';
493 $headers['Content-Disposition'] = 'attachment; filename='.noNS($ID).'.txt';
494 $output = rawWiki($ID,$REV);
495 break;
496 case 'xhtml':
497 $pre .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' . DOKU_LF;
498 $pre .= ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . DOKU_LF;
499 $pre .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$conf['lang'].'"' . DOKU_LF;
500 $pre .= ' lang="'.$conf['lang'].'" dir="'.$lang['direction'].'">' . DOKU_LF;
501 $pre .= '<head>' . DOKU_LF;
502 $pre .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . DOKU_LF;
503 $pre .= ' <title>'.$ID.'</title>' . DOKU_LF;
505 // get metaheaders
506 ob_start();
507 tpl_metaheaders();
508 $pre .= ob_get_clean();
510 $pre .= '</head>' . DOKU_LF;
511 $pre .= '<body>' . DOKU_LF;
512 $pre .= '<div class="dokuwiki export">' . DOKU_LF;
514 // get toc
515 $pre .= tpl_toc(true);
517 $headers['Content-Type'] = 'text/html; charset=utf-8';
518 $output = p_wiki_xhtml($ID,$REV,false);
520 $post .= '</div>' . DOKU_LF;
521 $post .= '</body>' . DOKU_LF;
522 $post .= '</html>' . DOKU_LF;
523 break;
524 case 'xhtmlbody':
525 $headers['Content-Type'] = 'text/html; charset=utf-8';
526 $output = p_wiki_xhtml($ID,$REV,false);
527 break;
528 default:
529 $output = p_cached_output(wikiFN($ID,$REV), $mode);
530 $headers = p_get_metadata($ID,"format $mode");
531 break;
534 // prepare event data
535 $data = array();
536 $data['id'] = $ID;
537 $data['mode'] = $mode;
538 $data['headers'] = $headers;
539 $data['output'] =& $output;
541 trigger_event('ACTION_EXPORT_POSTPROCESS', $data);
543 if(!empty($data['output'])){
544 if(is_array($data['headers'])) foreach($data['headers'] as $key => $val){
545 header("$key: $val");
547 print $pre.$data['output'].$post;
548 exit;
550 return 'show';
554 * Handle page 'subscribe'
556 * Throws exception on error.
558 * @author Adrian Lang <lang@cosmocode.de>
560 function act_subscription($act){
561 global $lang;
562 global $INFO;
563 global $ID;
565 // get and preprocess data.
566 $params = array();
567 foreach(array('target', 'style', 'action') as $param) {
568 if (isset($_REQUEST["sub_$param"])) {
569 $params[$param] = $_REQUEST["sub_$param"];
573 // any action given? if not just return and show the subscription page
574 if(!$params['action']) return $act;
576 // Handle POST data, may throw exception.
577 trigger_event('ACTION_HANDLE_SUBSCRIBE', $params, 'subscription_handle_post');
579 $target = $params['target'];
580 $style = $params['style'];
581 $data = $params['data'];
582 $action = $params['action'];
584 // Perform action.
585 require_once DOKU_INC . 'inc/subscription.php';
586 if (!subscription_set($_SERVER['REMOTE_USER'], $target, $style, $data)) {
587 throw new Exception(sprintf($lang["subscr_{$action}_error"],
588 hsc($INFO['userinfo']['name']),
589 prettyprint_id($target)));
591 msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
592 prettyprint_id($target)), 1);
593 act_redirect($ID, $act);
595 // Assure that we have valid data if act_redirect somehow fails.
596 $INFO['subscribed'] = get_info_subscribed();
597 return 'show';
601 * Validate POST data
603 * Validates POST data for a subscribe or unsubscribe request. This is the
604 * default action for the event ACTION_HANDLE_SUBSCRIBE.
606 * @author Adrian Lang <lang@cosmocode.de>
608 function subscription_handle_post(&$params) {
609 global $INFO;
610 global $lang;
612 // Get and validate parameters.
613 if (!isset($params['target'])) {
614 throw new Exception('no subscription target given');
616 $target = $params['target'];
617 $valid_styles = array('every', 'digest');
618 if (substr($target, -1, 1) === ':') {
619 // Allow “list” subscribe style since the target is a namespace.
620 $valid_styles[] = 'list';
622 $style = valid_input_set('style', $valid_styles, $params,
623 'invalid subscription style given');
624 $action = valid_input_set('action', array('subscribe', 'unsubscribe'),
625 $params, 'invalid subscription action given');
627 // Check other conditions.
628 if ($action === 'subscribe') {
629 if ($INFO['userinfo']['mail'] === '') {
630 throw new Exception($lang['subscr_subscribe_noaddress']);
632 } elseif ($action === 'unsubscribe') {
633 $is = false;
634 foreach($INFO['subscribed'] as $subscr) {
635 if ($subscr['target'] === $target) {
636 $is = true;
639 if ($is === false) {
640 throw new Exception(sprintf($lang['subscr_not_subscribed'],
641 $_SERVER['REMOTE_USER'],
642 prettyprint_id($target)));
644 // subscription_set deletes a subscription if style = null.
645 $style = null;
648 $data = in_array($style, array('list', 'digest')) ? time() : null;
649 $params = compact('target', 'style', 'data', 'action');
652 //Setup VIM: ex: et ts=2 enc=utf-8 :