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