xref: /plugin/todo/action.php (revision b2bf3541cf89d3113065ec0f6619cacbcbd6f0aa)
1<?php
2
3/**
4 * ToDo Action Plugin: Inserts button for ToDo plugin into toolbar
5 *
6 * Original Example: http://www.dokuwiki.org/devel:action_plugins
7 * @author     Babbage <babbage@digitalbrink.com>
8 * @date 20130405 Leo Eibler <dokuwiki@sprossenwanne.at> \n
9 *                replace old sack() method with new jQuery method and use post instead of get \n
10 * @date 20130408 Leo Eibler <dokuwiki@sprossenwanne.at> \n
11 *                remove getInfo() call because it's done by plugin.info.txt (since dokuwiki 2009-12-25 Lemming)
12 */
13
14if(!defined('DOKU_INC')) die();
15/**
16 * Class action_plugin_todo registers actions
17 */
18class action_plugin_todo extends DokuWiki_Action_Plugin {
19
20    /**
21     * Register the eventhandlers
22     */
23    public function register(Doku_Event_Handler $controller) {
24        $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'insert_button', array());
25        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_ajax_call', array());
26    }
27
28    /**
29     * Inserts the toolbar button
30     */
31    public function insert_button(&$event, $param) {
32        $event->data[] = array(
33            'type' => 'format',
34            'title' => $this->getLang('qb_todobutton'),
35            'icon' => '../../plugins/todo/todo.png',
36            'key' => 't',
37            'open' => '<todo>',
38            'close' => '</todo>',
39            'block' => false,
40        );
41    }
42
43    /**
44     * Handles ajax requests for to do plugin
45     *
46     * @brief This method is called by ajax if the user clicks on the to-do checkbox or the to-do text.
47     * It sets the to-do state to completed or reset it to open.
48     *
49     * POST Parameters:
50     *   index    int the position of the occurrence of the input element (starting with 0 for first element/to-do)
51     *   checked    int should the to-do set to completed (1) or to open (0)
52     *   path    string id/path/name of the page
53     *
54     * @date 20140317 Leo Eibler <dokuwiki@sprossenwanne.at> \n
55     *                use todo content as change description \n
56     * @date 20131008 Gerrit Uitslag <klapinklapin@gmail.com> \n
57     *                move ajax.php to action.php, added lock and conflict checks and improved saving
58     * @date 20130405 Leo Eibler <dokuwiki@sprossenwanne.at> \n
59     *                replace old sack() method with new jQuery method and use post instead of get \n
60     * @date 20130407 Leo Eibler <dokuwiki@sprossenwanne.at> \n
61     *                add user assignment for todos \n
62     * @date 20130408 Christian Marg <marg@rz.tu-clausthal.de> \n
63     *                change only the clicked to-do item instead of all items with the same text \n
64     *                origVal is not used anymore, we use the index (occurrence) of input element \n
65     * @date 20130408 Leo Eibler <dokuwiki@sprossenwanne.at> \n
66     *                migrate changes made by Christian Marg to current version of plugin \n
67     *
68     *
69     * @param Doku_Event $event
70     * @param mixed $param not defined
71     */
72    public function _ajax_call(&$event, $param) {
73        global $ID, $conf, $lang;
74
75        if($event->data !== 'plugin_todo') {
76            return;
77        }
78        //no other ajax call handlers needed
79        $event->stopPropagation();
80        $event->preventDefault();
81
82        #Variables
83        // by einhirn <marg@rz.tu-clausthal.de> determine checkbox index by using class 'todocheckbox'
84
85        if(isset($_REQUEST['index'], $_REQUEST['checked'], $_REQUEST['pageid'])) {
86            // index = position of occurrence of <input> element (starting with 0 for first element)
87            $index = (int) $_REQUEST['index'];
88            // checked = flag if input is checked means to do is complete (1) or not (0)
89            $checked = (boolean) urldecode($_REQUEST['checked']);
90            // path = page ID
91            $ID = cleanID(urldecode($_REQUEST['pageid']));
92        } else {
93            return;
94        }
95
96        $date = 0;
97        if(isset($_REQUEST['date'])) $date = (int) $_REQUEST['date'];
98
99        $INFO = pageinfo();
100
101        #Determine Permissions
102        if(auth_quickaclcheck($ID) < AUTH_EDIT) {
103            echo "You do not have permission to edit this file.\nAccess was denied.";
104            return;
105        }
106        // Check, if page is locked
107        if(checklock($ID)) {
108            $locktime = filemtime(wikiLockFN($ID));
109            $expire = dformat($locktime + $conf['locktime']);
110            $min = round(($conf['locktime'] - (time() - $locktime)) / 60);
111
112            $msg = $this->getLang('lockedpage').'
113'.$lang['lockedby'] . ': ' . editorinfo($INFO['locked']) . '
114' . $lang['lockexpire'] . ': ' . $expire . ' (' . $min . ' min)';
115            $this->printJson(array('message' => $msg));
116            return;
117        }
118
119        //conflict check
120        if($date != 0 && $INFO['meta']['date']['modified'] > $date) {
121            $this->printJson(array('message' => $this->getLang('refreshpage')));
122            return;
123        }
124
125        #Retrieve Page Contents
126        $wikitext = rawWiki($ID);
127
128        #Determine position of tag
129        if($index >= 0) {
130            $index++;
131            // index is only set on the current page with the todos
132            // the occurances are counted, untill the index-th input is reached which is updated
133            $todoTagStartPos = $this->_strnpos($wikitext, '<todo', $index);
134            $todoTagEndPos = strpos($wikitext, '>', $todoTagStartPos) + 1;
135
136            if($todoTagEndPos > $todoTagStartPos) {
137				// @date 20140714 le add todo text to minorchange
138				$todoTextEndPos = strpos( $wikitext, '</todo', $todoTagEndPos );
139				$todoText = substr( $wikitext, $todoTagEndPos, $todoTextEndPos-$todoTagEndPos );
140                // update text
141                $oldTag = substr($wikitext, $todoTagStartPos, ($todoTagEndPos - $todoTagStartPos));
142                $newTag = $this->_buildTodoTag($oldTag, $checked);
143                $wikitext = substr_replace($wikitext, $newTag, $todoTagStartPos, ($todoTagEndPos - $todoTagStartPos));
144
145                // save Update (Minor)
146                lock($ID);
147				// @date 20140714 le add todo text to minorchange, use different message for checked or unchecked
148                saveWikiText($ID, $wikitext, $this->getLang($checked?'checkboxchange_on':'checkboxchange_off').': '.$todoText, $minoredit = true);
149                unlock($ID);
150
151                $return = array(
152                    'date' => @filemtime(wikiFN($ID)),
153                    'succeed' => true
154                );
155                $this->printJson($return);
156            }
157        }
158    }
159
160    /**
161     * Encode and print an arbitrary variable into JSON format
162     *
163     * @param mixed $return
164     */
165    private function printJson($return) {
166        $json = new JSON();
167        echo $json->encode($return);
168    }
169
170    /**
171     * @brief gets current to-do tag and returns a new one depending on checked
172     * @param $todoTag    string current to-do tag e.g. <todo @user>
173     * @param $checked    int check flag (todo completed=1, todo uncompleted=0)
174     * @return string new to-do completed or uncompleted tag e.g. <todo @user #>
175     */
176    private function _buildTodoTag($todoTag, $checked) {
177        if($checked == 1) {
178            $newTag = preg_replace('/[#\s]*>/', ' #>', $todoTag);
179        } else {
180            $newTag = preg_replace('/[#\s]*>/', '>', $todoTag);
181        }
182
183        return $newTag;
184    }
185
186    /**
187     * Find position of $occurance-th $needle in haystack
188     */
189    private function _strnpos($haystack, $needle, $occurance, $pos = 0) {
190        for($i = 1; $i <= $occurance; $i++) {
191            $pos = strpos($haystack, $needle, $pos) + 1;
192        }
193        return $pos - 1;
194    }
195}
196