1<?php
2/**
3 * Task Plugin, task component: Handles individual tasks on a wiki page
4 *
5 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author   Esther Brunner <wikidesign@gmail.com>
7 */
8
9class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
10
11    var $my   = NULL;
12    var $task = array();
13
14    function getType() { return 'substition'; }
15    function getSort() { return 305; }
16    function getPType() { return 'block';}
17
18    function connectTo($mode) {
19        $this->Lexer->addSpecialPattern('~~TASK.*?~~', $mode, 'plugin_task_task');
20    }
21
22    function handle($match, $state, $pos, Doku_Handler $handler) {
23        global $ID;
24        global $INFO;
25        global $ACT;
26        global $REV;
27
28        // strip markup and split arguments
29        $match = substr($match, 6, -2);
30        $priority = strspn(strstr($match, '!'), '!');
31        $match = trim($match, ':!');
32        list($user, $date) = explode('?', $match);
33
34        if ($my =& plugin_load('helper', 'task')) {
35            $date = $my->_interpretDate($date);
36
37            $task = array(
38                    'user'     => array('name' => $user),
39                    'date'     => array('due' => $date),
40                    'priority' => $priority
41                    );
42
43            // save task meta file if changes were made
44            // but only for already existing tasks, or when the page is saved
45            // $REV prevents overwriting current task information with old revision ones
46            if(@file_exists(metaFN($ID, '.task')) && $ACT != 'preview' && !$REV) {
47                $current = $my->readTask($ID);
48                if (($current['user']['name'] != $user) || ($current['date']['due'] != $date) || ($current['priority'] != $priority)) {
49                    $my->writeTask($ID, $task);
50                }
51            } elseif ($ACT != 'preview' && !$REV) {
52                $my->writeTask($ID, $task);
53            }
54        }
55        return array($user, $date, $priority);
56    }
57
58    function render($mode, Doku_Renderer $renderer, $data) {
59        global $ID;
60
61        list($user, $date, $priority) = $data;
62
63        // XHTML output
64        if ($mode == 'xhtml') {
65            $renderer->nocache();
66
67            // prepare data
68            $this->_loadHelper();
69
70            $task = array();
71            if(@file_exists(metaFN($ID, '.task'))) {
72                $task = $this->my->readTask($ID);
73            }
74
75            $status = $this->_getStatus($user, $sn);
76            $due = '';
77
78            if ($date && ($sn < 3)) {
79                if ($date + 86400 < time()) $due = 'overdue';
80                elseif ($date < time()) $due = 'due';
81            }
82
83            $class = ' class="vtodo';
84            if ($priority) $class .= ' priority' . $priority;
85            if ($due) {
86                $class .= ' '.$due;
87                $due = ' class="'.$due.'"';
88            }
89
90            $class .= '"';
91
92            // generate output
93            $renderer->doc .= '<div class="vcalendar">'.DOKU_LF
94                            . '<fieldset'.$class.'>'.DOKU_LF
95                            . '<legend>'.$this->_icsDownload().$this->getLang('task').'</legend>'.DOKU_LF
96                            . '<table class="blind">'.DOKU_LF;
97
98            if ($user) {
99                $this->_tablerow('user', $this->_hCalUser($user), $renderer, '', 'organizer');
100            } elseif ($task['user']['name']) {
101                $this->_tablerow('user', $this->_hCalUser($task['user']['name']), $renderer, '', 'organizer');
102            }
103
104            if ($date) {
105                $this->_tablerow('date', $this->_hCalDate($date), $renderer, $due);
106            } elseif ($task['date']['due']) {
107                $this->_tablerow('date', $this->_hCalDate($task['date']['due']), $renderer, $due);
108            }
109
110            // show status update form only to logged in users
111            if(isset($_SERVER['REMOTE_USER'])) {
112                $this->_tablerow('status', $status, $renderer);
113            }
114
115            $renderer->doc .= '</table>'.DOKU_LF;
116            $renderer->doc .= '</fieldset>'.DOKU_LF.
117            '</div>'.DOKU_LF;
118
119            return true;
120
121        // for metadata renderer
122        } elseif ($mode == 'metadata') {
123            return true;
124        }
125
126        return false;
127    }
128
129    /**
130     * Outputs a table row
131     */
132    function _tablerow($header, $data, &$renderer, $trclass = '', $tdclass = '') {
133        if ($tdclass) $tdclass = ' class="'.$tdclass.'"';
134
135        $renderer->doc .= '<tr'.$trclass.'>'.DOKU_LF;
136        $renderer->tableheader_open(1, '');
137        if ($header) $renderer->doc .= hsc($this->getLang($header)).':';
138        $renderer->tableheader_close();
139        $renderer->doc .= '<td'.$tdclass.'>'.$data;
140        $renderer->tablecell_close();
141        $renderer->tablerow_close();
142    }
143
144    /**
145     * Loads the helper plugin and gets task data for current ID
146     */
147    function _loadHelper() {
148        global $ID;
149        $this->my =& plugin_load('helper', 'task');
150        if (!is_object($this->my)) return false;
151        $this->task = $this->my->readTask($ID);
152        return $true;
153    }
154
155    /**
156     * Returns the status cell contents
157     */
158    function _getStatus($user, &$status) {
159        global $INFO;
160
161        $ret = '';
162        $status = $this->task['status'];
163        $responsible = $this->my->_isResponsible($user);
164
165        if ($INFO['perm'] == AUTH_ADMIN) {
166            $ret = $this->_statusMenu(array(-1, 0, 1, 2, 3, 4), $status);
167        } elseif ($responsible) {
168            if ($status < 3) $ret = $this->_statusMenu(array(-1, 0, 1, 2, 3), $status);
169        } else {
170            if ($status == 0) {
171                $ret = $this->_statusMenu(array(0, 1), $status);
172            } elseif ($status == 3) {
173                $ret = $this->_statusMenu(array(2, 3, 4), $status);
174            }
175        }
176
177        if (!$ret && $this->my) $ret = $this->my->statusLabel($status);
178
179        return '<abbr class="status" title="'.$this->my->_vstatus($status).'">'. $ret .'</abbr>';
180    }
181
182    /**
183     * Returns the XHTML for the status drop down list.
184     * Just forwards call to the old or new function.
185     */
186    function _statusMenu($options, $status) {
187        if (class_exists('dokuwiki\Form\Form')) {
188            return $this->_statusMenuNew($options, $status);
189        } else {
190            return $this->_statusMenuOld($options, $status);
191        }
192    }
193
194    /**
195     * Returns the XHTML for the status popup menu.
196     * This is the new version using class dokuwiki\Form\Form.
197     *
198     * @see _statusMenu
199     */
200    function _statusMenuNew($options, $status) {
201        global $ID, $lang;
202
203        $form = new dokuwiki\Form\Form(array('id' => 'task__changetask_form'));
204        $pos = 1;
205
206        $form->addHTML('<div class="no">', $pos++);
207
208        // Set hidden fields
209        $form->setHiddenField ('id', $ID);
210        $form->setHiddenField ('do', 'changetask');
211
212        // Select status from drop down list
213        $dropDownOptions = array();
214        $selected = NULL;
215        $value = 0;
216        foreach ($options as $option) {
217            if ($status == $option) {
218                $selected = $option.' ';
219            }
220            $dropDownOptions [$option.' '] = $this->my->statusLabel($option);
221        }
222        $input = $form->addDropdown('status', $dropDownOptions, NULL, $pos++);
223        $input->val($selected);
224
225        // Add button
226        $form->addButton(NULL, $this->getLang('btn_change'), $pos++);
227
228        $form->addHTML('</div>', $pos++);
229
230        return $form->toHTML();
231    }
232
233    /**
234     * Returns the XHTML for the status popup menu.
235     * Old function generating all HTML on its own.
236     *
237     * @see _statusMenu
238     */
239    function _statusMenuOld($options, $status) {
240        global $ID;
241        global $lang;
242
243        $ret  = '<form id="task__changetask_form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">';
244        $ret .= '<div class="no">';
245        $ret .= '<input type="hidden" name="id" value="'.$ID.'" />';
246        $ret .= '<input type="hidden" name="do" value="changetask" />';
247        $ret .= '<select name="status" size="1" class="edit">';
248
249        foreach ($options as $option) {
250            $ret .= '<option value="'.$option.'"';
251            if ($status == $option) $ret .= ' selected="selected"';
252            $ret .= '>'.$this->my->statusLabel($option).'</option>';
253        }
254
255        $ret .= '</select>';
256        $ret .= '<input class="button" type="submit" value="'.$this->getLang('btn_change').'" />';
257        $ret .= '</div>';
258        $ret .= '</form>'.DOKU_LF;
259
260        return $ret;
261    }
262
263    /**
264     * Returns the download link for the iCal file
265     */
266    function _icsDownload() {
267        global $ID;
268        global $INFO;
269
270        $uid   = hsc($ID.'@'.$_SERVER['SERVER_NAME']);
271        $title = hsc($INFO['meta']['title']);
272        $link  = DOKU_BASE.'lib/plugins/task/ics.php?id='.$ID;
273        $src   = DOKU_BASE.'lib/plugins/task/images/ics.gif';
274
275        $out   = '<a href="'.$link.'" class="uid" title="'.$uid.'">'
276               . '<img src="'.$src.'" class="summary" alt="'.$title.'" title="'.$title.'" width="16" height="16"/>'
277               . '</a> ';
278
279        return $out;
280    }
281
282    /**
283     * Returns the organizer in hCalendar format as hCard
284     */
285    function _hCalUser($user) {
286        return '<span class="vcard"><span class="fn">' . hsc($user) . '</span></span>';
287    }
288
289    /**
290     * Returns the date in hCalendar format
291     */
292    function _hCalDate($date) {
293        global $conf;
294
295        // strip time from preferred date format
296        $onlydate = preg_replace('#%[HIMprRST]|:#', '', ($conf['dformat']));
297
298        return '<abbr class="due" title="'.$this->my->_vdate($date, true).'">' . strftime($onlydate, $date) . '</abbr>';
299    }
300}
301// vim:ts=4:sw=4:et:enc=utf-8:
302