1<?php
2/**
3 * DokuWiki Plugin saveandedit (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Michael Hamann <michael@content-space.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
15
16class action_plugin_saveandedit extends DokuWiki_Action_Plugin {
17
18    /** The action that has been handled before the current action */
19    private $previous_act = null;
20
21    public function register(Doku_Event_Handler $controller) {
22       // try to register our handler at a late position so e.g. the edittable plugin has a possibility to process its data
23       $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_action_act_preprocess', null, 1000);
24       $controller->register_hook('HTML_EDITFORM_OUTPUT', 'BEFORE', $this, 'handle_html_editform_output');
25    }
26
27    /**
28     * Clean the environment after saving for the next edit.
29     */
30    private function clean_after_save() {
31        global $ID, $INFO, $REV, $RANGE, $TEXT, $PRE, $SUF;
32        $REV = ''; // now we are working on the current revision
33        // Handle section edits
34        if ($PRE || $SUF) {
35            // $from and $to are 1-based indexes of the actually edited content
36            $from = strlen($PRE) + 1;
37            $to = $from + strlen($TEXT);
38            $RANGE = $from . '-' . $to;
39        }
40        // Ensure the current text is loaded again from the file
41        unset($GLOBALS['TEXT'], $GLOBALS['PRE'], $GLOBALS['SUF']);
42        // Reset the date of the last modification to avoid conflict messages
43        unset($GLOBALS['DATE']);
44        // Reset the change check
45        unset($_REQUEST['changecheck']);
46        // Force rendering of the metadata in order to ensure metadata is correct
47        p_set_metadata($ID, array(), true);
48        $INFO = pageinfo(); // reset pageinfo to new data (e.g. if the page exists)
49    }
50
51    public function handle_action_act_preprocess(Doku_Event $event, $param) {
52        global $INPUT;
53
54        if (!$INPUT->bool('saveandedit')) {
55            return;
56        }
57
58        // check if the action was given as array key
59        if(is_array($event->data)){
60            list($act) = array_keys($event->data);
61        } else {
62            $act = $event->data;
63        }
64
65        // Greebo and above
66        if (class_exists('\\dokuwiki\\ActionRouter', false)) {
67	    /*
68	       The ACTION_ACT_PREPROCESS event is triggered several
69	       times, once for every action. After the save has been
70	       executed, the next event is 'draftdel'. We intercept
71	       the 'draftdel' action and replace it by 'edit'. As this
72	       is a logical place where other plugins may want to save
73	       data (e.g. blogtng), we try to be handled relatively
74	       late. To fix plugins that want to handle the 'edit'
75	       action, we trigger a new event for the 'edit' action.
76	    */
77            if ($this->previous_act === 'save' && $act === 'draftdel') {
78                $this->clean_after_save();
79                $event->data = 'edit';
80
81		/*
82		   The edittable plugin would restore $TEXT from the
83		   edittable_data post data on each
84		   ACTION_ACT_PREPROCESS call. This breaks the
85		   automatic restore of the prefix and suffix
86		   data. Stop it from doing this by unsetting its
87		   data.
88		*/
89		$INPUT->post->remove('edittable_data');
90
91		/*
92		   Stop propagation of the event. All subsequent event
93		   handlers will be called anyway again by the event
94		   triggered below.
95		*/
96		$event->stopPropagation();
97
98		/*
99		   Trigger a new event for the edit action.
100		   This ensures that all event handlers for the edit
101		   action are called.  However, we only advise the
102		   before handlers and re-use the default action and
103		   the after handling of the original event.
104		*/
105		$new_evt = new \Doku_Event('ACTION_ACT_PREPROCESS', $event->data);
106		// prevent the default action of the original event
107		if (!$new_evt->advise_before()) {
108		    $event->preventDefault();
109		}
110
111            }
112
113            $this->previous_act = $act;
114
115        // pre-Greebo compatibility
116        } else if ($act === 'save' && actionOK($act) && act_permcheck($act) == 'save' && checkSecurityToken()) {
117            $event->data = act_save($act);
118
119            if ($event->data === 'show') {
120                $event->data = 'edit';
121                $this->clean_after_save();
122            } elseif ($event->data === 'conflict') {
123                // DokuWiki won't accept 'conflict' as action here.
124                // Just execute save again, the conflict will be detected again
125                $event->data = 'save';
126            }
127        }
128    }
129
130    public function handle_html_editform_output(Doku_Event $event, $param) {
131        global $INPUT;
132
133        $pos = $event->data->findElementByAttribute('type','submit');
134        if(!$pos) return; // no submit button found, source view
135        $pos -= 1;
136        $event->data->insertElement($pos++, form_makeOpenTag('div', array()));
137        $attrs = $INPUT->bool('saveandedit') ? array('checked' => 'checked') : array();
138        $event->data->insertElement($pos++, form_makeCheckboxField('saveandedit', '1', $this->getLang('btn_saveandedit'), '', '', $attrs));
139        $event->data->insertElement($pos++, form_makeCloseTag('div'));
140    }
141}
142
143// vim:ts=4:sw=4:et:
144