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