source: documentation/trunk/packages/dokuwiki-2011-05-25a/inc/actions.php@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 20.4 KB
Line 
1<?php
2/**
3 * DokuWiki Actions
4 *
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Andreas Gohr <[email protected]>
7 */
8
9if(!defined('DOKU_INC')) die('meh.');
10
11/**
12 * Call the needed action handlers
13 *
14 * @author Andreas Gohr <[email protected]>
15 * @triggers ACTION_ACT_PREPROCESS
16 * @triggers ACTION_HEADERS_SEND
17 */
18function act_dispatch(){
19 global $ACT;
20 global $ID;
21 global $INFO;
22 global $QUERY;
23 global $lang;
24 global $conf;
25
26 $preact = $ACT;
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') {
48 try {
49 $ACT = act_subscription($ACT);
50 } catch (Exception $e) {
51 msg($e->getMessage(), -1);
52 }
53 }
54
55 //display some infos
56 if($ACT == 'check'){
57 check();
58 $ACT = 'show';
59 }
60
61 //check permissions
62 $ACT = act_permcheck($ACT);
63
64 //sitemap
65 if ($ACT == 'sitemap'){
66 $ACT = act_sitemap($ACT);
67 }
68
69 //register
70 if($ACT == 'register' && $_POST['save'] && register()){
71 $ACT = 'login';
72 }
73
74 if ($ACT == 'resendpwd' && act_resendpwd()) {
75 $ACT = 'login';
76 }
77
78 //update user profile
79 if ($ACT == 'profile') {
80 if(!$_SERVER['REMOTE_USER']) {
81 $ACT = 'login';
82 } else {
83 if(updateprofile()) {
84 msg($lang['profchanged'],1);
85 $ACT = 'show';
86 }
87 }
88 }
89
90 //revert
91 if($ACT == 'revert'){
92 if(checkSecurityToken()){
93 $ACT = act_revert($ACT);
94 }else{
95 $ACT = 'show';
96 }
97 }
98
99 //save
100 if($ACT == 'save'){
101 if(checkSecurityToken()){
102 $ACT = act_save($ACT);
103 }else{
104 $ACT = 'show';
105 }
106 }
107
108 //cancel conflicting edit
109 if($ACT == 'cancel')
110 $ACT = 'show';
111
112 //draft deletion
113 if($ACT == 'draftdel')
114 $ACT = act_draftdel($ACT);
115
116 //draft saving on preview
117 if($ACT == 'preview')
118 $ACT = act_draftsave($ACT);
119
120 //edit
121 if(in_array($ACT, array('edit', 'preview', 'recover'))) {
122 $ACT = act_edit($ACT);
123 }else{
124 unlock($ID); //try to unlock
125 }
126
127 //handle export
128 if(substr($ACT,0,7) == 'export_')
129 $ACT = act_export($ACT);
130
131 //handle admin tasks
132 if($ACT == 'admin'){
133 // retrieve admin plugin name from $_REQUEST['page']
134 if (!empty($_REQUEST['page'])) {
135 $pluginlist = plugin_list('admin');
136 if (in_array($_REQUEST['page'], $pluginlist)) {
137 // attempt to load the plugin
138 if ($plugin =& plugin_load('admin',$_REQUEST['page']) !== null){
139 if($plugin->forAdminOnly() && !$INFO['isadmin']){
140 // a manager tried to load a plugin that's for admins only
141 unset($_REQUEST['page']);
142 msg('For admins only',-1);
143 }else{
144 $plugin->handle();
145 }
146 }
147 }
148 }
149 }
150
151 // check permissions again - the action may have changed
152 $ACT = act_permcheck($ACT);
153 } // end event ACTION_ACT_PREPROCESS default action
154 $evt->advise_after();
155 // Make sure plugs can handle 'denied'
156 if($conf['send404'] && $ACT == 'denied') {
157 header('HTTP/1.0 403 Forbidden');
158 }
159 unset($evt);
160
161 // when action 'show', the intial not 'show' and POST, do a redirect
162 if($ACT == 'show' && $preact != 'show' && strtolower($_SERVER['REQUEST_METHOD']) == 'post'){
163 act_redirect($ID,$preact);
164 }
165
166 global $INFO;
167 global $conf;
168 global $license;
169
170 //call template FIXME: all needed vars available?
171 $headers[] = 'Content-Type: text/html; charset=utf-8';
172 trigger_event('ACTION_HEADERS_SEND',$headers,'act_sendheaders');
173
174 include(template('main.php'));
175 // output for the commands is now handled in inc/templates.php
176 // in function tpl_content()
177}
178
179function act_sendheaders($headers) {
180 foreach ($headers as $hdr) header($hdr);
181}
182
183/**
184 * Sanitize the action command
185 *
186 * Add all allowed commands here.
187 *
188 * @author Andreas Gohr <[email protected]>
189 */
190function act_clean($act){
191 global $lang;
192 global $conf;
193 global $INFO;
194
195 // check if the action was given as array key
196 if(is_array($act)){
197 list($act) = array_keys($act);
198 }
199
200 //remove all bad chars
201 $act = strtolower($act);
202 $act = preg_replace('/[^1-9a-z_]+/','',$act);
203
204 if($act == 'export_html') $act = 'export_xhtml';
205 if($act == 'export_htmlbody') $act = 'export_xhtmlbody';
206
207 if($act === '') $act = 'show';
208
209 // check if action is disabled
210 if(!actionOK($act)){
211 msg('Command disabled: '.htmlspecialchars($act),-1);
212 return 'show';
213 }
214
215 //disable all acl related commands if ACL is disabled
216 if(!$conf['useacl'] && in_array($act,array('login','logout','register','admin',
217 'subscribe','unsubscribe','profile','revert',
218 'resendpwd','subscribens','unsubscribens',))){
219 msg('Command unavailable: '.htmlspecialchars($act),-1);
220 return 'show';
221 }
222
223 //is there really a draft?
224 if($act == 'draft' && !file_exists($INFO['draft'])) return 'edit';
225
226 if(!in_array($act,array('login','logout','register','save','cancel','edit','draft',
227 'preview','search','show','check','index','revisions',
228 'diff','recent','backlink','admin','subscribe','revert',
229 'unsubscribe','profile','resendpwd','recover',
230 'draftdel','subscribens','unsubscribens','sitemap')) && substr($act,0,7) != 'export_' ) {
231 msg('Command unknown: '.htmlspecialchars($act),-1);
232 return 'show';
233 }
234 return $act;
235}
236
237/**
238 * Run permissionchecks
239 *
240 * @author Andreas Gohr <[email protected]>
241 */
242function act_permcheck($act){
243 global $INFO;
244 global $conf;
245
246 if(in_array($act,array('save','preview','edit','recover'))){
247 if($INFO['exists']){
248 if($act == 'edit'){
249 //the edit function will check again and do a source show
250 //when no AUTH_EDIT available
251 $permneed = AUTH_READ;
252 }else{
253 $permneed = AUTH_EDIT;
254 }
255 }else{
256 $permneed = AUTH_CREATE;
257 }
258 }elseif(in_array($act,array('login','search','recent','profile','index', 'sitemap'))){
259 $permneed = AUTH_NONE;
260 }elseif($act == 'revert'){
261 $permneed = AUTH_ADMIN;
262 if($INFO['ismanager']) $permneed = AUTH_EDIT;
263 }elseif($act == 'register'){
264 $permneed = AUTH_NONE;
265 }elseif($act == 'resendpwd'){
266 $permneed = AUTH_NONE;
267 }elseif($act == 'admin'){
268 if($INFO['ismanager']){
269 // if the manager has the needed permissions for a certain admin
270 // action is checked later
271 $permneed = AUTH_READ;
272 }else{
273 $permneed = AUTH_ADMIN;
274 }
275 }else{
276 $permneed = AUTH_READ;
277 }
278 if($INFO['perm'] >= $permneed) return $act;
279
280 return 'denied';
281}
282
283/**
284 * Handle 'draftdel'
285 *
286 * Deletes the draft for the current page and user
287 */
288function act_draftdel($act){
289 global $INFO;
290 @unlink($INFO['draft']);
291 $INFO['draft'] = null;
292 return 'show';
293}
294
295/**
296 * Saves a draft on preview
297 *
298 * @todo this currently duplicates code from ajax.php :-/
299 */
300function act_draftsave($act){
301 global $INFO;
302 global $ID;
303 global $conf;
304 if($conf['usedraft'] && $_POST['wikitext']){
305 $draft = array('id' => $ID,
306 'prefix' => substr($_POST['prefix'], 0, -1),
307 'text' => $_POST['wikitext'],
308 'suffix' => $_POST['suffix'],
309 'date' => (int) $_POST['date'],
310 'client' => $INFO['client'],
311 );
312 $cname = getCacheName($draft['client'].$ID,'.draft');
313 if(io_saveFile($cname,serialize($draft))){
314 $INFO['draft'] = $cname;
315 }
316 }
317 return $act;
318}
319
320/**
321 * Handle 'save'
322 *
323 * Checks for spam and conflicts and saves the page.
324 * Does a redirect to show the page afterwards or
325 * returns a new action.
326 *
327 * @author Andreas Gohr <[email protected]>
328 */
329function act_save($act){
330 global $ID;
331 global $DATE;
332 global $PRE;
333 global $TEXT;
334 global $SUF;
335 global $SUM;
336 global $lang;
337 global $INFO;
338
339 //spam check
340 if(checkwordblock()) {
341 msg($lang['wordblock'], -1);
342 return 'edit';
343 }
344 //conflict check
345 if($DATE != 0 && $INFO['meta']['date']['modified'] > $DATE )
346 return 'conflict';
347
348 //save it
349 saveWikiText($ID,con($PRE,$TEXT,$SUF,1),$SUM,$_REQUEST['minor']); //use pretty mode for con
350 //unlock it
351 unlock($ID);
352
353 //delete draft
354 act_draftdel($act);
355 session_write_close();
356
357 // when done, show page
358 return 'show';
359}
360
361/**
362 * Revert to a certain revision
363 *
364 * @author Andreas Gohr <[email protected]>
365 */
366function act_revert($act){
367 global $ID;
368 global $REV;
369 global $lang;
370 // FIXME $INFO['writable'] currently refers to the attic version
371 // global $INFO;
372 // if (!$INFO['writable']) {
373 // return 'show';
374 // }
375
376 // when no revision is given, delete current one
377 // FIXME this feature is not exposed in the GUI currently
378 $text = '';
379 $sum = $lang['deleted'];
380 if($REV){
381 $text = rawWiki($ID,$REV);
382 if(!$text) return 'show'; //something went wrong
383 $sum = $lang['restored'];
384 }
385
386 // spam check
387
388 if (checkwordblock($text)) {
389 msg($lang['wordblock'], -1);
390 return 'edit';
391 }
392
393 saveWikiText($ID,$text,$sum,false);
394 msg($sum,1);
395
396 //delete any draft
397 act_draftdel($act);
398 session_write_close();
399
400 // when done, show current page
401 $_SERVER['REQUEST_METHOD'] = 'post'; //should force a redirect
402 $REV = '';
403 return 'show';
404}
405
406/**
407 * Do a redirect after receiving post data
408 *
409 * Tries to add the section id as hash mark after section editing
410 */
411function act_redirect($id,$preact){
412 global $PRE;
413 global $TEXT;
414
415 $opts = array(
416 'id' => $id,
417 'preact' => $preact
418 );
419 //get section name when coming from section edit
420 if($PRE && preg_match('/^\s*==+([^=\n]+)/',$TEXT,$match)){
421 $check = false; //Byref
422 $opts['fragment'] = sectionID($match[0], $check);
423 }
424
425 trigger_event('ACTION_SHOW_REDIRECT',$opts,'act_redirect_execute');
426}
427
428function act_redirect_execute($opts){
429 $go = wl($opts['id'],'',true);
430 if(isset($opts['fragment'])) $go .= '#'.$opts['fragment'];
431
432 //show it
433 send_redirect($go);
434}
435
436/**
437 * Handle 'login', 'logout'
438 *
439 * @author Andreas Gohr <[email protected]>
440 */
441function act_auth($act){
442 global $ID;
443 global $INFO;
444
445 //already logged in?
446 if(isset($_SERVER['REMOTE_USER']) && $act=='login'){
447 return 'show';
448 }
449
450 //handle logout
451 if($act=='logout'){
452 $lockedby = checklock($ID); //page still locked?
453 if($lockedby == $_SERVER['REMOTE_USER'])
454 unlock($ID); //try to unlock
455
456 // do the logout stuff
457 auth_logoff();
458
459 // rebuild info array
460 $INFO = pageinfo();
461
462 act_redirect($ID,'login');
463 }
464
465 return $act;
466}
467
468/**
469 * Handle 'edit', 'preview', 'recover'
470 *
471 * @author Andreas Gohr <[email protected]>
472 */
473function act_edit($act){
474 global $ID;
475 global $INFO;
476
477 global $TEXT;
478 global $RANGE;
479 global $PRE;
480 global $SUF;
481 global $REV;
482 global $SUM;
483 global $lang;
484 global $DATE;
485
486 if (!isset($TEXT)) {
487 if ($INFO['exists']) {
488 if ($RANGE) {
489 list($PRE,$TEXT,$SUF) = rawWikiSlices($RANGE,$ID,$REV);
490 } else {
491 $TEXT = rawWiki($ID,$REV);
492 }
493 } else {
494 $TEXT = pageTemplate($ID);
495 }
496 }
497
498 //set summary default
499 if(!$SUM){
500 if($REV){
501 $SUM = $lang['restored'];
502 }elseif(!$INFO['exists']){
503 $SUM = $lang['created'];
504 }
505 }
506
507 // Use the date of the newest revision, not of the revision we edit
508 // This is used for conflict detection
509 if(!$DATE) $DATE = $INFO['meta']['date']['modified'];
510
511 //check if locked by anyone - if not lock for my self
512 //do not lock when the user can't edit anyway
513 if ($INFO['writable']) {
514 $lockedby = checklock($ID);
515 if($lockedby) return 'locked';
516
517 lock($ID);
518 }
519
520 return $act;
521}
522
523/**
524 * Export a wiki page for various formats
525 *
526 * Triggers ACTION_EXPORT_POSTPROCESS
527 *
528 * Event data:
529 * data['id'] -- page id
530 * data['mode'] -- requested export mode
531 * data['headers'] -- export headers
532 * data['output'] -- export output
533 *
534 * @author Andreas Gohr <[email protected]>
535 * @author Michael Klier <[email protected]>
536 */
537function act_export($act){
538 global $ID;
539 global $REV;
540 global $conf;
541 global $lang;
542
543 $pre = '';
544 $post = '';
545 $output = '';
546 $headers = array();
547
548 // search engines: never cache exported docs! (Google only currently)
549 $headers['X-Robots-Tag'] = 'noindex';
550
551 $mode = substr($act,7);
552 switch($mode) {
553 case 'raw':
554 $headers['Content-Type'] = 'text/plain; charset=utf-8';
555 $headers['Content-Disposition'] = 'attachment; filename='.noNS($ID).'.txt';
556 $output = rawWiki($ID,$REV);
557 break;
558 case 'xhtml':
559 $pre .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' . DOKU_LF;
560 $pre .= ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . DOKU_LF;
561 $pre .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$conf['lang'].'"' . DOKU_LF;
562 $pre .= ' lang="'.$conf['lang'].'" dir="'.$lang['direction'].'">' . DOKU_LF;
563 $pre .= '<head>' . DOKU_LF;
564 $pre .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . DOKU_LF;
565 $pre .= ' <title>'.$ID.'</title>' . DOKU_LF;
566
567 // get metaheaders
568 ob_start();
569 tpl_metaheaders();
570 $pre .= ob_get_clean();
571
572 $pre .= '</head>' . DOKU_LF;
573 $pre .= '<body>' . DOKU_LF;
574 $pre .= '<div class="dokuwiki export">' . DOKU_LF;
575
576 // get toc
577 $pre .= tpl_toc(true);
578
579 $headers['Content-Type'] = 'text/html; charset=utf-8';
580 $output = p_wiki_xhtml($ID,$REV,false);
581
582 $post .= '</div>' . DOKU_LF;
583 $post .= '</body>' . DOKU_LF;
584 $post .= '</html>' . DOKU_LF;
585 break;
586 case 'xhtmlbody':
587 $headers['Content-Type'] = 'text/html; charset=utf-8';
588 $output = p_wiki_xhtml($ID,$REV,false);
589 break;
590 default:
591 $output = p_cached_output(wikiFN($ID,$REV), $mode);
592 $headers = p_get_metadata($ID,"format $mode");
593 break;
594 }
595
596 // prepare event data
597 $data = array();
598 $data['id'] = $ID;
599 $data['mode'] = $mode;
600 $data['headers'] = $headers;
601 $data['output'] =& $output;
602
603 trigger_event('ACTION_EXPORT_POSTPROCESS', $data);
604
605 if(!empty($data['output'])){
606 if(is_array($data['headers'])) foreach($data['headers'] as $key => $val){
607 header("$key: $val");
608 }
609 print $pre.$data['output'].$post;
610 exit;
611 }
612 return 'show';
613}
614
615/**
616 * Handle sitemap delivery
617 *
618 * @author Michael Hamann <[email protected]>
619 */
620function act_sitemap($act) {
621 global $conf;
622
623 if ($conf['sitemap'] < 1 || !is_numeric($conf['sitemap'])) {
624 header("HTTP/1.0 404 Not Found");
625 print "Sitemap generation is disabled.";
626 exit;
627 }
628
629 $sitemap = Sitemapper::getFilePath();
630 if(strrchr($sitemap, '.') === '.gz'){
631 $mime = 'application/x-gzip';
632 }else{
633 $mime = 'application/xml; charset=utf-8';
634 }
635
636 // Check if sitemap file exists, otherwise create it
637 if (!is_readable($sitemap)) {
638 Sitemapper::generate();
639 }
640
641 if (is_readable($sitemap)) {
642 // Send headers
643 header('Content-Type: '.$mime);
644 header('Content-Disposition: attachment; filename='.basename($sitemap));
645
646 http_conditionalRequest(filemtime($sitemap));
647
648 // Send file
649 //use x-sendfile header to pass the delivery to compatible webservers
650 if (http_sendfile($sitemap)) exit;
651
652 readfile($sitemap);
653 exit;
654 }
655
656 header("HTTP/1.0 500 Internal Server Error");
657 print "Could not read the sitemap file - bad permissions?";
658 exit;
659}
660
661/**
662 * Handle page 'subscribe'
663 *
664 * Throws exception on error.
665 *
666 * @author Adrian Lang <[email protected]>
667 */
668function act_subscription($act){
669 global $lang;
670 global $INFO;
671 global $ID;
672
673 // subcriptions work for logged in users only
674 if(!$_SERVER['REMOTE_USER']) return 'show';
675
676 // get and preprocess data.
677 $params = array();
678 foreach(array('target', 'style', 'action') as $param) {
679 if (isset($_REQUEST["sub_$param"])) {
680 $params[$param] = $_REQUEST["sub_$param"];
681 }
682 }
683
684 // any action given? if not just return and show the subscription page
685 if(!$params['action'] || !checkSecurityToken()) return $act;
686
687 // Handle POST data, may throw exception.
688 trigger_event('ACTION_HANDLE_SUBSCRIBE', $params, 'subscription_handle_post');
689
690 $target = $params['target'];
691 $style = $params['style'];
692 $data = $params['data'];
693 $action = $params['action'];
694
695 // Perform action.
696 if (!subscription_set($_SERVER['REMOTE_USER'], $target, $style, $data)) {
697 throw new Exception(sprintf($lang["subscr_{$action}_error"],
698 hsc($INFO['userinfo']['name']),
699 prettyprint_id($target)));
700 }
701 msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
702 prettyprint_id($target)), 1);
703 act_redirect($ID, $act);
704
705 // Assure that we have valid data if act_redirect somehow fails.
706 $INFO['subscribed'] = get_info_subscribed();
707 return 'show';
708}
709
710/**
711 * Validate POST data
712 *
713 * Validates POST data for a subscribe or unsubscribe request. This is the
714 * default action for the event ACTION_HANDLE_SUBSCRIBE.
715 *
716 * @author Adrian Lang <[email protected]>
717 */
718function subscription_handle_post(&$params) {
719 global $INFO;
720 global $lang;
721
722 // Get and validate parameters.
723 if (!isset($params['target'])) {
724 throw new Exception('no subscription target given');
725 }
726 $target = $params['target'];
727 $valid_styles = array('every', 'digest');
728 if (substr($target, -1, 1) === ':') {
729 // Allow “list” subscribe style since the target is a namespace.
730 $valid_styles[] = 'list';
731 }
732 $style = valid_input_set('style', $valid_styles, $params,
733 'invalid subscription style given');
734 $action = valid_input_set('action', array('subscribe', 'unsubscribe'),
735 $params, 'invalid subscription action given');
736
737 // Check other conditions.
738 if ($action === 'subscribe') {
739 if ($INFO['userinfo']['mail'] === '') {
740 throw new Exception($lang['subscr_subscribe_noaddress']);
741 }
742 } elseif ($action === 'unsubscribe') {
743 $is = false;
744 foreach($INFO['subscribed'] as $subscr) {
745 if ($subscr['target'] === $target) {
746 $is = true;
747 }
748 }
749 if ($is === false) {
750 throw new Exception(sprintf($lang['subscr_not_subscribed'],
751 $_SERVER['REMOTE_USER'],
752 prettyprint_id($target)));
753 }
754 // subscription_set deletes a subscription if style = null.
755 $style = null;
756 }
757
758 $data = in_array($style, array('list', 'digest')) ? time() : null;
759 $params = compact('target', 'style', 'data', 'action');
760}
761
762//Setup VIM: ex: et ts=2 :
Note: See TracBrowser for help on using the repository browser.