xref: /dokuwiki/inc/actions.php (revision d00ec4555f81210cd067f98d9bc7cef51f456462)
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