1 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/config.inc.php.dist roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/config.inc.php.dist |
2 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/config.inc.php.dist 1970-01-01 01:00:00.000000000 +0100 |
3 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/config.inc.php.dist 2013-11-24 00:38:51.000000000 +0100 |
4 |
@@ -0,0 +1,4 @@ |
5 |
+<?php |
6 |
+ |
7 |
+$rcmail_config['tasklist_driver'] = 'kolab'; |
8 |
+ |
9 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/database/SQL/mysql.sql roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/database/SQL/mysql.sql |
10 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/database/SQL/mysql.sql 1970-01-01 01:00:00.000000000 +0100 |
11 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/database/SQL/mysql.sql 2013-11-24 00:38:51.000000000 +0100 |
12 |
@@ -0,0 +1,49 @@ |
13 |
+/** |
14 |
+ * Roundcube Tasklist plugin database |
15 |
+ * |
16 |
+ * @version @package_version@ |
17 |
+ * @author Thomas Bruederli |
18 |
+ * @licence GNU AGPL |
19 |
+ * @copyright (C) 2012, Kolab Systems AG |
20 |
+ */ |
21 |
+ |
22 |
+CREATE TABLE `tasklists` ( |
23 |
+ `tasklist_id` int(10) unsigned NOT NULL AUTO_INCREMENT, |
24 |
+ `user_id` int(10) unsigned NOT NULL, |
25 |
+ `name` varchar(255) NOT NULL, |
26 |
+ `color` varchar(8) NOT NULL, |
27 |
+ `showalarms` tinyint(2) unsigned NOT NULL DEFAULT '0', |
28 |
+ PRIMARY KEY (`tasklist_id`), |
29 |
+ KEY `user_id` (`user_id`), |
30 |
+ CONSTRAINT `fk_tasklist_user_id` FOREIGN KEY (`user_id`) |
31 |
+ REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE |
32 |
+) /*!40000 ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci */; |
33 |
+ |
34 |
+CREATE TABLE `tasks` ( |
35 |
+ `task_id` int(10) unsigned NOT NULL AUTO_INCREMENT, |
36 |
+ `tasklist_id` int(10) unsigned NOT NULL, |
37 |
+ `parent_id` int(10) unsigned DEFAULT NULL, |
38 |
+ `uid` varchar(255) NOT NULL, |
39 |
+ `created` datetime NOT NULL, |
40 |
+ `changed` datetime NOT NULL, |
41 |
+ `del` tinyint(1) unsigned NOT NULL DEFAULT '0', |
42 |
+ `title` varchar(255) NOT NULL, |
43 |
+ `description` text, |
44 |
+ `tags` text, |
45 |
+ `date` varchar(10) DEFAULT NULL, |
46 |
+ `time` varchar(5) DEFAULT NULL, |
47 |
+ `startdate` varchar(10) DEFAULT NULL, |
48 |
+ `starttime` varchar(5) DEFAULT NULL, |
49 |
+ `flagged` tinyint(4) NOT NULL DEFAULT '0', |
50 |
+ `complete` float NOT NULL DEFAULT '0', |
51 |
+ `alarms` varchar(255) DEFAULT NULL, |
52 |
+ `recurrence` varchar(255) DEFAULT NULL, |
53 |
+ `organizer` varchar(255) DEFAULT NULL, |
54 |
+ `attendees` text, |
55 |
+ `notify` datetime DEFAULT NULL, |
56 |
+ PRIMARY KEY (`task_id`), |
57 |
+ KEY `tasklisting` (`tasklist_id`,`del`,`date`), |
58 |
+ KEY `uid` (`uid`), |
59 |
+ CONSTRAINT `fk_tasks_tasklist_id` FOREIGN KEY (`tasklist_id`) |
60 |
+ REFERENCES `tasklists`(`tasklist_id`) ON DELETE CASCADE ON UPDATE CASCADE |
61 |
+) /*!40000 ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci */; |
62 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/database/tasklist_database_driver.php roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/database/tasklist_database_driver.php |
63 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/database/tasklist_database_driver.php 1970-01-01 01:00:00.000000000 +0100 |
64 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/database/tasklist_database_driver.php 2013-11-24 00:38:51.000000000 +0100 |
65 |
@@ -0,0 +1,666 @@ |
66 |
+<?php |
67 |
+ |
68 |
+/** |
69 |
+ * Database driver for the Tasklist plugin |
70 |
+ * |
71 |
+ * @version @package_version@ |
72 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
73 |
+ * |
74 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
75 |
+ * |
76 |
+ * This program is free software: you can redistribute it and/or modify |
77 |
+ * it under the terms of the GNU Affero General Public License as |
78 |
+ * published by the Free Software Foundation, either version 3 of the |
79 |
+ * License, or (at your option) any later version. |
80 |
+ * |
81 |
+ * This program is distributed in the hope that it will be useful, |
82 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
83 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
84 |
+ * GNU Affero General Public License for more details. |
85 |
+ * |
86 |
+ * You should have received a copy of the GNU Affero General Public License |
87 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
88 |
+ */ |
89 |
+ |
90 |
+class tasklist_database_driver extends tasklist_driver |
91 |
+{ |
92 |
+ public $undelete = true; // yes, we can |
93 |
+ public $sortable = false; |
94 |
+ public $alarm_types = array('DISPLAY','EMAIL'); |
95 |
+ |
96 |
+ private $rc; |
97 |
+ private $plugin; |
98 |
+ private $lists = array(); |
99 |
+ private $list_ids = ''; |
100 |
+ |
101 |
+ private $db_tasks = 'tasks'; |
102 |
+ private $db_lists = 'tasklists'; |
103 |
+ private $sequence_tasks = 'task_ids'; |
104 |
+ private $sequence_lists = 'tasklist_ids'; |
105 |
+ |
106 |
+ |
107 |
+ /** |
108 |
+ * Default constructor |
109 |
+ */ |
110 |
+ public function __construct($plugin) |
111 |
+ { |
112 |
+ $this->rc = $plugin->rc; |
113 |
+ $this->plugin = $plugin; |
114 |
+ |
115 |
+ // read database config |
116 |
+ $this->db_lists = $this->rc->config->get('db_table_lists', $this->db_lists); |
117 |
+ $this->db_tasks = $this->rc->config->get('db_table_tasks', $this->db_tasks); |
118 |
+ $this->sequence_lists = $this->rc->config->get('db_sequence_lists', $this->sequence_lists); |
119 |
+ $this->sequence_tasks = $this->rc->config->get('db_sequence_tasks', $this->sequence_tasks); |
120 |
+ |
121 |
+ $this->_read_lists(); |
122 |
+ } |
123 |
+ |
124 |
+ /** |
125 |
+ * Read available calendars for the current user and store them internally |
126 |
+ */ |
127 |
+ private function _read_lists() |
128 |
+ { |
129 |
+ $hidden = array_filter(explode(',', $this->rc->config->get('hidden_tasklists', ''))); |
130 |
+ |
131 |
+ if (!empty($this->rc->user->ID)) { |
132 |
+ $list_ids = array(); |
133 |
+ $result = $this->rc->db->query( |
134 |
+ "SELECT *, tasklist_id AS id FROM " . $this->db_lists . " |
135 |
+ WHERE user_id=? |
136 |
+ ORDER BY CASE WHEN name='INBOX' THEN 0 ELSE 1 END, name", |
137 |
+ $this->rc->user->ID |
138 |
+ ); |
139 |
+ |
140 |
+ while ($result && ($arr = $this->rc->db->fetch_assoc($result))) { |
141 |
+ $arr['showalarms'] = intval($arr['showalarms']); |
142 |
+ $arr['active'] = !in_array($arr['id'], $hidden); |
143 |
+ $arr['name'] = html::quote($arr['name']); |
144 |
+ $arr['editable'] = true; |
145 |
+ $this->lists[$arr['id']] = $arr; |
146 |
+ $list_ids[] = $this->rc->db->quote($arr['id']); |
147 |
+ } |
148 |
+ $this->list_ids = join(',', $list_ids); |
149 |
+ } |
150 |
+ } |
151 |
+ |
152 |
+ /** |
153 |
+ * Get a list of available tasks lists from this source |
154 |
+ */ |
155 |
+ public function get_lists() |
156 |
+ { |
157 |
+ // attempt to create a default list for this user |
158 |
+ if (empty($this->lists)) { |
159 |
+ if ($this->create_list(array('name' => 'Default', 'color' => '000000'))) |
160 |
+ $this->_read_lists(); |
161 |
+ } |
162 |
+ |
163 |
+ return $this->lists; |
164 |
+ } |
165 |
+ |
166 |
+ /** |
167 |
+ * Create a new list assigned to the current user |
168 |
+ * |
169 |
+ * @param array Hash array with list properties |
170 |
+ * @return mixed ID of the new list on success, False on error |
171 |
+ * @see tasklist_driver::create_list() |
172 |
+ */ |
173 |
+ public function create_list($prop) |
174 |
+ { |
175 |
+ $result = $this->rc->db->query( |
176 |
+ "INSERT INTO " . $this->db_lists . " |
177 |
+ (user_id, name, color, showalarms) |
178 |
+ VALUES (?, ?, ?, ?)", |
179 |
+ $this->rc->user->ID, |
180 |
+ $prop['name'], |
181 |
+ $prop['color'], |
182 |
+ $prop['showalarms']?1:0 |
183 |
+ ); |
184 |
+ |
185 |
+ if ($result) |
186 |
+ return $this->rc->db->insert_id($this->sequence_lists); |
187 |
+ |
188 |
+ return false; |
189 |
+ } |
190 |
+ |
191 |
+ /** |
192 |
+ * Update properties of an existing tasklist |
193 |
+ * |
194 |
+ * @param array Hash array with list properties |
195 |
+ * @return boolean True on success, Fales on failure |
196 |
+ * @see tasklist_driver::edit_list() |
197 |
+ */ |
198 |
+ public function edit_list($prop) |
199 |
+ { |
200 |
+ $query = $this->rc->db->query( |
201 |
+ "UPDATE " . $this->db_lists . " |
202 |
+ SET name=?, color=?, showalarms=? |
203 |
+ WHERE tasklist_id=? |
204 |
+ AND user_id=?", |
205 |
+ $prop['name'], |
206 |
+ $prop['color'], |
207 |
+ $prop['showalarms']?1:0, |
208 |
+ $prop['id'], |
209 |
+ $this->rc->user->ID |
210 |
+ ); |
211 |
+ |
212 |
+ return $this->rc->db->affected_rows($query); |
213 |
+ } |
214 |
+ |
215 |
+ /** |
216 |
+ * Set active/subscribed state of a list |
217 |
+ * |
218 |
+ * @param array Hash array with list properties |
219 |
+ * @return boolean True on success, Fales on failure |
220 |
+ * @see tasklist_driver::subscribe_list() |
221 |
+ */ |
222 |
+ public function subscribe_list($prop) |
223 |
+ { |
224 |
+ $hidden = array_flip(explode(',', $this->rc->config->get('hidden_tasklists', ''))); |
225 |
+ |
226 |
+ if ($prop['active']) |
227 |
+ unset($hidden[$prop['id']]); |
228 |
+ else |
229 |
+ $hidden[$prop['id']] = 1; |
230 |
+ |
231 |
+ return $this->rc->user->save_prefs(array('hidden_tasklists' => join(',', array_keys($hidden)))); |
232 |
+ } |
233 |
+ |
234 |
+ /** |
235 |
+ * Delete the given list with all its contents |
236 |
+ * |
237 |
+ * @param array Hash array with list properties |
238 |
+ * @return boolean True on success, Fales on failure |
239 |
+ * @see tasklist_driver::remove_list() |
240 |
+ */ |
241 |
+ public function remove_list($prop) |
242 |
+ { |
243 |
+ $list_id = $prop['id']; |
244 |
+ |
245 |
+ if ($this->lists[$list_id]) { |
246 |
+ // delete all tasks linked with this list |
247 |
+ $this->rc->db->query( |
248 |
+ "DELETE FROM " . $this->db_tasks . " |
249 |
+ WHERE tasklist_id=?", |
250 |
+ $list_id |
251 |
+ ); |
252 |
+ |
253 |
+ // delete list record |
254 |
+ $query = $this->rc->db->query( |
255 |
+ "DELETE FROM " . $this->db_lists . " |
256 |
+ WHERE tasklist_id=? |
257 |
+ AND user_id=?", |
258 |
+ $list_id, |
259 |
+ $this->rc->user->ID |
260 |
+ ); |
261 |
+ |
262 |
+ return $this->rc->db->affected_rows($query); |
263 |
+ } |
264 |
+ |
265 |
+ return false; |
266 |
+ } |
267 |
+ |
268 |
+ /** |
269 |
+ * Get number of tasks matching the given filter |
270 |
+ * |
271 |
+ * @param array List of lists to count tasks of |
272 |
+ * @return array Hash array with counts grouped by status (all|flagged|today|tomorrow|overdue|nodate) |
273 |
+ * @see tasklist_driver::count_tasks() |
274 |
+ */ |
275 |
+ function count_tasks($lists = null) |
276 |
+ { |
277 |
+ if (empty($lists)) |
278 |
+ $lists = array_keys($this->lists); |
279 |
+ else if (is_string($lists)) |
280 |
+ $lists = explode(',', $lists); |
281 |
+ |
282 |
+ // only allow to select from lists of this user |
283 |
+ $list_ids = array_map(array($this->rc->db, 'quote'), array_intersect($lists, array_keys($this->lists))); |
284 |
+ |
285 |
+ $today_date = new DateTime('now', $this->plugin->timezone); |
286 |
+ $today = $today_date->format('Y-m-d'); |
287 |
+ $tomorrow_date = new DateTime('now + 1 day', $this->plugin->timezone); |
288 |
+ $tomorrow = $tomorrow_date->format('Y-m-d'); |
289 |
+ |
290 |
+ $result = $this->rc->db->query(sprintf( |
291 |
+ "SELECT task_id, flagged, date FROM " . $this->db_tasks . " |
292 |
+ WHERE tasklist_id IN (%s) |
293 |
+ AND del=0 AND complete<1", |
294 |
+ join(',', $list_ids) |
295 |
+ )); |
296 |
+ |
297 |
+ $counts = array('all' => 0, 'flagged' => 0, 'today' => 0, 'tomorrow' => 0, 'overdue' => 0, 'nodate' => 0); |
298 |
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) { |
299 |
+ $counts['all']++; |
300 |
+ if ($rec['flagged']) |
301 |
+ $counts['flagged']++; |
302 |
+ if (empty($rec['date'])) |
303 |
+ $counts['nodate']++; |
304 |
+ else if ($rec['date'] == $today) |
305 |
+ $counts['today']++; |
306 |
+ else if ($rec['date'] == $tomorrow) |
307 |
+ $counts['tomorrow']++; |
308 |
+ else if ($rec['date'] < $today) |
309 |
+ $counts['overdue']++; |
310 |
+ } |
311 |
+ |
312 |
+ return $counts; |
313 |
+ } |
314 |
+ |
315 |
+ /** |
316 |
+ * Get all taks records matching the given filter |
317 |
+ * |
318 |
+ * @param array Hash array wiht filter criterias |
319 |
+ * @param array List of lists to get tasks from |
320 |
+ * @return array List of tasks records matchin the criteria |
321 |
+ * @see tasklist_driver::list_tasks() |
322 |
+ */ |
323 |
+ function list_tasks($filter, $lists = null) |
324 |
+ { |
325 |
+ if (empty($lists)) |
326 |
+ $lists = array_keys($this->lists); |
327 |
+ else if (is_string($lists)) |
328 |
+ $lists = explode(',', $lists); |
329 |
+ |
330 |
+ // only allow to select from lists of this user |
331 |
+ $list_ids = array_map(array($this->rc->db, 'quote'), array_intersect($lists, array_keys($this->lists))); |
332 |
+ $sql_add = ''; |
333 |
+ |
334 |
+ // add filter criteria |
335 |
+ if ($filter['from'] || ($filter['mask'] & tasklist::FILTER_MASK_TODAY)) { |
336 |
+ $sql_add .= ' AND (date IS NULL OR date >= ?)'; |
337 |
+ $datefrom = $filter['from']; |
338 |
+ } |
339 |
+ if ($filter['to']) { |
340 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_OVERDUE) |
341 |
+ $sql_add .= ' AND (date IS NOT NULL AND date <= ' . $this->rc->db->quote($filter['to']) . ')'; |
342 |
+ else |
343 |
+ $sql_add .= ' AND (date IS NULL OR date <= ' . $this->rc->db->quote($filter['to']) . ')'; |
344 |
+ } |
345 |
+ |
346 |
+ // special case 'today': also show all events with date before today |
347 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_TODAY) { |
348 |
+ $datefrom = date('Y-m-d', 0); |
349 |
+ } |
350 |
+ |
351 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_NODATE) |
352 |
+ $sql_add = ' AND date IS NULL'; |
353 |
+ |
354 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_COMPLETE) |
355 |
+ $sql_add .= ' AND complete=1'; |
356 |
+ else // don't show complete tasks by default |
357 |
+ $sql_add .= ' AND complete<1'; |
358 |
+ |
359 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_FLAGGED) |
360 |
+ $sql_add .= ' AND flagged=1'; |
361 |
+ |
362 |
+ // compose (slow) SQL query for searching |
363 |
+ // FIXME: improve searching using a dedicated col and normalized values |
364 |
+ if ($filter['search']) { |
365 |
+ $sql_query = array(); |
366 |
+ foreach (array('title','description','organizer','attendees') as $col) |
367 |
+ $sql_query[] = $this->rc->db->ilike($col, '%'.$filter['search'].'%'); |
368 |
+ $sql_add = 'AND (' . join(' OR ', $sql_query) . ')'; |
369 |
+ } |
370 |
+ |
371 |
+ $tasks = array(); |
372 |
+ if (!empty($list_ids)) { |
373 |
+ $result = $this->rc->db->query(sprintf( |
374 |
+ "SELECT * FROM " . $this->db_tasks . " |
375 |
+ WHERE tasklist_id IN (%s) |
376 |
+ AND del=0 |
377 |
+ %s |
378 |
+ ORDER BY parent_id, task_id ASC", |
379 |
+ join(',', $list_ids), |
380 |
+ $sql_add |
381 |
+ ), |
382 |
+ $datefrom |
383 |
+ ); |
384 |
+ |
385 |
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) { |
386 |
+ $tasks[] = $this->_read_postprocess($rec); |
387 |
+ } |
388 |
+ } |
389 |
+ |
390 |
+ return $tasks; |
391 |
+ } |
392 |
+ |
393 |
+ /** |
394 |
+ * Return data of a specific task |
395 |
+ * |
396 |
+ * @param mixed Hash array with task properties or task UID |
397 |
+ * @return array Hash array with task properties or false if not found |
398 |
+ */ |
399 |
+ public function get_task($prop) |
400 |
+ { |
401 |
+ if (is_string($prop)) |
402 |
+ $prop['uid'] = $prop; |
403 |
+ |
404 |
+ $query_col = $prop['id'] ? 'task_id' : 'uid'; |
405 |
+ |
406 |
+ $result = $this->rc->db->query(sprintf( |
407 |
+ "SELECT * FROM " . $this->db_tasks . " |
408 |
+ WHERE tasklist_id IN (%s) |
409 |
+ AND %s=? |
410 |
+ AND del=0", |
411 |
+ $this->list_ids, |
412 |
+ $query_col |
413 |
+ ), |
414 |
+ $prop['id'] ? $prop['id'] : $prop['uid'] |
415 |
+ ); |
416 |
+ |
417 |
+ if ($result && ($rec = $this->rc->db->fetch_assoc($result))) { |
418 |
+ return $this->_read_postprocess($rec); |
419 |
+ } |
420 |
+ |
421 |
+ return false; |
422 |
+ } |
423 |
+ |
424 |
+ /** |
425 |
+ * Get all decendents of the given task record |
426 |
+ * |
427 |
+ * @param mixed Hash array with task properties or task UID |
428 |
+ * @param boolean True if all childrens children should be fetched |
429 |
+ * @return array List of all child task IDs |
430 |
+ */ |
431 |
+ public function get_childs($prop, $recursive = false) |
432 |
+ { |
433 |
+ // resolve UID first |
434 |
+ if (is_string($prop)) { |
435 |
+ $result = $this->rc->db->query(sprintf( |
436 |
+ "SELECT task_id AS id, tasklist_id AS list FROM " . $this->db_tasks . " |
437 |
+ WHERE tasklist_id IN (%s) |
438 |
+ AND uid=?", |
439 |
+ $this->list_ids |
440 |
+ ), |
441 |
+ $prop); |
442 |
+ $prop = $this->rc->db->fetch_assoc($result); |
443 |
+ } |
444 |
+ |
445 |
+ $childs = array(); |
446 |
+ $task_ids = array($prop['id']); |
447 |
+ |
448 |
+ // query for childs (recursively) |
449 |
+ while (!empty($task_ids)) { |
450 |
+ $result = $this->rc->db->query(sprintf( |
451 |
+ "SELECT task_id AS id FROM " . $this->db_tasks . " |
452 |
+ WHERE tasklist_id IN (%s) |
453 |
+ AND parent_id IN (%s) |
454 |
+ AND del=0", |
455 |
+ $this->list_ids, |
456 |
+ join(',', array_map(array($this->rc->db, 'quote'), $task_ids)) |
457 |
+ )); |
458 |
+ |
459 |
+ $task_ids = array(); |
460 |
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) { |
461 |
+ $childs[] = $rec['id']; |
462 |
+ $task_ids[] = $rec['id']; |
463 |
+ } |
464 |
+ |
465 |
+ if (!$recursive) |
466 |
+ break; |
467 |
+ } |
468 |
+ |
469 |
+ return $childs; |
470 |
+ } |
471 |
+ |
472 |
+ /** |
473 |
+ * Get a list of pending alarms to be displayed to the user |
474 |
+ * |
475 |
+ * @param integer Current time (unix timestamp) |
476 |
+ * @param mixed List of list IDs to show alarms for (either as array or comma-separated string) |
477 |
+ * @return array A list of alarms, each encoded as hash array with task properties |
478 |
+ * @see tasklist_driver::pending_alarms() |
479 |
+ */ |
480 |
+ public function pending_alarms($time, $lists = null) |
481 |
+ { |
482 |
+ if (empty($lists)) |
483 |
+ $lists = array_keys($this->lists); |
484 |
+ else if (is_string($lists)) |
485 |
+ $lists = explode(',', $lists); |
486 |
+ |
487 |
+ // only allow to select from calendars with activated alarms |
488 |
+ $list_ids = array(); |
489 |
+ foreach ($lists as $lid) { |
490 |
+ if ($this->lists[$lid] && $this->lists[$lid]['showalarms']) |
491 |
+ $list_ids[] = $lid; |
492 |
+ } |
493 |
+ $list_ids = array_map(array($this->rc->db, 'quote'), $list_ids); |
494 |
+ |
495 |
+ $alarms = array(); |
496 |
+ if (!empty($list_ids)) { |
497 |
+ $result = $this->rc->db->query(sprintf( |
498 |
+ "SELECT * FROM " . $this->db_tasks . " |
499 |
+ WHERE tasklist_id IN (%s) |
500 |
+ AND notify <= %s AND complete < 1", |
501 |
+ join(',', $list_ids), |
502 |
+ $this->rc->db->fromunixtime($time) |
503 |
+ )); |
504 |
+ |
505 |
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) |
506 |
+ $alarms[] = $this->_read_postprocess($rec); |
507 |
+ } |
508 |
+ |
509 |
+ return $alarms; |
510 |
+ } |
511 |
+ |
512 |
+ /** |
513 |
+ * Feedback after showing/sending an alarm notification |
514 |
+ * |
515 |
+ * @see tasklist_driver::dismiss_alarm() |
516 |
+ */ |
517 |
+ public function dismiss_alarm($task_id, $snooze = 0) |
518 |
+ { |
519 |
+ // set new notifyat time or unset if not snoozed |
520 |
+ $notify_at = $snooze > 0 ? date('Y-m-d H:i:s', time() + $snooze) : null; |
521 |
+ |
522 |
+ $query = $this->rc->db->query(sprintf( |
523 |
+ "UPDATE " . $this->db_tasks . " |
524 |
+ SET changed=%s, notify=? |
525 |
+ WHERE task_id=? |
526 |
+ AND tasklist_id IN (" . $this->list_ids . ")", |
527 |
+ $this->rc->db->now()), |
528 |
+ $notify_at, |
529 |
+ $task_id |
530 |
+ ); |
531 |
+ |
532 |
+ return $this->rc->db->affected_rows($query); |
533 |
+ } |
534 |
+ |
535 |
+ /** |
536 |
+ * Map some internal database values to match the generic "API" |
537 |
+ */ |
538 |
+ private function _read_postprocess($rec) |
539 |
+ { |
540 |
+ $rec['id'] = $rec['task_id']; |
541 |
+ $rec['list'] = $rec['tasklist_id']; |
542 |
+ $rec['changed'] = new DateTime($rec['changed']); |
543 |
+ $rec['tags'] = array_filter(explode(',', $rec['tags'])); |
544 |
+ |
545 |
+ if (!$rec['parent_id']) |
546 |
+ unset($rec['parent_id']); |
547 |
+ |
548 |
+ unset($rec['task_id'], $rec['tasklist_id'], $rec['created']); |
549 |
+ return $rec; |
550 |
+ } |
551 |
+ |
552 |
+ /** |
553 |
+ * Add a single task to the database |
554 |
+ * |
555 |
+ * @param array Hash array with task properties (see header of this file) |
556 |
+ * @return mixed New event ID on success, False on error |
557 |
+ * @see tasklist_driver::create_task() |
558 |
+ */ |
559 |
+ public function create_task($prop) |
560 |
+ { |
561 |
+ // check list permissions |
562 |
+ $list_id = $prop['list'] ? $prop['list'] : reset(array_keys($this->lists)); |
563 |
+ if (!$this->lists[$list_id] || $this->lists[$list_id]['readonly']) |
564 |
+ return false; |
565 |
+ |
566 |
+ foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms') as $col) { |
567 |
+ if (empty($prop[$col])) |
568 |
+ $prop[$col] = null; |
569 |
+ } |
570 |
+ |
571 |
+ $notify_at = $this->_get_notification($prop); |
572 |
+ $result = $this->rc->db->query(sprintf( |
573 |
+ "INSERT INTO " . $this->db_tasks . " |
574 |
+ (tasklist_id, uid, parent_id, created, changed, title, date, time, startdate, starttime, description, tags, alarms, notify) |
575 |
+ VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?)", |
576 |
+ $this->rc->db->now(), |
577 |
+ $this->rc->db->now() |
578 |
+ ), |
579 |
+ $list_id, |
580 |
+ $prop['uid'], |
581 |
+ $prop['parent_id'], |
582 |
+ $prop['title'], |
583 |
+ $prop['date'], |
584 |
+ $prop['time'], |
585 |
+ $prop['startdate'], |
586 |
+ $prop['starttime'], |
587 |
+ strval($prop['description']), |
588 |
+ join(',', (array)$prop['tags']), |
589 |
+ $prop['alarms'], |
590 |
+ $notify_at |
591 |
+ ); |
592 |
+ |
593 |
+ if ($result) |
594 |
+ return $this->rc->db->insert_id($this->sequence_tasks); |
595 |
+ |
596 |
+ return false; |
597 |
+ } |
598 |
+ |
599 |
+ /** |
600 |
+ * Update an task entry with the given data |
601 |
+ * |
602 |
+ * @param array Hash array with task properties |
603 |
+ * @return boolean True on success, False on error |
604 |
+ * @see tasklist_driver::edit_task() |
605 |
+ */ |
606 |
+ public function edit_task($prop) |
607 |
+ { |
608 |
+ $sql_set = array(); |
609 |
+ foreach (array('title', 'description', 'flagged', 'complete') as $col) { |
610 |
+ if (isset($prop[$col])) |
611 |
+ $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($prop[$col]); |
612 |
+ } |
613 |
+ foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms') as $col) { |
614 |
+ if (isset($prop[$col])) |
615 |
+ $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col])); |
616 |
+ } |
617 |
+ if (isset($prop['tags'])) |
618 |
+ $sql_set[] = $this->rc->db->quote_identifier('tags') . '=' . $this->rc->db->quote(join(',', (array)$prop['tags'])); |
619 |
+ |
620 |
+ if (isset($prop['date']) || isset($prop['time']) || isset($prop['alarms'])) { |
621 |
+ $notify_at = $this->_get_notification($prop); |
622 |
+ $sql_set[] = $this->rc->db->quote_identifier('notify') . '=' . (empty($notify_at) ? 'NULL' : $this->rc->db->quote($notify_at)); |
623 |
+ } |
624 |
+ |
625 |
+ // moved from another list |
626 |
+ if ($prop['_fromlist'] && ($newlist = $prop['list'])) { |
627 |
+ $sql_set[] = 'tasklist_id=' . $this->rc->db->quote($newlist); |
628 |
+ } |
629 |
+ |
630 |
+ $query = $this->rc->db->query(sprintf( |
631 |
+ "UPDATE " . $this->db_tasks . " |
632 |
+ SET changed=%s %s |
633 |
+ WHERE task_id=? |
634 |
+ AND tasklist_id IN (%s)", |
635 |
+ $this->rc->db->now(), |
636 |
+ ($sql_set ? ', ' . join(', ', $sql_set) : ''), |
637 |
+ $this->list_ids |
638 |
+ ), |
639 |
+ $prop['id'] |
640 |
+ ); |
641 |
+ |
642 |
+ return $this->rc->db->affected_rows($query); |
643 |
+ } |
644 |
+ |
645 |
+ /** |
646 |
+ * Move a single task to another list |
647 |
+ * |
648 |
+ * @param array Hash array with task properties: |
649 |
+ * @return boolean True on success, False on error |
650 |
+ * @see tasklist_driver::move_task() |
651 |
+ */ |
652 |
+ public function move_task($prop) |
653 |
+ { |
654 |
+ return $this->edit_task($prop); |
655 |
+ } |
656 |
+ |
657 |
+ /** |
658 |
+ * Remove a single task from the database |
659 |
+ * |
660 |
+ * @param array Hash array with task properties |
661 |
+ * @param boolean Remove record irreversible |
662 |
+ * @return boolean True on success, False on error |
663 |
+ * @see tasklist_driver::delete_task() |
664 |
+ */ |
665 |
+ public function delete_task($prop, $force = true) |
666 |
+ { |
667 |
+ $task_id = $prop['id']; |
668 |
+ |
669 |
+ if ($task_id && $force) { |
670 |
+ $query = $this->rc->db->query( |
671 |
+ "DELETE FROM " . $this->db_tasks . " |
672 |
+ WHERE task_id=? |
673 |
+ AND tasklist_id IN (" . $this->list_ids . ")", |
674 |
+ $task_id |
675 |
+ ); |
676 |
+ } |
677 |
+ else if ($task_id) { |
678 |
+ $query = $this->rc->db->query(sprintf( |
679 |
+ "UPDATE " . $this->db_tasks . " |
680 |
+ SET changed=%s, del=1 |
681 |
+ WHERE task_id=? |
682 |
+ AND tasklist_id IN (%s)", |
683 |
+ $this->rc->db->now(), |
684 |
+ $this->list_ids |
685 |
+ ), |
686 |
+ $task_id |
687 |
+ ); |
688 |
+ } |
689 |
+ |
690 |
+ return $this->rc->db->affected_rows($query); |
691 |
+ } |
692 |
+ |
693 |
+ /** |
694 |
+ * Restores a single deleted task (if supported) |
695 |
+ * |
696 |
+ * @param array Hash array with task properties |
697 |
+ * @return boolean True on success, False on error |
698 |
+ * @see tasklist_driver::undelete_task() |
699 |
+ */ |
700 |
+ public function undelete_task($prop) |
701 |
+ { |
702 |
+ $query = $this->rc->db->query(sprintf( |
703 |
+ "UPDATE " . $this->db_tasks . " |
704 |
+ SET changed=%s, del=0 |
705 |
+ WHERE task_id=? |
706 |
+ AND tasklist_id IN (%s)", |
707 |
+ $this->rc->db->now(), |
708 |
+ $this->list_ids |
709 |
+ ), |
710 |
+ $prop['id'] |
711 |
+ ); |
712 |
+ |
713 |
+ return $this->rc->db->affected_rows($query); |
714 |
+ } |
715 |
+ |
716 |
+ /** |
717 |
+ * Compute absolute time to notify the user |
718 |
+ */ |
719 |
+ private function _get_notification($task) |
720 |
+ { |
721 |
+ if ($task['alarms'] && $task['complete'] < 1 || strpos($task['alarms'], '@') !== false) { |
722 |
+ $alarm = libcalendaring::get_next_alarm($task, 'task'); |
723 |
+ |
724 |
+ if ($alarm['time'] && $alarm['action'] == 'DISPLAY') |
725 |
+ return date('Y-m-d H:i:s', $alarm['time']); |
726 |
+ } |
727 |
+ |
728 |
+ return null; |
729 |
+ } |
730 |
+ |
731 |
+} |
732 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php |
733 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php 1970-01-01 01:00:00.000000000 +0100 |
734 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php 2013-11-24 00:38:51.000000000 +0100 |
735 |
@@ -0,0 +1,853 @@ |
736 |
+<?php |
737 |
+ |
738 |
+/** |
739 |
+ * Kolab Groupware driver for the Tasklist plugin |
740 |
+ * |
741 |
+ * @version @package_version@ |
742 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
743 |
+ * |
744 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
745 |
+ * |
746 |
+ * This program is free software: you can redistribute it and/or modify |
747 |
+ * it under the terms of the GNU Affero General Public License as |
748 |
+ * published by the Free Software Foundation, either version 3 of the |
749 |
+ * License, or (at your option) any later version. |
750 |
+ * |
751 |
+ * This program is distributed in the hope that it will be useful, |
752 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
753 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
754 |
+ * GNU Affero General Public License for more details. |
755 |
+ * |
756 |
+ * You should have received a copy of the GNU Affero General Public License |
757 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
758 |
+ */ |
759 |
+ |
760 |
+class tasklist_kolab_driver extends tasklist_driver |
761 |
+{ |
762 |
+ // features supported by the backend |
763 |
+ public $alarms = false; |
764 |
+ public $attachments = true; |
765 |
+ public $undelete = false; // task undelete action |
766 |
+ public $alarm_types = array('DISPLAY'); |
767 |
+ |
768 |
+ private $rc; |
769 |
+ private $plugin; |
770 |
+ private $lists; |
771 |
+ private $folders = array(); |
772 |
+ private $tasks = array(); |
773 |
+ |
774 |
+ |
775 |
+ /** |
776 |
+ * Default constructor |
777 |
+ */ |
778 |
+ public function __construct($plugin) |
779 |
+ { |
780 |
+ $this->rc = $plugin->rc; |
781 |
+ $this->plugin = $plugin; |
782 |
+ |
783 |
+ $this->_read_lists(); |
784 |
+ |
785 |
+ if (kolab_storage::$version == '2.0') { |
786 |
+ $this->alarm_absolute = false; |
787 |
+ } |
788 |
+ } |
789 |
+ |
790 |
+ /** |
791 |
+ * Read available calendars for the current user and store them internally |
792 |
+ */ |
793 |
+ private function _read_lists($force = false) |
794 |
+ { |
795 |
+ // already read sources |
796 |
+ if (isset($this->lists) && !$force) |
797 |
+ return $this->lists; |
798 |
+ |
799 |
+ // get all folders that have type "task" |
800 |
+ $this->folders = kolab_storage::get_folders('task'); |
801 |
+ $this->lists = array(); |
802 |
+ |
803 |
+ // convert to UTF8 and sort |
804 |
+ $names = array(); |
805 |
+ $default_folder = null; |
806 |
+ foreach ($this->folders as $folder) { |
807 |
+ $names[$folder->name] = rcube_charset::convert($folder->name, 'UTF7-IMAP'); |
808 |
+ $this->folders[$folder->name] = $folder; |
809 |
+ if ($folder->default) |
810 |
+ $default_folder = $folder->name; |
811 |
+ } |
812 |
+ |
813 |
+ asort($names, SORT_LOCALE_STRING); |
814 |
+ |
815 |
+ // put default folder (aka INBOX) on top of the list |
816 |
+ if ($default_folder) { |
817 |
+ $default_name = $names[$default_folder]; |
818 |
+ unset($names[$default_folder]); |
819 |
+ $names = array_merge(array($default_folder => $default_name), $names); |
820 |
+ } |
821 |
+ |
822 |
+ $delim = $this->rc->get_storage()->get_hierarchy_delimiter(); |
823 |
+ $listnames = array(); |
824 |
+ |
825 |
+ $prefs = $this->rc->config->get('kolab_tasklists', array()); |
826 |
+ |
827 |
+ foreach ($names as $utf7name => $name) { |
828 |
+ $folder = $this->folders[$utf7name]; |
829 |
+ |
830 |
+ $path_imap = explode($delim, $name); |
831 |
+ $editname = array_pop($path_imap); // pop off raw name part |
832 |
+ $path_imap = join($delim, $path_imap); |
833 |
+ |
834 |
+ $name = kolab_storage::folder_displayname(kolab_storage::object_name($utf7name), $listnames); |
835 |
+ |
836 |
+ if ($folder->get_namespace() == 'personal') { |
837 |
+ $readonly = false; |
838 |
+ $alarms = true; |
839 |
+ } |
840 |
+ else { |
841 |
+ $alarms = false; |
842 |
+ $readonly = true; |
843 |
+ if (($rights = $folder->get_myrights()) && !PEAR::isError($rights)) { |
844 |
+ if (strpos($rights, 'i') !== false) |
845 |
+ $readonly = false; |
846 |
+ } |
847 |
+ } |
848 |
+ |
849 |
+ $list_id = kolab_storage::folder_id($utf7name); |
850 |
+ $tasklist = array( |
851 |
+ 'id' => $list_id, |
852 |
+ 'name' => $name, |
853 |
+ 'editname' => $editname, |
854 |
+ 'color' => $folder->get_color('0000CC'), |
855 |
+ 'showalarms' => isset($prefs[$list_id]['showalarms']) ? $prefs[$list_id]['showalarms'] : $alarms, |
856 |
+ 'editable' => !$readonly, |
857 |
+ 'active' => $folder->is_active(), |
858 |
+ 'parentfolder' => $path_imap, |
859 |
+ 'default' => $folder->default, |
860 |
+ 'class_name' => trim($folder->get_namespace() . ($folder->default ? ' default' : '')), |
861 |
+ ); |
862 |
+ $this->lists[$tasklist['id']] = $tasklist; |
863 |
+ $this->folders[$tasklist['id']] = $folder; |
864 |
+ } |
865 |
+ } |
866 |
+ |
867 |
+ /** |
868 |
+ * Get a list of available task lists from this source |
869 |
+ */ |
870 |
+ public function get_lists() |
871 |
+ { |
872 |
+ // attempt to create a default list for this user |
873 |
+ if (empty($this->lists)) { |
874 |
+ if ($this->create_list(array('name' => 'Tasks', 'color' => '0000CC', 'default' => true))) |
875 |
+ $this->_read_lists(true); |
876 |
+ } |
877 |
+ |
878 |
+ return $this->lists; |
879 |
+ } |
880 |
+ |
881 |
+ /** |
882 |
+ * Create a new list assigned to the current user |
883 |
+ * |
884 |
+ * @param array Hash array with list properties |
885 |
+ * name: List name |
886 |
+ * color: The color of the list |
887 |
+ * showalarms: True if alarms are enabled |
888 |
+ * @return mixed ID of the new list on success, False on error |
889 |
+ */ |
890 |
+ public function create_list($prop) |
891 |
+ { |
892 |
+ $prop['type'] = 'task' . ($prop['default'] ? '.default' : ''); |
893 |
+ $prop['active'] = true; // activate folder by default |
894 |
+ $folder = kolab_storage::folder_update($prop); |
895 |
+ |
896 |
+ if ($folder === false) { |
897 |
+ $this->last_error = kolab_storage::$last_error; |
898 |
+ return false; |
899 |
+ } |
900 |
+ |
901 |
+ // create ID |
902 |
+ $id = kolab_storage::folder_id($folder); |
903 |
+ |
904 |
+ $prefs['kolab_tasklists'] = $this->rc->config->get('kolab_tasklists', array()); |
905 |
+ |
906 |
+ if (isset($prop['showalarms'])) |
907 |
+ $prefs['kolab_tasklists'][$id]['showalarms'] = $prop['showalarms'] ? true : false; |
908 |
+ |
909 |
+ if ($prefs['kolab_tasklists'][$id]) |
910 |
+ $this->rc->user->save_prefs($prefs); |
911 |
+ |
912 |
+ return $id; |
913 |
+ } |
914 |
+ |
915 |
+ /** |
916 |
+ * Update properties of an existing tasklist |
917 |
+ * |
918 |
+ * @param array Hash array with list properties |
919 |
+ * id: List Identifier |
920 |
+ * name: List name |
921 |
+ * color: The color of the list |
922 |
+ * showalarms: True if alarms are enabled (if supported) |
923 |
+ * @return boolean True on success, Fales on failure |
924 |
+ */ |
925 |
+ public function edit_list($prop) |
926 |
+ { |
927 |
+ if ($prop['id'] && ($folder = $this->folders[$prop['id']])) { |
928 |
+ $prop['oldname'] = $folder->name; |
929 |
+ $prop['type'] = 'task'; |
930 |
+ $newfolder = kolab_storage::folder_update($prop); |
931 |
+ |
932 |
+ if ($newfolder === false) { |
933 |
+ $this->last_error = kolab_storage::$last_error; |
934 |
+ return false; |
935 |
+ } |
936 |
+ |
937 |
+ // create ID |
938 |
+ $id = kolab_storage::folder_id($newfolder); |
939 |
+ |
940 |
+ // fallback to local prefs |
941 |
+ $prefs['kolab_tasklists'] = $this->rc->config->get('kolab_tasklists', array()); |
942 |
+ unset($prefs['kolab_tasklists'][$prop['id']]); |
943 |
+ |
944 |
+ if (isset($prop['showalarms'])) |
945 |
+ $prefs['kolab_tasklists'][$id]['showalarms'] = $prop['showalarms'] ? true : false; |
946 |
+ |
947 |
+ if ($prefs['kolab_tasklists'][$id]) |
948 |
+ $this->rc->user->save_prefs($prefs); |
949 |
+ |
950 |
+ return $id; |
951 |
+ } |
952 |
+ |
953 |
+ return false; |
954 |
+ } |
955 |
+ |
956 |
+ /** |
957 |
+ * Set active/subscribed state of a list |
958 |
+ * |
959 |
+ * @param array Hash array with list properties |
960 |
+ * id: List Identifier |
961 |
+ * active: True if list is active, false if not |
962 |
+ * @return boolean True on success, Fales on failure |
963 |
+ */ |
964 |
+ public function subscribe_list($prop) |
965 |
+ { |
966 |
+ if ($prop['id'] && ($folder = $this->folders[$prop['id']])) { |
967 |
+ return $folder->activate($prop['active']); |
968 |
+ } |
969 |
+ return false; |
970 |
+ } |
971 |
+ |
972 |
+ /** |
973 |
+ * Delete the given list with all its contents |
974 |
+ * |
975 |
+ * @param array Hash array with list properties |
976 |
+ * id: list Identifier |
977 |
+ * @return boolean True on success, Fales on failure |
978 |
+ */ |
979 |
+ public function remove_list($prop) |
980 |
+ { |
981 |
+ if ($prop['id'] && ($folder = $this->folders[$prop['id']])) { |
982 |
+ if (kolab_storage::folder_delete($folder->name)) |
983 |
+ return true; |
984 |
+ else |
985 |
+ $this->last_error = kolab_storage::$last_error; |
986 |
+ } |
987 |
+ |
988 |
+ return false; |
989 |
+ } |
990 |
+ |
991 |
+ /** |
992 |
+ * Get number of tasks matching the given filter |
993 |
+ * |
994 |
+ * @param array List of lists to count tasks of |
995 |
+ * @return array Hash array with counts grouped by status (all|flagged|completed|today|tomorrow|nodate) |
996 |
+ */ |
997 |
+ public function count_tasks($lists = null) |
998 |
+ { |
999 |
+ if (empty($lists)) |
1000 |
+ $lists = array_keys($this->lists); |
1001 |
+ else if (is_string($lists)) |
1002 |
+ $lists = explode(',', $lists); |
1003 |
+ |
1004 |
+ $today_date = new DateTime('now', $this->plugin->timezone); |
1005 |
+ $today = $today_date->format('Y-m-d'); |
1006 |
+ $tomorrow_date = new DateTime('now + 1 day', $this->plugin->timezone); |
1007 |
+ $tomorrow = $tomorrow_date->format('Y-m-d'); |
1008 |
+ |
1009 |
+ $counts = array('all' => 0, 'flagged' => 0, 'today' => 0, 'tomorrow' => 0, 'overdue' => 0, 'nodate' => 0); |
1010 |
+ foreach ($lists as $list_id) { |
1011 |
+ $folder = $this->folders[$list_id]; |
1012 |
+ foreach ((array)$folder->select(array(array('tags','!~','x-complete'))) as $record) { |
1013 |
+ $rec = $this->_to_rcube_task($record); |
1014 |
+ |
1015 |
+ if ($rec['complete'] >= 1.0) // don't count complete tasks |
1016 |
+ continue; |
1017 |
+ |
1018 |
+ $counts['all']++; |
1019 |
+ if ($rec['flagged']) |
1020 |
+ $counts['flagged']++; |
1021 |
+ if (empty($rec['date'])) |
1022 |
+ $counts['nodate']++; |
1023 |
+ else if ($rec['date'] == $today) |
1024 |
+ $counts['today']++; |
1025 |
+ else if ($rec['date'] == $tomorrow) |
1026 |
+ $counts['tomorrow']++; |
1027 |
+ else if ($rec['date'] < $today) |
1028 |
+ $counts['overdue']++; |
1029 |
+ } |
1030 |
+ } |
1031 |
+ |
1032 |
+ return $counts; |
1033 |
+ } |
1034 |
+ |
1035 |
+ /** |
1036 |
+ * Get all taks records matching the given filter |
1037 |
+ * |
1038 |
+ * @param array Hash array with filter criterias: |
1039 |
+ * - mask: Bitmask representing the filter selection (check against tasklist::FILTER_MASK_* constants) |
1040 |
+ * - from: Date range start as string (Y-m-d) |
1041 |
+ * - to: Date range end as string (Y-m-d) |
1042 |
+ * - search: Search query string |
1043 |
+ * @param array List of lists to get tasks from |
1044 |
+ * @return array List of tasks records matchin the criteria |
1045 |
+ */ |
1046 |
+ public function list_tasks($filter, $lists = null) |
1047 |
+ { |
1048 |
+ if (empty($lists)) |
1049 |
+ $lists = array_keys($this->lists); |
1050 |
+ else if (is_string($lists)) |
1051 |
+ $lists = explode(',', $lists); |
1052 |
+ |
1053 |
+ $results = array(); |
1054 |
+ |
1055 |
+ // query Kolab storage |
1056 |
+ $query = array(); |
1057 |
+ if ($filter['mask'] & tasklist::FILTER_MASK_COMPLETE) |
1058 |
+ $query[] = array('tags','~','x-complete'); |
1059 |
+ else |
1060 |
+ $query[] = array('tags','!~','x-complete'); |
1061 |
+ |
1062 |
+ // full text search (only works with cache enabled) |
1063 |
+ if ($filter['search']) { |
1064 |
+ $search = mb_strtolower($filter['search']); |
1065 |
+ foreach (rcube_utils::normalize_string($search, true) as $word) { |
1066 |
+ $query[] = array('words', '~', $word); |
1067 |
+ } |
1068 |
+ } |
1069 |
+ |
1070 |
+ foreach ($lists as $list_id) { |
1071 |
+ $folder = $this->folders[$list_id]; |
1072 |
+ foreach ((array)$folder->select($query) as $record) { |
1073 |
+ $task = $this->_to_rcube_task($record); |
1074 |
+ $task['list'] = $list_id; |
1075 |
+ |
1076 |
+ // TODO: post-filter tasks returned from storage |
1077 |
+ |
1078 |
+ $results[] = $task; |
1079 |
+ } |
1080 |
+ } |
1081 |
+ |
1082 |
+ return $results; |
1083 |
+ } |
1084 |
+ |
1085 |
+ /** |
1086 |
+ * Return data of a specific task |
1087 |
+ * |
1088 |
+ * @param mixed Hash array with task properties or task UID |
1089 |
+ * @return array Hash array with task properties or false if not found |
1090 |
+ */ |
1091 |
+ public function get_task($prop) |
1092 |
+ { |
1093 |
+ $id = is_array($prop) ? ($prop['uid'] ?: $prop['id']) : $prop; |
1094 |
+ $list_id = is_array($prop) ? $prop['list'] : null; |
1095 |
+ $folders = $list_id ? array($list_id => $this->folders[$list_id]) : $this->folders; |
1096 |
+ |
1097 |
+ // find task in the available folders |
1098 |
+ foreach ($folders as $list_id => $folder) { |
1099 |
+ if (is_numeric($list_id)) |
1100 |
+ continue; |
1101 |
+ if (!$this->tasks[$id] && ($object = $folder->get_object($id))) { |
1102 |
+ $this->tasks[$id] = $this->_to_rcube_task($object); |
1103 |
+ $this->tasks[$id]['list'] = $list_id; |
1104 |
+ break; |
1105 |
+ } |
1106 |
+ } |
1107 |
+ |
1108 |
+ return $this->tasks[$id]; |
1109 |
+ } |
1110 |
+ |
1111 |
+ /** |
1112 |
+ * Get all decendents of the given task record |
1113 |
+ * |
1114 |
+ * @param mixed Hash array with task properties or task UID |
1115 |
+ * @param boolean True if all childrens children should be fetched |
1116 |
+ * @return array List of all child task IDs |
1117 |
+ */ |
1118 |
+ public function get_childs($prop, $recursive = false) |
1119 |
+ { |
1120 |
+ if (is_string($prop)) { |
1121 |
+ $task = $this->get_task($prop); |
1122 |
+ $prop = array('id' => $task['id'], 'list' => $task['list']); |
1123 |
+ } |
1124 |
+ |
1125 |
+ $childs = array(); |
1126 |
+ $list_id = $prop['list']; |
1127 |
+ $task_ids = array($prop['id']); |
1128 |
+ $folder = $this->folders[$list_id]; |
1129 |
+ |
1130 |
+ // query for childs (recursively) |
1131 |
+ while ($folder && !empty($task_ids)) { |
1132 |
+ $query_ids = array(); |
1133 |
+ foreach ($task_ids as $task_id) { |
1134 |
+ $query = array(array('tags','=','x-parent:' . $task_id)); |
1135 |
+ foreach ((array)$folder->select($query) as $record) { |
1136 |
+ // don't rely on kolab_storage_folder filtering |
1137 |
+ if ($record['parent_id'] == $task_id) { |
1138 |
+ $childs[] = $record['uid']; |
1139 |
+ $query_ids[] = $record['uid']; |
1140 |
+ } |
1141 |
+ } |
1142 |
+ } |
1143 |
+ |
1144 |
+ if (!$recursive) |
1145 |
+ break; |
1146 |
+ |
1147 |
+ $task_ids = $query_ids; |
1148 |
+ } |
1149 |
+ |
1150 |
+ return $childs; |
1151 |
+ } |
1152 |
+ |
1153 |
+ /** |
1154 |
+ * Get a list of pending alarms to be displayed to the user |
1155 |
+ * |
1156 |
+ * @param integer Current time (unix timestamp) |
1157 |
+ * @param mixed List of list IDs to show alarms for (either as array or comma-separated string) |
1158 |
+ * @return array A list of alarms, each encoded as hash array with task properties |
1159 |
+ * @see tasklist_driver::pending_alarms() |
1160 |
+ */ |
1161 |
+ public function pending_alarms($time, $lists = null) |
1162 |
+ { |
1163 |
+ $interval = 300; |
1164 |
+ $time -= $time % 60; |
1165 |
+ |
1166 |
+ $slot = $time; |
1167 |
+ $slot -= $slot % $interval; |
1168 |
+ |
1169 |
+ $last = $time - max(60, $this->rc->config->get('refresh_interval', 0)); |
1170 |
+ $last -= $last % $interval; |
1171 |
+ |
1172 |
+ // only check for alerts once in 5 minutes |
1173 |
+ if ($last == $slot) |
1174 |
+ return array(); |
1175 |
+ |
1176 |
+ if ($lists && is_string($lists)) |
1177 |
+ $lists = explode(',', $lists); |
1178 |
+ |
1179 |
+ $time = $slot + $interval; |
1180 |
+ |
1181 |
+ $tasks = array(); |
1182 |
+ $query = array(array('tags', '=', 'x-has-alarms'), array('tags', '!=', 'x-complete')); |
1183 |
+ foreach ($this->lists as $lid => $list) { |
1184 |
+ // skip lists with alarms disabled |
1185 |
+ if (!$list['showalarms'] || ($lists && !in_array($lid, $lists))) |
1186 |
+ continue; |
1187 |
+ |
1188 |
+ $folder = $this->folders[$lid]; |
1189 |
+ foreach ((array)$folder->select($query) as $record) { |
1190 |
+ if (!$record['alarms']) // don't trust query :-) |
1191 |
+ continue; |
1192 |
+ |
1193 |
+ $task = $this->_to_rcube_task($record); |
1194 |
+ |
1195 |
+ // add to list if alarm is set |
1196 |
+ $alarm = libcalendaring::get_next_alarm($task, 'task'); |
1197 |
+ if ($alarm && $alarm['time'] && $alarm['time'] <= $time && $alarm['action'] == 'DISPLAY') { |
1198 |
+ $id = $task['id']; |
1199 |
+ $tasks[$id] = $task; |
1200 |
+ $tasks[$id]['notifyat'] = $alarm['time']; |
1201 |
+ } |
1202 |
+ } |
1203 |
+ } |
1204 |
+ |
1205 |
+ // get alarm information stored in local database |
1206 |
+ if (!empty($tasks)) { |
1207 |
+ $task_ids = array_map(array($this->rc->db, 'quote'), array_keys($tasks)); |
1208 |
+ $result = $this->rc->db->query(sprintf( |
1209 |
+ "SELECT * FROM kolab_alarms |
1210 |
+ WHERE event_id IN (%s) AND user_id=?", |
1211 |
+ join(',', $task_ids), |
1212 |
+ $this->rc->db->now() |
1213 |
+ ), |
1214 |
+ $this->rc->user->ID |
1215 |
+ ); |
1216 |
+ |
1217 |
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) { |
1218 |
+ $dbdata[$rec['event_id']] = $rec; |
1219 |
+ } |
1220 |
+ } |
1221 |
+ |
1222 |
+ $alarms = array(); |
1223 |
+ foreach ($tasks as $id => $task) { |
1224 |
+ // skip dismissed |
1225 |
+ if ($dbdata[$id]['dismissed']) |
1226 |
+ continue; |
1227 |
+ |
1228 |
+ // snooze function may have shifted alarm time |
1229 |
+ $notifyat = $dbdata[$id]['notifyat'] ? strtotime($dbdata[$id]['notifyat']) : $task['notifyat']; |
1230 |
+ if ($notifyat <= $time) |
1231 |
+ $alarms[] = $task; |
1232 |
+ } |
1233 |
+ |
1234 |
+ return $alarms; |
1235 |
+ } |
1236 |
+ |
1237 |
+ /** |
1238 |
+ * (User) feedback after showing an alarm notification |
1239 |
+ * This should mark the alarm as 'shown' or snooze it for the given amount of time |
1240 |
+ * |
1241 |
+ * @param string Task identifier |
1242 |
+ * @param integer Suspend the alarm for this number of seconds |
1243 |
+ */ |
1244 |
+ public function dismiss_alarm($id, $snooze = 0) |
1245 |
+ { |
1246 |
+ // delete old alarm entry |
1247 |
+ $this->rc->db->query( |
1248 |
+ "DELETE FROM kolab_alarms |
1249 |
+ WHERE event_id=? AND user_id=?", |
1250 |
+ $id, |
1251 |
+ $this->rc->user->ID |
1252 |
+ ); |
1253 |
+ |
1254 |
+ // set new notifyat time or unset if not snoozed |
1255 |
+ $notifyat = $snooze > 0 ? date('Y-m-d H:i:s', time() + $snooze) : null; |
1256 |
+ |
1257 |
+ $query = $this->rc->db->query( |
1258 |
+ "INSERT INTO kolab_alarms |
1259 |
+ (event_id, user_id, dismissed, notifyat) |
1260 |
+ VALUES(?, ?, ?, ?)", |
1261 |
+ $id, |
1262 |
+ $this->rc->user->ID, |
1263 |
+ $snooze > 0 ? 0 : 1, |
1264 |
+ $notifyat |
1265 |
+ ); |
1266 |
+ |
1267 |
+ return $this->rc->db->affected_rows($query); |
1268 |
+ } |
1269 |
+ |
1270 |
+ /** |
1271 |
+ * Convert from Kolab_Format to internal representation |
1272 |
+ */ |
1273 |
+ private function _to_rcube_task($record) |
1274 |
+ { |
1275 |
+ $task = array( |
1276 |
+ 'id' => $record['uid'], |
1277 |
+ 'uid' => $record['uid'], |
1278 |
+ 'title' => $record['title'], |
1279 |
+# 'location' => $record['location'], |
1280 |
+ 'description' => $record['description'], |
1281 |
+ 'tags' => (array)$record['categories'], |
1282 |
+ 'flagged' => $record['priority'] == 1, |
1283 |
+ 'complete' => $record['status'] == 'COMPLETED' ? 1 : floatval($record['complete'] / 100), |
1284 |
+ 'parent_id' => $record['parent_id'], |
1285 |
+ ); |
1286 |
+ |
1287 |
+ // convert from DateTime to internal date format |
1288 |
+ if (is_a($record['due'], 'DateTime')) { |
1289 |
+ $task['date'] = $record['due']->format('Y-m-d'); |
1290 |
+ if (!$record['due']->_dateonly) |
1291 |
+ $task['time'] = $record['due']->format('H:i'); |
1292 |
+ } |
1293 |
+ // convert from DateTime to internal date format |
1294 |
+ if (is_a($record['start'], 'DateTime')) { |
1295 |
+ $task['startdate'] = $record['start']->format('Y-m-d'); |
1296 |
+ if (!$record['start']->_dateonly) |
1297 |
+ $task['starttime'] = $record['start']->format('H:i'); |
1298 |
+ } |
1299 |
+ if (is_a($record['dtstamp'], 'DateTime')) { |
1300 |
+ $task['changed'] = $record['dtstamp']; |
1301 |
+ } |
1302 |
+ |
1303 |
+ if ($record['alarms']) { |
1304 |
+ $task['alarms'] = $record['alarms']; |
1305 |
+ } |
1306 |
+ |
1307 |
+ if (!empty($record['_attachments'])) { |
1308 |
+ foreach ($record['_attachments'] as $key => $attachment) { |
1309 |
+ if ($attachment !== false) { |
1310 |
+ if (!$attachment['name']) |
1311 |
+ $attachment['name'] = $key; |
1312 |
+ $attachments[] = $attachment; |
1313 |
+ } |
1314 |
+ } |
1315 |
+ |
1316 |
+ $task['attachments'] = $attachments; |
1317 |
+ } |
1318 |
+ |
1319 |
+ return $task; |
1320 |
+ } |
1321 |
+ |
1322 |
+ /** |
1323 |
+ * Convert the given task record into a data structure that can be passed to kolab_storage backend for saving |
1324 |
+ * (opposite of self::_to_rcube_event()) |
1325 |
+ */ |
1326 |
+ private function _from_rcube_task($task, $old = array()) |
1327 |
+ { |
1328 |
+ $object = $task; |
1329 |
+ $object['categories'] = (array)$task['tags']; |
1330 |
+ |
1331 |
+ if (!empty($task['date'])) { |
1332 |
+ $object['due'] = new DateTime($task['date'].' '.$task['time'], $this->plugin->timezone); |
1333 |
+ if (empty($task['time'])) |
1334 |
+ $object['due']->_dateonly = true; |
1335 |
+ unset($object['date']); |
1336 |
+ } |
1337 |
+ |
1338 |
+ if (!empty($task['startdate'])) { |
1339 |
+ $object['start'] = new DateTime($task['startdate'].' '.$task['starttime'], $this->plugin->timezone); |
1340 |
+ if (empty($task['starttime'])) |
1341 |
+ $object['start']->_dateonly = true; |
1342 |
+ unset($object['startdate']); |
1343 |
+ } |
1344 |
+ |
1345 |
+ $object['complete'] = $task['complete'] * 100; |
1346 |
+ if ($task['complete'] == 1.0) |
1347 |
+ $object['status'] = 'COMPLETED'; |
1348 |
+ |
1349 |
+ if ($task['flagged']) |
1350 |
+ $object['priority'] = 1; |
1351 |
+ else |
1352 |
+ $object['priority'] = $old['priority'] > 1 ? $old['priority'] : 0; |
1353 |
+ |
1354 |
+ // copy meta data (starting with _) from old object |
1355 |
+ foreach ((array)$old as $key => $val) { |
1356 |
+ if (!isset($object[$key]) && $key[0] == '_') |
1357 |
+ $object[$key] = $val; |
1358 |
+ } |
1359 |
+ |
1360 |
+ // delete existing attachment(s) |
1361 |
+ if (!empty($task['deleted_attachments'])) { |
1362 |
+ foreach ($task['deleted_attachments'] as $attachment) { |
1363 |
+ if (is_array($object['_attachments'])) { |
1364 |
+ foreach ($object['_attachments'] as $idx => $att) { |
1365 |
+ if ($att['id'] == $attachment) |
1366 |
+ $object['_attachments'][$idx] = false; |
1367 |
+ } |
1368 |
+ } |
1369 |
+ } |
1370 |
+ unset($task['deleted_attachments']); |
1371 |
+ } |
1372 |
+ |
1373 |
+ // in kolab_storage attachments are indexed by content-id |
1374 |
+ if (is_array($task['attachments'])) { |
1375 |
+ foreach ($task['attachments'] as $idx => $attachment) { |
1376 |
+ $key = null; |
1377 |
+ // Roundcube ID has nothing to do with the storage ID, remove it |
1378 |
+ if ($attachment['content']) { |
1379 |
+ unset($attachment['id']); |
1380 |
+ } |
1381 |
+ else { |
1382 |
+ foreach ((array)$old['_attachments'] as $cid => $oldatt) { |
1383 |
+ if ($oldatt && $attachment['id'] == $oldatt['id']) |
1384 |
+ $key = $cid; |
1385 |
+ } |
1386 |
+ } |
1387 |
+ |
1388 |
+ // replace existing entry |
1389 |
+ if ($key) { |
1390 |
+ $object['_attachments'][$key] = $attachment; |
1391 |
+ } |
1392 |
+ // append as new attachment |
1393 |
+ else { |
1394 |
+ $object['_attachments'][] = $attachment; |
1395 |
+ } |
1396 |
+ } |
1397 |
+ |
1398 |
+ unset($object['attachments']); |
1399 |
+ } |
1400 |
+ |
1401 |
+ unset($object['tempid'], $object['raw'], $object['list'], $object['flagged'], $object['tags']); |
1402 |
+ return $object; |
1403 |
+ } |
1404 |
+ |
1405 |
+ /** |
1406 |
+ * Add a single task to the database |
1407 |
+ * |
1408 |
+ * @param array Hash array with task properties (see header of tasklist_driver.php) |
1409 |
+ * @return mixed New task ID on success, False on error |
1410 |
+ */ |
1411 |
+ public function create_task($task) |
1412 |
+ { |
1413 |
+ return $this->edit_task($task); |
1414 |
+ } |
1415 |
+ |
1416 |
+ /** |
1417 |
+ * Update an task entry with the given data |
1418 |
+ * |
1419 |
+ * @param array Hash array with task properties (see header of tasklist_driver.php) |
1420 |
+ * @return boolean True on success, False on error |
1421 |
+ */ |
1422 |
+ public function edit_task($task) |
1423 |
+ { |
1424 |
+ $list_id = $task['list']; |
1425 |
+ if (!$list_id || !($folder = $this->folders[$list_id])) |
1426 |
+ return false; |
1427 |
+ |
1428 |
+ // moved from another folder |
1429 |
+ if ($task['_fromlist'] && ($fromfolder = $this->folders[$task['_fromlist']])) { |
1430 |
+ if (!$fromfolder->move($task['id'], $folder->name)) |
1431 |
+ return false; |
1432 |
+ |
1433 |
+ unset($task['_fromlist']); |
1434 |
+ } |
1435 |
+ |
1436 |
+ // load previous version of this task to merge |
1437 |
+ if ($task['id']) { |
1438 |
+ $old = $folder->get_object($task['id']); |
1439 |
+ if (!$old || PEAR::isError($old)) |
1440 |
+ return false; |
1441 |
+ |
1442 |
+ // merge existing properties if the update isn't complete |
1443 |
+ if (!isset($task['title']) || !isset($task['complete'])) |
1444 |
+ $task += $this->_to_rcube_task($old); |
1445 |
+ } |
1446 |
+ |
1447 |
+ // generate new task object from RC input |
1448 |
+ $object = $this->_from_rcube_task($task, $old); |
1449 |
+ $saved = $folder->save($object, 'task', $task['id']); |
1450 |
+ |
1451 |
+ if (!$saved) { |
1452 |
+ raise_error(array( |
1453 |
+ 'code' => 600, 'type' => 'php', |
1454 |
+ 'file' => __FILE__, 'line' => __LINE__, |
1455 |
+ 'message' => "Error saving task object to Kolab server"), |
1456 |
+ true, false); |
1457 |
+ $saved = false; |
1458 |
+ } |
1459 |
+ else { |
1460 |
+ $task = $this->_to_rcube_task($object); |
1461 |
+ $task['list'] = $list_id; |
1462 |
+ $this->tasks[$task['id']] = $task; |
1463 |
+ } |
1464 |
+ |
1465 |
+ return $saved; |
1466 |
+ } |
1467 |
+ |
1468 |
+ /** |
1469 |
+ * Move a single task to another list |
1470 |
+ * |
1471 |
+ * @param array Hash array with task properties: |
1472 |
+ * @return boolean True on success, False on error |
1473 |
+ * @see tasklist_driver::move_task() |
1474 |
+ */ |
1475 |
+ public function move_task($task) |
1476 |
+ { |
1477 |
+ $list_id = $task['list']; |
1478 |
+ if (!$list_id || !($folder = $this->folders[$list_id])) |
1479 |
+ return false; |
1480 |
+ |
1481 |
+ // execute move command |
1482 |
+ if ($task['_fromlist'] && ($fromfolder = $this->folders[$task['_fromlist']])) { |
1483 |
+ return $fromfolder->move($task['id'], $folder->name); |
1484 |
+ } |
1485 |
+ |
1486 |
+ return false; |
1487 |
+ } |
1488 |
+ |
1489 |
+ /** |
1490 |
+ * Remove a single task from the database |
1491 |
+ * |
1492 |
+ * @param array Hash array with task properties: |
1493 |
+ * id: Task identifier |
1494 |
+ * @param boolean Remove record irreversible (mark as deleted otherwise, if supported by the backend) |
1495 |
+ * @return boolean True on success, False on error |
1496 |
+ */ |
1497 |
+ public function delete_task($task, $force = true) |
1498 |
+ { |
1499 |
+ $list_id = $task['list']; |
1500 |
+ if (!$list_id || !($folder = $this->folders[$list_id])) |
1501 |
+ return false; |
1502 |
+ |
1503 |
+ return $folder->delete($task['id']); |
1504 |
+ } |
1505 |
+ |
1506 |
+ /** |
1507 |
+ * Restores a single deleted task (if supported) |
1508 |
+ * |
1509 |
+ * @param array Hash array with task properties: |
1510 |
+ * id: Task identifier |
1511 |
+ * @return boolean True on success, False on error |
1512 |
+ */ |
1513 |
+ public function undelete_task($prop) |
1514 |
+ { |
1515 |
+ // TODO: implement this |
1516 |
+ return false; |
1517 |
+ } |
1518 |
+ |
1519 |
+ |
1520 |
+ /** |
1521 |
+ * Get attachment properties |
1522 |
+ * |
1523 |
+ * @param string $id Attachment identifier |
1524 |
+ * @param array $task Hash array with event properties: |
1525 |
+ * id: Task identifier |
1526 |
+ * list: List identifier |
1527 |
+ * |
1528 |
+ * @return array Hash array with attachment properties: |
1529 |
+ * id: Attachment identifier |
1530 |
+ * name: Attachment name |
1531 |
+ * mimetype: MIME content type of the attachment |
1532 |
+ * size: Attachment size |
1533 |
+ */ |
1534 |
+ public function get_attachment($id, $task) |
1535 |
+ { |
1536 |
+ $task['uid'] = $task['id']; |
1537 |
+ $task = $this->get_task($task); |
1538 |
+ |
1539 |
+ if ($task && !empty($task['attachments'])) { |
1540 |
+ foreach ($task['attachments'] as $att) { |
1541 |
+ if ($att['id'] == $id) |
1542 |
+ return $att; |
1543 |
+ } |
1544 |
+ } |
1545 |
+ |
1546 |
+ return null; |
1547 |
+ } |
1548 |
+ |
1549 |
+ /** |
1550 |
+ * Get attachment body |
1551 |
+ * |
1552 |
+ * @param string $id Attachment identifier |
1553 |
+ * @param array $task Hash array with event properties: |
1554 |
+ * id: Task identifier |
1555 |
+ * list: List identifier |
1556 |
+ * |
1557 |
+ * @return string Attachment body |
1558 |
+ */ |
1559 |
+ public function get_attachment_body($id, $task) |
1560 |
+ { |
1561 |
+ if ($storage = $this->folders[$task['list']]) { |
1562 |
+ return $storage->get_attachment($task['id'], $id); |
1563 |
+ } |
1564 |
+ |
1565 |
+ return false; |
1566 |
+ } |
1567 |
+ |
1568 |
+ /** |
1569 |
+ * |
1570 |
+ */ |
1571 |
+ public function tasklist_edit_form($fieldprop) |
1572 |
+ { |
1573 |
+ $select = kolab_storage::folder_selector('task', array('name' => 'parent', 'id' => 'taskedit-parentfolder'), null); |
1574 |
+ $fieldprop['parent'] = array( |
1575 |
+ 'id' => 'taskedit-parentfolder', |
1576 |
+ 'label' => $this->plugin->gettext('parentfolder'), |
1577 |
+ 'value' => $select->show(''), |
1578 |
+ ); |
1579 |
+ |
1580 |
+ $formfields = array(); |
1581 |
+ foreach (array('name','parent','showalarms') as $f) { |
1582 |
+ $formfields[$f] = $fieldprop[$f]; |
1583 |
+ } |
1584 |
+ |
1585 |
+ return parent::tasklist_edit_form($formfields); |
1586 |
+ } |
1587 |
+ |
1588 |
+} |
1589 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/tasklist_driver.php roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/tasklist_driver.php |
1590 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/drivers/tasklist_driver.php 1970-01-01 01:00:00.000000000 +0100 |
1591 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/drivers/tasklist_driver.php 2013-11-24 00:38:51.000000000 +0100 |
1592 |
@@ -0,0 +1,278 @@ |
1593 |
+<?php |
1594 |
+ |
1595 |
+/** |
1596 |
+ * Driver interface for the Tasklist plugin |
1597 |
+ * |
1598 |
+ * @version @package_version@ |
1599 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
1600 |
+ * |
1601 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
1602 |
+ * |
1603 |
+ * This program is free software: you can redistribute it and/or modify |
1604 |
+ * it under the terms of the GNU Affero General Public License as |
1605 |
+ * published by the Free Software Foundation, either version 3 of the |
1606 |
+ * License, or (at your option) any later version. |
1607 |
+ * |
1608 |
+ * This program is distributed in the hope that it will be useful, |
1609 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1610 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1611 |
+ * GNU Affero General Public License for more details. |
1612 |
+ * |
1613 |
+ * You should have received a copy of the GNU Affero General Public License |
1614 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1615 |
+ */ |
1616 |
+ |
1617 |
+ /** |
1618 |
+ * Struct of an internal task object how it is passed from/to the driver classes: |
1619 |
+ * |
1620 |
+ * $task = array( |
1621 |
+ * 'id' => 'Task ID used for editing', // must be unique for the current user |
1622 |
+ * 'parent_id' => 'ID of parent task', // null if top-level task |
1623 |
+ * 'uid' => 'Unique identifier of this task', |
1624 |
+ * 'list' => 'Task list identifier to add the task to or where the task is stored', |
1625 |
+ * 'changed' => <DateTime>, // Last modification date/time of the record |
1626 |
+ * 'title' => 'Event title/summary', |
1627 |
+ * 'description' => 'Event description', |
1628 |
+ * 'tags' => array(), // List of tags for this task |
1629 |
+ * 'date' => 'Due date', // as string of format YYYY-MM-DD or null if no date is set |
1630 |
+ * 'time' => 'Due time', // as string of format hh::ii or null if no due time is set |
1631 |
+ * 'startdate' => 'Start date' // Delay start of the task until that date |
1632 |
+ * 'starttime' => 'Start time' // ...and time |
1633 |
+ * 'categories' => 'Task category', |
1634 |
+ * 'flagged' => 'Boolean value whether this record is flagged', |
1635 |
+ * 'complete' => 'Float value representing the completeness state (range 0..1)', |
1636 |
+ * 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential) |
1637 |
+ * 'alarms' => '-15M:DISPLAY', // Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before due time) |
1638 |
+ * '_fromlist' => 'List identifier where the task was stored before', |
1639 |
+ * ); |
1640 |
+ */ |
1641 |
+ |
1642 |
+/** |
1643 |
+ * Driver interface for the Tasklist plugin |
1644 |
+ */ |
1645 |
+abstract class tasklist_driver |
1646 |
+{ |
1647 |
+ // features supported by the backend |
1648 |
+ public $alarms = false; |
1649 |
+ public $attachments = false; |
1650 |
+ public $undelete = false; // task undelete action |
1651 |
+ public $sortable = false; |
1652 |
+ public $alarm_types = array('DISPLAY'); |
1653 |
+ public $alarm_absolute = true; |
1654 |
+ public $last_error; |
1655 |
+ |
1656 |
+ /** |
1657 |
+ * Get a list of available task lists from this source |
1658 |
+ */ |
1659 |
+ abstract function get_lists(); |
1660 |
+ |
1661 |
+ /** |
1662 |
+ * Create a new list assigned to the current user |
1663 |
+ * |
1664 |
+ * @param array Hash array with list properties |
1665 |
+ * name: List name |
1666 |
+ * color: The color of the list |
1667 |
+ * showalarms: True if alarms are enabled |
1668 |
+ * @return mixed ID of the new list on success, False on error |
1669 |
+ */ |
1670 |
+ abstract function create_list($prop); |
1671 |
+ |
1672 |
+ /** |
1673 |
+ * Update properties of an existing tasklist |
1674 |
+ * |
1675 |
+ * @param array Hash array with list properties |
1676 |
+ * id: List Identifier |
1677 |
+ * name: List name |
1678 |
+ * color: The color of the list |
1679 |
+ * showalarms: True if alarms are enabled (if supported) |
1680 |
+ * @return boolean True on success, Fales on failure |
1681 |
+ */ |
1682 |
+ abstract function edit_list($prop); |
1683 |
+ |
1684 |
+ /** |
1685 |
+ * Set active/subscribed state of a list |
1686 |
+ * |
1687 |
+ * @param array Hash array with list properties |
1688 |
+ * id: List Identifier |
1689 |
+ * active: True if list is active, false if not |
1690 |
+ * @return boolean True on success, Fales on failure |
1691 |
+ */ |
1692 |
+ abstract function subscribe_list($prop); |
1693 |
+ |
1694 |
+ /** |
1695 |
+ * Delete the given list with all its contents |
1696 |
+ * |
1697 |
+ * @param array Hash array with list properties |
1698 |
+ * id: list Identifier |
1699 |
+ * @return boolean True on success, Fales on failure |
1700 |
+ */ |
1701 |
+ abstract function remove_list($prop); |
1702 |
+ |
1703 |
+ /** |
1704 |
+ * Get number of tasks matching the given filter |
1705 |
+ * |
1706 |
+ * @param array List of lists to count tasks of |
1707 |
+ * @return array Hash array with counts grouped by status (all|flagged|completed|today|tomorrow|nodate) |
1708 |
+ */ |
1709 |
+ abstract function count_tasks($lists = null); |
1710 |
+ |
1711 |
+ /** |
1712 |
+ * Get all taks records matching the given filter |
1713 |
+ * |
1714 |
+ * @param array Hash array with filter criterias: |
1715 |
+ * - mask: Bitmask representing the filter selection (check against tasklist::FILTER_MASK_* constants) |
1716 |
+ * - from: Date range start as string (Y-m-d) |
1717 |
+ * - to: Date range end as string (Y-m-d) |
1718 |
+ * - search: Search query string |
1719 |
+ * @param array List of lists to get tasks from |
1720 |
+ * @return array List of tasks records matchin the criteria |
1721 |
+ */ |
1722 |
+ abstract function list_tasks($filter, $lists = null); |
1723 |
+ |
1724 |
+ /** |
1725 |
+ * Get a list of pending alarms to be displayed to the user |
1726 |
+ * |
1727 |
+ * @param integer Current time (unix timestamp) |
1728 |
+ * @param mixed List of list IDs to show alarms for (either as array or comma-separated string) |
1729 |
+ * @return array A list of alarms, each encoded as hash array with task properties |
1730 |
+ * id: Task identifier |
1731 |
+ * uid: Unique identifier of this task |
1732 |
+ * date: Task due date |
1733 |
+ * time: Task due time |
1734 |
+ * title: Task title/summary |
1735 |
+ */ |
1736 |
+ abstract function pending_alarms($time, $lists = null); |
1737 |
+ |
1738 |
+ /** |
1739 |
+ * (User) feedback after showing an alarm notification |
1740 |
+ * This should mark the alarm as 'shown' or snooze it for the given amount of time |
1741 |
+ * |
1742 |
+ * @param string Task identifier |
1743 |
+ * @param integer Suspend the alarm for this number of seconds |
1744 |
+ */ |
1745 |
+ abstract function dismiss_alarm($id, $snooze = 0); |
1746 |
+ |
1747 |
+ /** |
1748 |
+ * Return data of a specific task |
1749 |
+ * |
1750 |
+ * @param mixed Hash array with task properties or task UID |
1751 |
+ * @return array Hash array with task properties or false if not found |
1752 |
+ */ |
1753 |
+ abstract public function get_task($prop); |
1754 |
+ |
1755 |
+ /** |
1756 |
+ * Get decendents of the given task record |
1757 |
+ * |
1758 |
+ * @param mixed Hash array with task properties or task UID |
1759 |
+ * @param boolean True if all childrens children should be fetched |
1760 |
+ * @return array List of all child task IDs |
1761 |
+ */ |
1762 |
+ abstract public function get_childs($prop, $recursive = false); |
1763 |
+ |
1764 |
+ /** |
1765 |
+ * Add a single task to the database |
1766 |
+ * |
1767 |
+ * @param array Hash array with task properties (see header of this file) |
1768 |
+ * @return mixed New event ID on success, False on error |
1769 |
+ */ |
1770 |
+ abstract function create_task($prop); |
1771 |
+ |
1772 |
+ /** |
1773 |
+ * Update an task entry with the given data |
1774 |
+ * |
1775 |
+ * @param array Hash array with task properties (see header of this file) |
1776 |
+ * @return boolean True on success, False on error |
1777 |
+ */ |
1778 |
+ abstract function edit_task($prop); |
1779 |
+ |
1780 |
+ /** |
1781 |
+ * Move a single task to another list |
1782 |
+ * |
1783 |
+ * @param array Hash array with task properties: |
1784 |
+ * id: Task identifier |
1785 |
+ * list: New list identifier to move to |
1786 |
+ * _fromlist: Previous list identifier |
1787 |
+ * @return boolean True on success, False on error |
1788 |
+ */ |
1789 |
+ abstract function move_task($prop); |
1790 |
+ |
1791 |
+ /** |
1792 |
+ * Remove a single task from the database |
1793 |
+ * |
1794 |
+ * @param array Hash array with task properties: |
1795 |
+ * id: Task identifier |
1796 |
+ * @param boolean Remove record irreversible (mark as deleted otherwise, if supported by the backend) |
1797 |
+ * @return boolean True on success, False on error |
1798 |
+ */ |
1799 |
+ abstract function delete_task($prop, $force = true); |
1800 |
+ |
1801 |
+ /** |
1802 |
+ * Restores a single deleted task (if supported) |
1803 |
+ * |
1804 |
+ * @param array Hash array with task properties: |
1805 |
+ * id: Task identifier |
1806 |
+ * @return boolean True on success, False on error |
1807 |
+ */ |
1808 |
+ public function undelete_task($prop) |
1809 |
+ { |
1810 |
+ return false; |
1811 |
+ } |
1812 |
+ |
1813 |
+ /** |
1814 |
+ * Get attachment properties |
1815 |
+ * |
1816 |
+ * @param string $id Attachment identifier |
1817 |
+ * @param array $task Hash array with event properties: |
1818 |
+ * id: Task identifier |
1819 |
+ * list: List identifier |
1820 |
+ * |
1821 |
+ * @return array Hash array with attachment properties: |
1822 |
+ * id: Attachment identifier |
1823 |
+ * name: Attachment name |
1824 |
+ * mimetype: MIME content type of the attachment |
1825 |
+ * size: Attachment size |
1826 |
+ */ |
1827 |
+ public function get_attachment($id, $task) { } |
1828 |
+ |
1829 |
+ /** |
1830 |
+ * Get attachment body |
1831 |
+ * |
1832 |
+ * @param string $id Attachment identifier |
1833 |
+ * @param array $task Hash array with event properties: |
1834 |
+ * id: Task identifier |
1835 |
+ * list: List identifier |
1836 |
+ * |
1837 |
+ * @return string Attachment body |
1838 |
+ */ |
1839 |
+ public function get_attachment_body($id, $task) { } |
1840 |
+ |
1841 |
+ /** |
1842 |
+ * List availabale categories |
1843 |
+ * The default implementation reads them from config/user prefs |
1844 |
+ */ |
1845 |
+ public function list_categories() |
1846 |
+ { |
1847 |
+ $rcmail = rcube::get_instance(); |
1848 |
+ return $rcmail->config->get('tasklist_categories', array()); |
1849 |
+ } |
1850 |
+ |
1851 |
+ /** |
1852 |
+ * Build the edit/create form for lists. |
1853 |
+ * This gives the drivers the opportunity to add more list properties |
1854 |
+ * |
1855 |
+ * @param array List with form fields to be rendered |
1856 |
+ * @return string HTML content of the form |
1857 |
+ */ |
1858 |
+ public function tasklist_edit_form($formfields) |
1859 |
+ { |
1860 |
+ $html = ''; |
1861 |
+ foreach ($formfields as $field) { |
1862 |
+ $html .= html::div('form-section', |
1863 |
+ html::label($field['id'], $field['label']) . |
1864 |
+ $field['value']); |
1865 |
+ } |
1866 |
+ |
1867 |
+ return $html; |
1868 |
+ } |
1869 |
+ |
1870 |
+} |
1871 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/.gitignore roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/.gitignore |
1872 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/.gitignore 1970-01-01 01:00:00.000000000 +0100 |
1873 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/.gitignore 2013-11-24 00:38:51.000000000 +0100 |
1874 |
@@ -0,0 +1 @@ |
1875 |
+config.inc.php |
1876 |
\ Pas de fin de ligne à la fin du fichier. |
1877 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/jquery.tagedit.js roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/jquery.tagedit.js |
1878 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/jquery.tagedit.js 1970-01-01 01:00:00.000000000 +0100 |
1879 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/jquery.tagedit.js 2013-11-24 00:38:51.000000000 +0100 |
1880 |
@@ -0,0 +1,535 @@ |
1881 |
+/* |
1882 |
+* Tagedit - jQuery Plugin |
1883 |
+* The Plugin can be used to edit tags from a database the easy way |
1884 |
+* |
1885 |
+* Examples and documentation at: tagedit.webwork-albrecht.de |
1886 |
+* |
1887 |
+* Copyright (c) 2010 Oliver Albrecht <info@webwork-albrecht.de> |
1888 |
+* |
1889 |
+* License: |
1890 |
+* This work is licensed under a MIT License |
1891 |
+* http://www.opensource.org/licenses/mit-license.php |
1892 |
+* |
1893 |
+* @author Oliver Albrecht Mial: info@webwork-albrecht.de Twitter: @webworka |
1894 |
+* @version 1.2.1 (11/2011) |
1895 |
+* Requires: jQuery v1.4+, jQueryUI v1.8+, jQuerry.autoGrowInput |
1896 |
+* |
1897 |
+* Example of usage: |
1898 |
+* |
1899 |
+* $( "input.tag" ).tagedit(); |
1900 |
+* |
1901 |
+* Possible options: |
1902 |
+* |
1903 |
+* autocompleteURL: '', // url for a autocompletion |
1904 |
+* deleteEmptyItems: true, // Deletes items with empty value |
1905 |
+* deletedPostfix: '-d', // will be put to the Items that are marked as delete |
1906 |
+* addedPostfix: '-a', // will be put to the Items that are choosem from the database |
1907 |
+* additionalListClass: '', // put a classname here if the wrapper ul shoud receive a special class |
1908 |
+* allowEdit: true, // Switch on/off edit entries |
1909 |
+* allowDelete: true, // Switch on/off deletion of entries. Will be ignored if allowEdit = false |
1910 |
+* allowAdd: true, // switch on/off the creation of new entries |
1911 |
+* direction: 'ltr' // Sets the writing direction for Outputs and Inputs |
1912 |
+* animSpeed: 500 // Sets the animation speed for effects |
1913 |
+* autocompleteOptions: {}, // Setting Options for the jquery UI Autocomplete (http://jqueryui.com/demos/autocomplete/) |
1914 |
+* breakKeyCodes: [ 13, 44 ], // Sets the characters to break on to parse the tags (defaults: return, comma) |
1915 |
+* checkNewEntriesCaseSensitive: false, // If there is a new Entry, it is checked against the autocompletion list. This Flag controlls if the check is (in-)casesensitive |
1916 |
+* texts: { // some texts |
1917 |
+* removeLinkTitle: 'Remove from list.', |
1918 |
+* saveEditLinkTitle: 'Save changes.', |
1919 |
+* deleteLinkTitle: 'Delete this tag from database.', |
1920 |
+* deleteConfirmation: 'Are you sure to delete this entry?', |
1921 |
+* deletedElementTitle: 'This Element will be deleted.', |
1922 |
+* breakEditLinkTitle: 'Cancel' |
1923 |
+* } |
1924 |
+*/ |
1925 |
+ |
1926 |
+(function($) { |
1927 |
+ |
1928 |
+ $.fn.tagedit = function(options) { |
1929 |
+ /** |
1930 |
+ * Merge Options with defaults |
1931 |
+ */ |
1932 |
+ options = $.extend(true, { |
1933 |
+ // default options here |
1934 |
+ autocompleteURL: null, |
1935 |
+ deletedPostfix: '-d', |
1936 |
+ addedPostfix: '-a', |
1937 |
+ additionalListClass: '', |
1938 |
+ allowEdit: true, |
1939 |
+ allowDelete: true, |
1940 |
+ allowAdd: true, |
1941 |
+ direction: 'ltr', |
1942 |
+ animSpeed: 500, |
1943 |
+ autocompleteOptions: { |
1944 |
+ select: function( event, ui ) { |
1945 |
+ $(this).val(ui.item.value).trigger('transformToTag', [ui.item.id]); |
1946 |
+ return false; |
1947 |
+ } |
1948 |
+ }, |
1949 |
+ breakKeyCodes: [ 13, 44 ], |
1950 |
+ checkNewEntriesCaseSensitive: false, |
1951 |
+ texts: { |
1952 |
+ removeLinkTitle: 'Remove from list.', |
1953 |
+ saveEditLinkTitle: 'Save changes.', |
1954 |
+ deleteLinkTitle: 'Delete this tag from database.', |
1955 |
+ deleteConfirmation: 'Are you sure to delete this entry?', |
1956 |
+ deletedElementTitle: 'This Element will be deleted.', |
1957 |
+ breakEditLinkTitle: 'Cancel' |
1958 |
+ }, |
1959 |
+ tabindex: false |
1960 |
+ }, options || {}); |
1961 |
+ |
1962 |
+ // no action if there are no elements |
1963 |
+ if(this.length == 0) { |
1964 |
+ return; |
1965 |
+ } |
1966 |
+ |
1967 |
+ // set the autocompleteOptions source |
1968 |
+ if(options.autocompleteURL) { |
1969 |
+ options.autocompleteOptions.source = options.autocompleteURL; |
1970 |
+ } |
1971 |
+ |
1972 |
+ // Set the direction of the inputs |
1973 |
+ var direction= this.attr('dir'); |
1974 |
+ if(direction && direction.length > 0) { |
1975 |
+ options.direction = this.attr('dir'); |
1976 |
+ } |
1977 |
+ |
1978 |
+ var elements = this; |
1979 |
+ |
1980 |
+ var baseNameRegexp = new RegExp("^(.*)\\[([0-9]*?("+options.deletedPostfix+"|"+options.addedPostfix+")?)?\]$", "i"); |
1981 |
+ |
1982 |
+ var baseName = elements.eq(0).attr('name').match(baseNameRegexp); |
1983 |
+ if(baseName && baseName.length == 4) { |
1984 |
+ baseName = baseName[1]; |
1985 |
+ } |
1986 |
+ else { |
1987 |
+ // Elementname does not match the expected format, exit |
1988 |
+ alert('elementname dows not match the expected format (regexp: '+baseNameRegexp+')') |
1989 |
+ return; |
1990 |
+ } |
1991 |
+ |
1992 |
+ // read tabindex from source element |
1993 |
+ var ti; |
1994 |
+ if (!options.tabindex && (ti = elements.eq(0).attr('tabindex'))) |
1995 |
+ options.tabindex = ti; |
1996 |
+ |
1997 |
+ // init elements |
1998 |
+ inputsToList(); |
1999 |
+ |
2000 |
+ /** |
2001 |
+ * Creates the tageditinput from a list of textinputs |
2002 |
+ * |
2003 |
+ */ |
2004 |
+ function inputsToList() { |
2005 |
+ var html = '<ul class="tagedit-list '+options.additionalListClass+'">'; |
2006 |
+ |
2007 |
+ elements.each(function() { |
2008 |
+ var element_name = $(this).attr('name').match(baseNameRegexp); |
2009 |
+ if(element_name && element_name.length == 4 && (options.deleteEmptyItems == false || $(this).val().length > 0)) { |
2010 |
+ if(element_name[1].length > 0) { |
2011 |
+ var elementId = typeof element_name[2] != 'undefined'? element_name[2]: ''; |
2012 |
+ |
2013 |
+ html += '<li class="tagedit-listelement tagedit-listelement-old">'; |
2014 |
+ html += '<span dir="'+options.direction+'">' + $(this).val() + '</span>'; |
2015 |
+ html += '<input type="hidden" name="'+baseName+'['+elementId+']" value="'+$(this).val()+'" />'; |
2016 |
+ html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'">x</a>'; |
2017 |
+ html += '</li>'; |
2018 |
+ } |
2019 |
+ } |
2020 |
+ }); |
2021 |
+ |
2022 |
+ // replace Elements with the list and save the list in the local variable elements |
2023 |
+ elements.last().after(html) |
2024 |
+ var newList = elements.last().next(); |
2025 |
+ elements.remove(); |
2026 |
+ elements = newList; |
2027 |
+ |
2028 |
+ // Check if some of the elementshav to be marked as deleted |
2029 |
+ if(options.deletedPostfix.length > 0) { |
2030 |
+ elements.find('input[name$="'+options.deletedPostfix+'\]"]').each(function() { |
2031 |
+ markAsDeleted($(this).parent()); |
2032 |
+ }); |
2033 |
+ } |
2034 |
+ |
2035 |
+ // put an input field at the End |
2036 |
+ // Put an empty element at the end |
2037 |
+ html = '<li class="tagedit-listelement tagedit-listelement-new">'; |
2038 |
+ html += '<input type="text" name="'+baseName+'[]" value="" id="tagedit-input" disabled="disabled" class="tagedit-input-disabled" dir="'+options.direction+'"/>'; |
2039 |
+ html += '</li>'; |
2040 |
+ html += '</ul>'; |
2041 |
+ |
2042 |
+ elements |
2043 |
+ .append(html) |
2044 |
+ .attr('tabindex', options.tabindex) // set tabindex to <ul> to recieve focus |
2045 |
+ |
2046 |
+ // Set function on the input |
2047 |
+ .find('#tagedit-input') |
2048 |
+ .attr('tabindex', options.tabindex) |
2049 |
+ .each(function() { |
2050 |
+ $(this).autoGrowInput({comfortZone: 15, minWidth: 15, maxWidth: 20000}); |
2051 |
+ |
2052 |
+ // Event ist triggert in case of choosing an item from the autocomplete, or finish the input |
2053 |
+ $(this).bind('transformToTag', function(event, id) { |
2054 |
+ var oldValue = (typeof id != 'undefined' && id.length > 0); |
2055 |
+ |
2056 |
+ var checkAutocomplete = oldValue == true? false : true; |
2057 |
+ // check if the Value ist new |
2058 |
+ var isNewResult = isNew($(this).val(), checkAutocomplete); |
2059 |
+ if(isNewResult[0] === true || isNewResult[1] != null) { |
2060 |
+ |
2061 |
+ if(oldValue == false && isNewResult[1] != null) { |
2062 |
+ oldValue = true; |
2063 |
+ id = isNewResult[1]; |
2064 |
+ } |
2065 |
+ |
2066 |
+ if(options.allowAdd == true || oldValue) { |
2067 |
+ // Make a new tag in front the input |
2068 |
+ html = '<li class="tagedit-listelement tagedit-listelement-old">'; |
2069 |
+ html += '<span dir="'+options.direction+'">' + $(this).val() + '</span>'; |
2070 |
+ var name = oldValue? baseName + '['+id+options.addedPostfix+']' : baseName + '[]'; |
2071 |
+ html += '<input type="hidden" name="'+name+'" value="'+$(this).val()+'" />'; |
2072 |
+ html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'">x</a>'; |
2073 |
+ html += '</li>'; |
2074 |
+ |
2075 |
+ $(this).parent().before(html); |
2076 |
+ } |
2077 |
+ } |
2078 |
+ $(this).val(''); |
2079 |
+ |
2080 |
+ // close autocomplete |
2081 |
+ if(options.autocompleteOptions.source) { |
2082 |
+ $(this).autocomplete( "close" ); |
2083 |
+ } |
2084 |
+ |
2085 |
+ }) |
2086 |
+ .keydown(function(event) { |
2087 |
+ var code = event.keyCode > 0? event.keyCode : event.which; |
2088 |
+ |
2089 |
+ switch(code) { |
2090 |
+ case 8: // BACKSPACE |
2091 |
+ if($(this).val().length == 0) { |
2092 |
+ // delete Last Tag |
2093 |
+ var elementToRemove = elements.find('li.tagedit-listelement-old').last(); |
2094 |
+ elementToRemove.fadeOut(options.animSpeed, function() {elementToRemove.remove();}) |
2095 |
+ event.preventDefault(); |
2096 |
+ return false; |
2097 |
+ } |
2098 |
+ break; |
2099 |
+ case 9: // TAB |
2100 |
+ if($(this).val().length > 0 && $('ul.ui-autocomplete #ui-active-menuitem').length == 0) { |
2101 |
+ $(this).trigger('transformToTag'); |
2102 |
+ event.preventDefault(); |
2103 |
+ return false; |
2104 |
+ } |
2105 |
+ break; |
2106 |
+ } |
2107 |
+ return true; |
2108 |
+ }) |
2109 |
+ .keypress(function(event) { |
2110 |
+ var code = event.keyCode > 0? event.keyCode : event.which; |
2111 |
+ if($.inArray(code, options.breakKeyCodes) > -1) { |
2112 |
+ if($(this).val().length > 0 && $('ul.ui-autocomplete #ui-active-menuitem').length == 0) { |
2113 |
+ $(this).trigger('transformToTag'); |
2114 |
+ } |
2115 |
+ event.preventDefault(); |
2116 |
+ return false; |
2117 |
+ } |
2118 |
+ return true; |
2119 |
+ }) |
2120 |
+ .bind('paste', function(e){ |
2121 |
+ var that = $(this); |
2122 |
+ if (e.type == 'paste'){ |
2123 |
+ setTimeout(function(){ |
2124 |
+ that.trigger('transformToTag'); |
2125 |
+ }, 1); |
2126 |
+ } |
2127 |
+ }) |
2128 |
+ .blur(function() { |
2129 |
+ if($(this).val().length == 0) { |
2130 |
+ // disable the field to prevent sending with the form |
2131 |
+ $(this).attr('disabled', 'disabled').addClass('tagedit-input-disabled'); |
2132 |
+ } |
2133 |
+ else { |
2134 |
+ // Delete entry after a timeout |
2135 |
+ var input = $(this); |
2136 |
+ $(this).data('blurtimer', window.setTimeout(function() {input.val('');}, 500)); |
2137 |
+ } |
2138 |
+ }) |
2139 |
+ .focus(function() { |
2140 |
+ window.clearTimeout($(this).data('blurtimer')); |
2141 |
+ }); |
2142 |
+ |
2143 |
+ if(options.autocompleteOptions.source != false) { |
2144 |
+ $(this).autocomplete(options.autocompleteOptions); |
2145 |
+ } |
2146 |
+ }) |
2147 |
+ .end() |
2148 |
+ .click(function(event) { |
2149 |
+ switch(event.target.tagName) { |
2150 |
+ case 'A': |
2151 |
+ $(event.target).parent().fadeOut(options.animSpeed, function() { |
2152 |
+ $(event.target).parent().remove(); |
2153 |
+ }); |
2154 |
+ break; |
2155 |
+ case 'INPUT': |
2156 |
+ case 'SPAN': |
2157 |
+ case 'LI': |
2158 |
+ if($(event.target).hasClass('tagedit-listelement-deleted') == false && |
2159 |
+ $(event.target).parent('li').hasClass('tagedit-listelement-deleted') == false) { |
2160 |
+ // Don't edit an deleted Items |
2161 |
+ return doEdit(event); |
2162 |
+ } |
2163 |
+ default: |
2164 |
+ $(this).find('#tagedit-input') |
2165 |
+ .removeAttr('disabled') |
2166 |
+ .removeClass('tagedit-input-disabled') |
2167 |
+ .focus(); |
2168 |
+ } |
2169 |
+ return false; |
2170 |
+ }) |
2171 |
+ // forward focus event |
2172 |
+ .focus(function(e){ $(this).click(); }) |
2173 |
+ } |
2174 |
+ |
2175 |
+ /** |
2176 |
+ * Sets all Actions and events for editing an Existing Tag. |
2177 |
+ * |
2178 |
+ * @param event {object} The original Event that was given |
2179 |
+ * return {boolean} |
2180 |
+ */ |
2181 |
+ function doEdit(event) { |
2182 |
+ if(options.allowEdit == false) { |
2183 |
+ // Do nothing |
2184 |
+ return; |
2185 |
+ } |
2186 |
+ |
2187 |
+ var element = event.target.tagName == 'SPAN'? $(event.target).parent() : $(event.target); |
2188 |
+ |
2189 |
+ var closeTimer = null; |
2190 |
+ |
2191 |
+ // Event that is fired if the User finishes the edit of a tag |
2192 |
+ element.bind('finishEdit', function(event, doReset) { |
2193 |
+ window.clearTimeout(closeTimer); |
2194 |
+ |
2195 |
+ var textfield = $(this).find(':text'); |
2196 |
+ var isNewResult = isNew(textfield.val(), true); |
2197 |
+ if(textfield.val().length > 0 && (typeof doReset == 'undefined' || doReset === false) && (isNewResult[0] == true)) { |
2198 |
+ // This is a new Value and we do not want to do a reset. Set the new value |
2199 |
+ $(this).find(':hidden').val(textfield.val()); |
2200 |
+ $(this).find('span').html(textfield.val()); |
2201 |
+ } |
2202 |
+ |
2203 |
+ textfield.remove(); |
2204 |
+ $(this).find('a.tagedit-save, a.tagedit-break, a.tagedit-delete, tester').remove(); // Workaround. This normaly has to be done by autogrow Plugin |
2205 |
+ $(this).removeClass('tagedit-listelement-edit').unbind('finishEdit'); |
2206 |
+ return false; |
2207 |
+ }); |
2208 |
+ |
2209 |
+ var hidden = element.find(':hidden'); |
2210 |
+ html = '<input type="text" name="tmpinput" autocomplete="off" value="'+hidden.val()+'" class="tagedit-edit-input" dir="'+options.direction+'"/>'; |
2211 |
+ html += '<a class="tagedit-save" title="'+options.texts.saveEditLinkTitle+'">o</a>'; |
2212 |
+ html += '<a class="tagedit-break" title="'+options.texts.breakEditLinkTitle+'">x</a>'; |
2213 |
+ |
2214 |
+ // If the Element is one from the Database, it can be deleted |
2215 |
+ if(options.allowDelete == true && element.find(':hidden').length > 0 && |
2216 |
+ typeof element.find(':hidden').attr('name').match(baseNameRegexp)[3] != 'undefined') { |
2217 |
+ html += '<a class="tagedit-delete" title="'+options.texts.deleteLinkTitle+'">d</a>'; |
2218 |
+ } |
2219 |
+ |
2220 |
+ hidden.after(html); |
2221 |
+ element |
2222 |
+ .addClass('tagedit-listelement-edit') |
2223 |
+ .find('a.tagedit-save') |
2224 |
+ .click(function() { |
2225 |
+ $(this).parent().trigger('finishEdit'); |
2226 |
+ return false; |
2227 |
+ }) |
2228 |
+ .end() |
2229 |
+ .find('a.tagedit-break') |
2230 |
+ .click(function() { |
2231 |
+ $(this).parent().trigger('finishEdit', [true]); |
2232 |
+ return false; |
2233 |
+ }) |
2234 |
+ .end() |
2235 |
+ .find('a.tagedit-delete') |
2236 |
+ .click(function() { |
2237 |
+ window.clearTimeout(closeTimer); |
2238 |
+ if(confirm(options.texts.deleteConfirmation)) { |
2239 |
+ markAsDeleted($(this).parent()); |
2240 |
+ } |
2241 |
+ else { |
2242 |
+ $(this).parent().find(':text').trigger('finishEdit', [true]); |
2243 |
+ } |
2244 |
+ return false; |
2245 |
+ }) |
2246 |
+ .end() |
2247 |
+ .find(':text') |
2248 |
+ .focus() |
2249 |
+ .autoGrowInput({comfortZone: 10, minWidth: 15, maxWidth: 20000}) |
2250 |
+ .keypress(function(event) { |
2251 |
+ switch(event.keyCode) { |
2252 |
+ case 13: // RETURN |
2253 |
+ event.preventDefault(); |
2254 |
+ $(this).parent().trigger('finishEdit'); |
2255 |
+ return false; |
2256 |
+ case 27: // ESC |
2257 |
+ event.preventDefault(); |
2258 |
+ $(this).parent().trigger('finishEdit', [true]); |
2259 |
+ return false; |
2260 |
+ } |
2261 |
+ return true; |
2262 |
+ }) |
2263 |
+ .blur(function() { |
2264 |
+ var that = $(this); |
2265 |
+ closeTimer = window.setTimeout(function() {that.parent().trigger('finishEdit', [true])}, 500); |
2266 |
+ }); |
2267 |
+ } |
2268 |
+ |
2269 |
+ /** |
2270 |
+ * Marks a single Tag as deleted. |
2271 |
+ * |
2272 |
+ * @param element {object} |
2273 |
+ */ |
2274 |
+ function markAsDeleted(element) { |
2275 |
+ element |
2276 |
+ .trigger('finishEdit', [true]) |
2277 |
+ .addClass('tagedit-listelement-deleted') |
2278 |
+ .attr('title', options.deletedElementTitle); |
2279 |
+ element.find(':hidden').each(function() { |
2280 |
+ var nameEndRegexp = new RegExp('('+options.addedPostfix+'|'+options.deletedPostfix+')?\]'); |
2281 |
+ var name = $(this).attr('name').replace(nameEndRegexp, options.deletedPostfix+']'); |
2282 |
+ $(this).attr('name', name); |
2283 |
+ }); |
2284 |
+ |
2285 |
+ } |
2286 |
+ |
2287 |
+ /** |
2288 |
+ * Checks if a tag is already choosen. |
2289 |
+ * |
2290 |
+ * @param value {string} |
2291 |
+ * @param checkAutocomplete {boolean} optional Check also the autocomplet values |
2292 |
+ * @returns {Array} First item is a boolean, telling if the item should be put to the list, second is optional the ID from autocomplete list |
2293 |
+ */ |
2294 |
+ function isNew(value, checkAutocomplete) { |
2295 |
+ checkAutocomplete = typeof checkAutocomplete == 'undefined'? false : checkAutocomplete; |
2296 |
+ var autoCompleteId = null; |
2297 |
+ |
2298 |
+ var compareValue = options.checkNewEntriesCaseSensitive == true? value : value.toLowerCase(); |
2299 |
+ |
2300 |
+ var isNew = true; |
2301 |
+ elements.find('li.tagedit-listelement-old input:hidden').each(function() { |
2302 |
+ var elementValue = options.checkNewEntriesCaseSensitive == true? $(this).val() : $(this).val().toLowerCase(); |
2303 |
+ if(elementValue == compareValue) { |
2304 |
+ isNew = false; |
2305 |
+ } |
2306 |
+ }); |
2307 |
+ |
2308 |
+ if (isNew == true && checkAutocomplete == true && options.autocompleteOptions.source != false) { |
2309 |
+ var result = []; |
2310 |
+ if ($.isArray(options.autocompleteOptions.source)) { |
2311 |
+ result = options.autocompleteOptions.source; |
2312 |
+ } |
2313 |
+ else if ($.isFunction(options.autocompleteOptions.source)) { |
2314 |
+ options.autocompleteOptions.source({term: value}, function (data) {result = data}); |
2315 |
+ } |
2316 |
+ else if (typeof options.autocompleteOptions.source === "string") { |
2317 |
+ // Check also autocomplete values |
2318 |
+ var autocompleteURL = options.autocompleteOptions.source; |
2319 |
+ if (autocompleteURL.match(/\?/)) { |
2320 |
+ autocompleteURL += '&'; |
2321 |
+ } else { |
2322 |
+ autocompleteURL += '?'; |
2323 |
+ } |
2324 |
+ autocompleteURL += 'term=' + value; |
2325 |
+ $.ajax({ |
2326 |
+ async: false, |
2327 |
+ url: autocompleteURL, |
2328 |
+ dataType: 'json', |
2329 |
+ complete: function (XMLHttpRequest, textStatus) { |
2330 |
+ result = $.parseJSON(XMLHttpRequest.responseText); |
2331 |
+ } |
2332 |
+ }); |
2333 |
+ } |
2334 |
+ |
2335 |
+ // If there is an entry for that already in the autocomplete, don't use it (Check could be case sensitive or not) |
2336 |
+ for (var i = 0; i < result.length; i++) { |
2337 |
+ var label = typeof result[i] == 'string' ? result[i] : result[i].label; |
2338 |
+ if (options.checkNewEntriesCaseSensitive == false) |
2339 |
+ label = label.toLowerCase(); |
2340 |
+ if (label == compareValue) { |
2341 |
+ isNew = false; |
2342 |
+ autoCompleteId = typeof result[i] == 'string' ? i : result[i].id; |
2343 |
+ break; |
2344 |
+ } |
2345 |
+ } |
2346 |
+ } |
2347 |
+ |
2348 |
+ return new Array(isNew, autoCompleteId); |
2349 |
+ } |
2350 |
+ } |
2351 |
+})(jQuery); |
2352 |
+ |
2353 |
+(function($){ |
2354 |
+ |
2355 |
+// jQuery autoGrowInput plugin by James Padolsey |
2356 |
+// See related thread: http://stackoverflow.com/questions/931207/is-there-a-jquery-autogrow-plugin-for-text-fields |
2357 |
+ |
2358 |
+$.fn.autoGrowInput = function(o) { |
2359 |
+ |
2360 |
+ o = $.extend({ |
2361 |
+ maxWidth: 1000, |
2362 |
+ minWidth: 0, |
2363 |
+ comfortZone: 70 |
2364 |
+ }, o); |
2365 |
+ |
2366 |
+ this.filter('input:text').each(function(){ |
2367 |
+ |
2368 |
+ var minWidth = o.minWidth || $(this).width(), |
2369 |
+ val = '', |
2370 |
+ input = $(this), |
2371 |
+ testSubject = $('<tester/>').css({ |
2372 |
+ position: 'absolute', |
2373 |
+ top: -9999, |
2374 |
+ left: -9999, |
2375 |
+ width: 'auto', |
2376 |
+ fontSize: input.css('fontSize'), |
2377 |
+ fontFamily: input.css('fontFamily'), |
2378 |
+ fontWeight: input.css('fontWeight'), |
2379 |
+ letterSpacing: input.css('letterSpacing'), |
2380 |
+ whiteSpace: 'nowrap' |
2381 |
+ }), |
2382 |
+ check = function() { |
2383 |
+ |
2384 |
+ if (val === (val = input.val())) {return;} |
2385 |
+ |
2386 |
+ // Enter new content into testSubject |
2387 |
+ var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); |
2388 |
+ testSubject.html(escaped); |
2389 |
+ |
2390 |
+ // Calculate new width + whether to change |
2391 |
+ var testerWidth = testSubject.width(), |
2392 |
+ newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, |
2393 |
+ currentWidth = input.width(), |
2394 |
+ isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth) |
2395 |
+ || (newWidth > minWidth && newWidth < o.maxWidth); |
2396 |
+ |
2397 |
+ // Animate width |
2398 |
+ if (isValidWidthChange) { |
2399 |
+ input.width(newWidth); |
2400 |
+ } |
2401 |
+ |
2402 |
+ }; |
2403 |
+ |
2404 |
+ testSubject.insertAfter(input); |
2405 |
+ |
2406 |
+ $(this).bind('keyup keydown blur update', check); |
2407 |
+ |
2408 |
+ check(); |
2409 |
+ }); |
2410 |
+ |
2411 |
+ return this; |
2412 |
+ |
2413 |
+}; |
2414 |
+ |
2415 |
+})(jQuery); |
2416 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/LICENSE roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/LICENSE |
2417 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/LICENSE 1970-01-01 01:00:00.000000000 +0100 |
2418 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/LICENSE 2013-11-24 00:38:51.000000000 +0100 |
2419 |
@@ -0,0 +1,661 @@ |
2420 |
+ GNU AFFERO GENERAL PUBLIC LICENSE |
2421 |
+ Version 3, 19 November 2007 |
2422 |
+ |
2423 |
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
2424 |
+ Everyone is permitted to copy and distribute verbatim copies |
2425 |
+ of this license document, but changing it is not allowed. |
2426 |
+ |
2427 |
+ Preamble |
2428 |
+ |
2429 |
+ The GNU Affero General Public License is a free, copyleft license for |
2430 |
+software and other kinds of works, specifically designed to ensure |
2431 |
+cooperation with the community in the case of network server software. |
2432 |
+ |
2433 |
+ The licenses for most software and other practical works are designed |
2434 |
+to take away your freedom to share and change the works. By contrast, |
2435 |
+our General Public Licenses are intended to guarantee your freedom to |
2436 |
+share and change all versions of a program--to make sure it remains free |
2437 |
+software for all its users. |
2438 |
+ |
2439 |
+ When we speak of free software, we are referring to freedom, not |
2440 |
+price. Our General Public Licenses are designed to make sure that you |
2441 |
+have the freedom to distribute copies of free software (and charge for |
2442 |
+them if you wish), that you receive source code or can get it if you |
2443 |
+want it, that you can change the software or use pieces of it in new |
2444 |
+free programs, and that you know you can do these things. |
2445 |
+ |
2446 |
+ Developers that use our General Public Licenses protect your rights |
2447 |
+with two steps: (1) assert copyright on the software, and (2) offer |
2448 |
+you this License which gives you legal permission to copy, distribute |
2449 |
+and/or modify the software. |
2450 |
+ |
2451 |
+ A secondary benefit of defending all users' freedom is that |
2452 |
+improvements made in alternate versions of the program, if they |
2453 |
+receive widespread use, become available for other developers to |
2454 |
+incorporate. Many developers of free software are heartened and |
2455 |
+encouraged by the resulting cooperation. However, in the case of |
2456 |
+software used on network servers, this result may fail to come about. |
2457 |
+The GNU General Public License permits making a modified version and |
2458 |
+letting the public access it on a server without ever releasing its |
2459 |
+source code to the public. |
2460 |
+ |
2461 |
+ The GNU Affero General Public License is designed specifically to |
2462 |
+ensure that, in such cases, the modified source code becomes available |
2463 |
+to the community. It requires the operator of a network server to |
2464 |
+provide the source code of the modified version running there to the |
2465 |
+users of that server. Therefore, public use of a modified version, on |
2466 |
+a publicly accessible server, gives the public access to the source |
2467 |
+code of the modified version. |
2468 |
+ |
2469 |
+ An older license, called the Affero General Public License and |
2470 |
+published by Affero, was designed to accomplish similar goals. This is |
2471 |
+a different license, not a version of the Affero GPL, but Affero has |
2472 |
+released a new version of the Affero GPL which permits relicensing under |
2473 |
+this license. |
2474 |
+ |
2475 |
+ The precise terms and conditions for copying, distribution and |
2476 |
+modification follow. |
2477 |
+ |
2478 |
+ TERMS AND CONDITIONS |
2479 |
+ |
2480 |
+ 0. Definitions. |
2481 |
+ |
2482 |
+ "This License" refers to version 3 of the GNU Affero General Public License. |
2483 |
+ |
2484 |
+ "Copyright" also means copyright-like laws that apply to other kinds of |
2485 |
+works, such as semiconductor masks. |
2486 |
+ |
2487 |
+ "The Program" refers to any copyrightable work licensed under this |
2488 |
+License. Each licensee is addressed as "you". "Licensees" and |
2489 |
+"recipients" may be individuals or organizations. |
2490 |
+ |
2491 |
+ To "modify" a work means to copy from or adapt all or part of the work |
2492 |
+in a fashion requiring copyright permission, other than the making of an |
2493 |
+exact copy. The resulting work is called a "modified version" of the |
2494 |
+earlier work or a work "based on" the earlier work. |
2495 |
+ |
2496 |
+ A "covered work" means either the unmodified Program or a work based |
2497 |
+on the Program. |
2498 |
+ |
2499 |
+ To "propagate" a work means to do anything with it that, without |
2500 |
+permission, would make you directly or secondarily liable for |
2501 |
+infringement under applicable copyright law, except executing it on a |
2502 |
+computer or modifying a private copy. Propagation includes copying, |
2503 |
+distribution (with or without modification), making available to the |
2504 |
+public, and in some countries other activities as well. |
2505 |
+ |
2506 |
+ To "convey" a work means any kind of propagation that enables other |
2507 |
+parties to make or receive copies. Mere interaction with a user through |
2508 |
+a computer network, with no transfer of a copy, is not conveying. |
2509 |
+ |
2510 |
+ An interactive user interface displays "Appropriate Legal Notices" |
2511 |
+to the extent that it includes a convenient and prominently visible |
2512 |
+feature that (1) displays an appropriate copyright notice, and (2) |
2513 |
+tells the user that there is no warranty for the work (except to the |
2514 |
+extent that warranties are provided), that licensees may convey the |
2515 |
+work under this License, and how to view a copy of this License. If |
2516 |
+the interface presents a list of user commands or options, such as a |
2517 |
+menu, a prominent item in the list meets this criterion. |
2518 |
+ |
2519 |
+ 1. Source Code. |
2520 |
+ |
2521 |
+ The "source code" for a work means the preferred form of the work |
2522 |
+for making modifications to it. "Object code" means any non-source |
2523 |
+form of a work. |
2524 |
+ |
2525 |
+ A "Standard Interface" means an interface that either is an official |
2526 |
+standard defined by a recognized standards body, or, in the case of |
2527 |
+interfaces specified for a particular programming language, one that |
2528 |
+is widely used among developers working in that language. |
2529 |
+ |
2530 |
+ The "System Libraries" of an executable work include anything, other |
2531 |
+than the work as a whole, that (a) is included in the normal form of |
2532 |
+packaging a Major Component, but which is not part of that Major |
2533 |
+Component, and (b) serves only to enable use of the work with that |
2534 |
+Major Component, or to implement a Standard Interface for which an |
2535 |
+implementation is available to the public in source code form. A |
2536 |
+"Major Component", in this context, means a major essential component |
2537 |
+(kernel, window system, and so on) of the specific operating system |
2538 |
+(if any) on which the executable work runs, or a compiler used to |
2539 |
+produce the work, or an object code interpreter used to run it. |
2540 |
+ |
2541 |
+ The "Corresponding Source" for a work in object code form means all |
2542 |
+the source code needed to generate, install, and (for an executable |
2543 |
+work) run the object code and to modify the work, including scripts to |
2544 |
+control those activities. However, it does not include the work's |
2545 |
+System Libraries, or general-purpose tools or generally available free |
2546 |
+programs which are used unmodified in performing those activities but |
2547 |
+which are not part of the work. For example, Corresponding Source |
2548 |
+includes interface definition files associated with source files for |
2549 |
+the work, and the source code for shared libraries and dynamically |
2550 |
+linked subprograms that the work is specifically designed to require, |
2551 |
+such as by intimate data communication or control flow between those |
2552 |
+subprograms and other parts of the work. |
2553 |
+ |
2554 |
+ The Corresponding Source need not include anything that users |
2555 |
+can regenerate automatically from other parts of the Corresponding |
2556 |
+Source. |
2557 |
+ |
2558 |
+ The Corresponding Source for a work in source code form is that |
2559 |
+same work. |
2560 |
+ |
2561 |
+ 2. Basic Permissions. |
2562 |
+ |
2563 |
+ All rights granted under this License are granted for the term of |
2564 |
+copyright on the Program, and are irrevocable provided the stated |
2565 |
+conditions are met. This License explicitly affirms your unlimited |
2566 |
+permission to run the unmodified Program. The output from running a |
2567 |
+covered work is covered by this License only if the output, given its |
2568 |
+content, constitutes a covered work. This License acknowledges your |
2569 |
+rights of fair use or other equivalent, as provided by copyright law. |
2570 |
+ |
2571 |
+ You may make, run and propagate covered works that you do not |
2572 |
+convey, without conditions so long as your license otherwise remains |
2573 |
+in force. You may convey covered works to others for the sole purpose |
2574 |
+of having them make modifications exclusively for you, or provide you |
2575 |
+with facilities for running those works, provided that you comply with |
2576 |
+the terms of this License in conveying all material for which you do |
2577 |
+not control copyright. Those thus making or running the covered works |
2578 |
+for you must do so exclusively on your behalf, under your direction |
2579 |
+and control, on terms that prohibit them from making any copies of |
2580 |
+your copyrighted material outside their relationship with you. |
2581 |
+ |
2582 |
+ Conveying under any other circumstances is permitted solely under |
2583 |
+the conditions stated below. Sublicensing is not allowed; section 10 |
2584 |
+makes it unnecessary. |
2585 |
+ |
2586 |
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
2587 |
+ |
2588 |
+ No covered work shall be deemed part of an effective technological |
2589 |
+measure under any applicable law fulfilling obligations under article |
2590 |
+11 of the WIPO copyright treaty adopted on 20 December 1996, or |
2591 |
+similar laws prohibiting or restricting circumvention of such |
2592 |
+measures. |
2593 |
+ |
2594 |
+ When you convey a covered work, you waive any legal power to forbid |
2595 |
+circumvention of technological measures to the extent such circumvention |
2596 |
+is effected by exercising rights under this License with respect to |
2597 |
+the covered work, and you disclaim any intention to limit operation or |
2598 |
+modification of the work as a means of enforcing, against the work's |
2599 |
+users, your or third parties' legal rights to forbid circumvention of |
2600 |
+technological measures. |
2601 |
+ |
2602 |
+ 4. Conveying Verbatim Copies. |
2603 |
+ |
2604 |
+ You may convey verbatim copies of the Program's source code as you |
2605 |
+receive it, in any medium, provided that you conspicuously and |
2606 |
+appropriately publish on each copy an appropriate copyright notice; |
2607 |
+keep intact all notices stating that this License and any |
2608 |
+non-permissive terms added in accord with section 7 apply to the code; |
2609 |
+keep intact all notices of the absence of any warranty; and give all |
2610 |
+recipients a copy of this License along with the Program. |
2611 |
+ |
2612 |
+ You may charge any price or no price for each copy that you convey, |
2613 |
+and you may offer support or warranty protection for a fee. |
2614 |
+ |
2615 |
+ 5. Conveying Modified Source Versions. |
2616 |
+ |
2617 |
+ You may convey a work based on the Program, or the modifications to |
2618 |
+produce it from the Program, in the form of source code under the |
2619 |
+terms of section 4, provided that you also meet all of these conditions: |
2620 |
+ |
2621 |
+ a) The work must carry prominent notices stating that you modified |
2622 |
+ it, and giving a relevant date. |
2623 |
+ |
2624 |
+ b) The work must carry prominent notices stating that it is |
2625 |
+ released under this License and any conditions added under section |
2626 |
+ 7. This requirement modifies the requirement in section 4 to |
2627 |
+ "keep intact all notices". |
2628 |
+ |
2629 |
+ c) You must license the entire work, as a whole, under this |
2630 |
+ License to anyone who comes into possession of a copy. This |
2631 |
+ License will therefore apply, along with any applicable section 7 |
2632 |
+ additional terms, to the whole of the work, and all its parts, |
2633 |
+ regardless of how they are packaged. This License gives no |
2634 |
+ permission to license the work in any other way, but it does not |
2635 |
+ invalidate such permission if you have separately received it. |
2636 |
+ |
2637 |
+ d) If the work has interactive user interfaces, each must display |
2638 |
+ Appropriate Legal Notices; however, if the Program has interactive |
2639 |
+ interfaces that do not display Appropriate Legal Notices, your |
2640 |
+ work need not make them do so. |
2641 |
+ |
2642 |
+ A compilation of a covered work with other separate and independent |
2643 |
+works, which are not by their nature extensions of the covered work, |
2644 |
+and which are not combined with it such as to form a larger program, |
2645 |
+in or on a volume of a storage or distribution medium, is called an |
2646 |
+"aggregate" if the compilation and its resulting copyright are not |
2647 |
+used to limit the access or legal rights of the compilation's users |
2648 |
+beyond what the individual works permit. Inclusion of a covered work |
2649 |
+in an aggregate does not cause this License to apply to the other |
2650 |
+parts of the aggregate. |
2651 |
+ |
2652 |
+ 6. Conveying Non-Source Forms. |
2653 |
+ |
2654 |
+ You may convey a covered work in object code form under the terms |
2655 |
+of sections 4 and 5, provided that you also convey the |
2656 |
+machine-readable Corresponding Source under the terms of this License, |
2657 |
+in one of these ways: |
2658 |
+ |
2659 |
+ a) Convey the object code in, or embodied in, a physical product |
2660 |
+ (including a physical distribution medium), accompanied by the |
2661 |
+ Corresponding Source fixed on a durable physical medium |
2662 |
+ customarily used for software interchange. |
2663 |
+ |
2664 |
+ b) Convey the object code in, or embodied in, a physical product |
2665 |
+ (including a physical distribution medium), accompanied by a |
2666 |
+ written offer, valid for at least three years and valid for as |
2667 |
+ long as you offer spare parts or customer support for that product |
2668 |
+ model, to give anyone who possesses the object code either (1) a |
2669 |
+ copy of the Corresponding Source for all the software in the |
2670 |
+ product that is covered by this License, on a durable physical |
2671 |
+ medium customarily used for software interchange, for a price no |
2672 |
+ more than your reasonable cost of physically performing this |
2673 |
+ conveying of source, or (2) access to copy the |
2674 |
+ Corresponding Source from a network server at no charge. |
2675 |
+ |
2676 |
+ c) Convey individual copies of the object code with a copy of the |
2677 |
+ written offer to provide the Corresponding Source. This |
2678 |
+ alternative is allowed only occasionally and noncommercially, and |
2679 |
+ only if you received the object code with such an offer, in accord |
2680 |
+ with subsection 6b. |
2681 |
+ |
2682 |
+ d) Convey the object code by offering access from a designated |
2683 |
+ place (gratis or for a charge), and offer equivalent access to the |
2684 |
+ Corresponding Source in the same way through the same place at no |
2685 |
+ further charge. You need not require recipients to copy the |
2686 |
+ Corresponding Source along with the object code. If the place to |
2687 |
+ copy the object code is a network server, the Corresponding Source |
2688 |
+ may be on a different server (operated by you or a third party) |
2689 |
+ that supports equivalent copying facilities, provided you maintain |
2690 |
+ clear directions next to the object code saying where to find the |
2691 |
+ Corresponding Source. Regardless of what server hosts the |
2692 |
+ Corresponding Source, you remain obligated to ensure that it is |
2693 |
+ available for as long as needed to satisfy these requirements. |
2694 |
+ |
2695 |
+ e) Convey the object code using peer-to-peer transmission, provided |
2696 |
+ you inform other peers where the object code and Corresponding |
2697 |
+ Source of the work are being offered to the general public at no |
2698 |
+ charge under subsection 6d. |
2699 |
+ |
2700 |
+ A separable portion of the object code, whose source code is excluded |
2701 |
+from the Corresponding Source as a System Library, need not be |
2702 |
+included in conveying the object code work. |
2703 |
+ |
2704 |
+ A "User Product" is either (1) a "consumer product", which means any |
2705 |
+tangible personal property which is normally used for personal, family, |
2706 |
+or household purposes, or (2) anything designed or sold for incorporation |
2707 |
+into a dwelling. In determining whether a product is a consumer product, |
2708 |
+doubtful cases shall be resolved in favor of coverage. For a particular |
2709 |
+product received by a particular user, "normally used" refers to a |
2710 |
+typical or common use of that class of product, regardless of the status |
2711 |
+of the particular user or of the way in which the particular user |
2712 |
+actually uses, or expects or is expected to use, the product. A product |
2713 |
+is a consumer product regardless of whether the product has substantial |
2714 |
+commercial, industrial or non-consumer uses, unless such uses represent |
2715 |
+the only significant mode of use of the product. |
2716 |
+ |
2717 |
+ "Installation Information" for a User Product means any methods, |
2718 |
+procedures, authorization keys, or other information required to install |
2719 |
+and execute modified versions of a covered work in that User Product from |
2720 |
+a modified version of its Corresponding Source. The information must |
2721 |
+suffice to ensure that the continued functioning of the modified object |
2722 |
+code is in no case prevented or interfered with solely because |
2723 |
+modification has been made. |
2724 |
+ |
2725 |
+ If you convey an object code work under this section in, or with, or |
2726 |
+specifically for use in, a User Product, and the conveying occurs as |
2727 |
+part of a transaction in which the right of possession and use of the |
2728 |
+User Product is transferred to the recipient in perpetuity or for a |
2729 |
+fixed term (regardless of how the transaction is characterized), the |
2730 |
+Corresponding Source conveyed under this section must be accompanied |
2731 |
+by the Installation Information. But this requirement does not apply |
2732 |
+if neither you nor any third party retains the ability to install |
2733 |
+modified object code on the User Product (for example, the work has |
2734 |
+been installed in ROM). |
2735 |
+ |
2736 |
+ The requirement to provide Installation Information does not include a |
2737 |
+requirement to continue to provide support service, warranty, or updates |
2738 |
+for a work that has been modified or installed by the recipient, or for |
2739 |
+the User Product in which it has been modified or installed. Access to a |
2740 |
+network may be denied when the modification itself materially and |
2741 |
+adversely affects the operation of the network or violates the rules and |
2742 |
+protocols for communication across the network. |
2743 |
+ |
2744 |
+ Corresponding Source conveyed, and Installation Information provided, |
2745 |
+in accord with this section must be in a format that is publicly |
2746 |
+documented (and with an implementation available to the public in |
2747 |
+source code form), and must require no special password or key for |
2748 |
+unpacking, reading or copying. |
2749 |
+ |
2750 |
+ 7. Additional Terms. |
2751 |
+ |
2752 |
+ "Additional permissions" are terms that supplement the terms of this |
2753 |
+License by making exceptions from one or more of its conditions. |
2754 |
+Additional permissions that are applicable to the entire Program shall |
2755 |
+be treated as though they were included in this License, to the extent |
2756 |
+that they are valid under applicable law. If additional permissions |
2757 |
+apply only to part of the Program, that part may be used separately |
2758 |
+under those permissions, but the entire Program remains governed by |
2759 |
+this License without regard to the additional permissions. |
2760 |
+ |
2761 |
+ When you convey a copy of a covered work, you may at your option |
2762 |
+remove any additional permissions from that copy, or from any part of |
2763 |
+it. (Additional permissions may be written to require their own |
2764 |
+removal in certain cases when you modify the work.) You may place |
2765 |
+additional permissions on material, added by you to a covered work, |
2766 |
+for which you have or can give appropriate copyright permission. |
2767 |
+ |
2768 |
+ Notwithstanding any other provision of this License, for material you |
2769 |
+add to a covered work, you may (if authorized by the copyright holders of |
2770 |
+that material) supplement the terms of this License with terms: |
2771 |
+ |
2772 |
+ a) Disclaiming warranty or limiting liability differently from the |
2773 |
+ terms of sections 15 and 16 of this License; or |
2774 |
+ |
2775 |
+ b) Requiring preservation of specified reasonable legal notices or |
2776 |
+ author attributions in that material or in the Appropriate Legal |
2777 |
+ Notices displayed by works containing it; or |
2778 |
+ |
2779 |
+ c) Prohibiting misrepresentation of the origin of that material, or |
2780 |
+ requiring that modified versions of such material be marked in |
2781 |
+ reasonable ways as different from the original version; or |
2782 |
+ |
2783 |
+ d) Limiting the use for publicity purposes of names of licensors or |
2784 |
+ authors of the material; or |
2785 |
+ |
2786 |
+ e) Declining to grant rights under trademark law for use of some |
2787 |
+ trade names, trademarks, or service marks; or |
2788 |
+ |
2789 |
+ f) Requiring indemnification of licensors and authors of that |
2790 |
+ material by anyone who conveys the material (or modified versions of |
2791 |
+ it) with contractual assumptions of liability to the recipient, for |
2792 |
+ any liability that these contractual assumptions directly impose on |
2793 |
+ those licensors and authors. |
2794 |
+ |
2795 |
+ All other non-permissive additional terms are considered "further |
2796 |
+restrictions" within the meaning of section 10. If the Program as you |
2797 |
+received it, or any part of it, contains a notice stating that it is |
2798 |
+governed by this License along with a term that is a further |
2799 |
+restriction, you may remove that term. If a license document contains |
2800 |
+a further restriction but permits relicensing or conveying under this |
2801 |
+License, you may add to a covered work material governed by the terms |
2802 |
+of that license document, provided that the further restriction does |
2803 |
+not survive such relicensing or conveying. |
2804 |
+ |
2805 |
+ If you add terms to a covered work in accord with this section, you |
2806 |
+must place, in the relevant source files, a statement of the |
2807 |
+additional terms that apply to those files, or a notice indicating |
2808 |
+where to find the applicable terms. |
2809 |
+ |
2810 |
+ Additional terms, permissive or non-permissive, may be stated in the |
2811 |
+form of a separately written license, or stated as exceptions; |
2812 |
+the above requirements apply either way. |
2813 |
+ |
2814 |
+ 8. Termination. |
2815 |
+ |
2816 |
+ You may not propagate or modify a covered work except as expressly |
2817 |
+provided under this License. Any attempt otherwise to propagate or |
2818 |
+modify it is void, and will automatically terminate your rights under |
2819 |
+this License (including any patent licenses granted under the third |
2820 |
+paragraph of section 11). |
2821 |
+ |
2822 |
+ However, if you cease all violation of this License, then your |
2823 |
+license from a particular copyright holder is reinstated (a) |
2824 |
+provisionally, unless and until the copyright holder explicitly and |
2825 |
+finally terminates your license, and (b) permanently, if the copyright |
2826 |
+holder fails to notify you of the violation by some reasonable means |
2827 |
+prior to 60 days after the cessation. |
2828 |
+ |
2829 |
+ Moreover, your license from a particular copyright holder is |
2830 |
+reinstated permanently if the copyright holder notifies you of the |
2831 |
+violation by some reasonable means, this is the first time you have |
2832 |
+received notice of violation of this License (for any work) from that |
2833 |
+copyright holder, and you cure the violation prior to 30 days after |
2834 |
+your receipt of the notice. |
2835 |
+ |
2836 |
+ Termination of your rights under this section does not terminate the |
2837 |
+licenses of parties who have received copies or rights from you under |
2838 |
+this License. If your rights have been terminated and not permanently |
2839 |
+reinstated, you do not qualify to receive new licenses for the same |
2840 |
+material under section 10. |
2841 |
+ |
2842 |
+ 9. Acceptance Not Required for Having Copies. |
2843 |
+ |
2844 |
+ You are not required to accept this License in order to receive or |
2845 |
+run a copy of the Program. Ancillary propagation of a covered work |
2846 |
+occurring solely as a consequence of using peer-to-peer transmission |
2847 |
+to receive a copy likewise does not require acceptance. However, |
2848 |
+nothing other than this License grants you permission to propagate or |
2849 |
+modify any covered work. These actions infringe copyright if you do |
2850 |
+not accept this License. Therefore, by modifying or propagating a |
2851 |
+covered work, you indicate your acceptance of this License to do so. |
2852 |
+ |
2853 |
+ 10. Automatic Licensing of Downstream Recipients. |
2854 |
+ |
2855 |
+ Each time you convey a covered work, the recipient automatically |
2856 |
+receives a license from the original licensors, to run, modify and |
2857 |
+propagate that work, subject to this License. You are not responsible |
2858 |
+for enforcing compliance by third parties with this License. |
2859 |
+ |
2860 |
+ An "entity transaction" is a transaction transferring control of an |
2861 |
+organization, or substantially all assets of one, or subdividing an |
2862 |
+organization, or merging organizations. If propagation of a covered |
2863 |
+work results from an entity transaction, each party to that |
2864 |
+transaction who receives a copy of the work also receives whatever |
2865 |
+licenses to the work the party's predecessor in interest had or could |
2866 |
+give under the previous paragraph, plus a right to possession of the |
2867 |
+Corresponding Source of the work from the predecessor in interest, if |
2868 |
+the predecessor has it or can get it with reasonable efforts. |
2869 |
+ |
2870 |
+ You may not impose any further restrictions on the exercise of the |
2871 |
+rights granted or affirmed under this License. For example, you may |
2872 |
+not impose a license fee, royalty, or other charge for exercise of |
2873 |
+rights granted under this License, and you may not initiate litigation |
2874 |
+(including a cross-claim or counterclaim in a lawsuit) alleging that |
2875 |
+any patent claim is infringed by making, using, selling, offering for |
2876 |
+sale, or importing the Program or any portion of it. |
2877 |
+ |
2878 |
+ 11. Patents. |
2879 |
+ |
2880 |
+ A "contributor" is a copyright holder who authorizes use under this |
2881 |
+License of the Program or a work on which the Program is based. The |
2882 |
+work thus licensed is called the contributor's "contributor version". |
2883 |
+ |
2884 |
+ A contributor's "essential patent claims" are all patent claims |
2885 |
+owned or controlled by the contributor, whether already acquired or |
2886 |
+hereafter acquired, that would be infringed by some manner, permitted |
2887 |
+by this License, of making, using, or selling its contributor version, |
2888 |
+but do not include claims that would be infringed only as a |
2889 |
+consequence of further modification of the contributor version. For |
2890 |
+purposes of this definition, "control" includes the right to grant |
2891 |
+patent sublicenses in a manner consistent with the requirements of |
2892 |
+this License. |
2893 |
+ |
2894 |
+ Each contributor grants you a non-exclusive, worldwide, royalty-free |
2895 |
+patent license under the contributor's essential patent claims, to |
2896 |
+make, use, sell, offer for sale, import and otherwise run, modify and |
2897 |
+propagate the contents of its contributor version. |
2898 |
+ |
2899 |
+ In the following three paragraphs, a "patent license" is any express |
2900 |
+agreement or commitment, however denominated, not to enforce a patent |
2901 |
+(such as an express permission to practice a patent or covenant not to |
2902 |
+sue for patent infringement). To "grant" such a patent license to a |
2903 |
+party means to make such an agreement or commitment not to enforce a |
2904 |
+patent against the party. |
2905 |
+ |
2906 |
+ If you convey a covered work, knowingly relying on a patent license, |
2907 |
+and the Corresponding Source of the work is not available for anyone |
2908 |
+to copy, free of charge and under the terms of this License, through a |
2909 |
+publicly available network server or other readily accessible means, |
2910 |
+then you must either (1) cause the Corresponding Source to be so |
2911 |
+available, or (2) arrange to deprive yourself of the benefit of the |
2912 |
+patent license for this particular work, or (3) arrange, in a manner |
2913 |
+consistent with the requirements of this License, to extend the patent |
2914 |
+license to downstream recipients. "Knowingly relying" means you have |
2915 |
+actual knowledge that, but for the patent license, your conveying the |
2916 |
+covered work in a country, or your recipient's use of the covered work |
2917 |
+in a country, would infringe one or more identifiable patents in that |
2918 |
+country that you have reason to believe are valid. |
2919 |
+ |
2920 |
+ If, pursuant to or in connection with a single transaction or |
2921 |
+arrangement, you convey, or propagate by procuring conveyance of, a |
2922 |
+covered work, and grant a patent license to some of the parties |
2923 |
+receiving the covered work authorizing them to use, propagate, modify |
2924 |
+or convey a specific copy of the covered work, then the patent license |
2925 |
+you grant is automatically extended to all recipients of the covered |
2926 |
+work and works based on it. |
2927 |
+ |
2928 |
+ A patent license is "discriminatory" if it does not include within |
2929 |
+the scope of its coverage, prohibits the exercise of, or is |
2930 |
+conditioned on the non-exercise of one or more of the rights that are |
2931 |
+specifically granted under this License. You may not convey a covered |
2932 |
+work if you are a party to an arrangement with a third party that is |
2933 |
+in the business of distributing software, under which you make payment |
2934 |
+to the third party based on the extent of your activity of conveying |
2935 |
+the work, and under which the third party grants, to any of the |
2936 |
+parties who would receive the covered work from you, a discriminatory |
2937 |
+patent license (a) in connection with copies of the covered work |
2938 |
+conveyed by you (or copies made from those copies), or (b) primarily |
2939 |
+for and in connection with specific products or compilations that |
2940 |
+contain the covered work, unless you entered into that arrangement, |
2941 |
+or that patent license was granted, prior to 28 March 2007. |
2942 |
+ |
2943 |
+ Nothing in this License shall be construed as excluding or limiting |
2944 |
+any implied license or other defenses to infringement that may |
2945 |
+otherwise be available to you under applicable patent law. |
2946 |
+ |
2947 |
+ 12. No Surrender of Others' Freedom. |
2948 |
+ |
2949 |
+ If conditions are imposed on you (whether by court order, agreement or |
2950 |
+otherwise) that contradict the conditions of this License, they do not |
2951 |
+excuse you from the conditions of this License. If you cannot convey a |
2952 |
+covered work so as to satisfy simultaneously your obligations under this |
2953 |
+License and any other pertinent obligations, then as a consequence you may |
2954 |
+not convey it at all. For example, if you agree to terms that obligate you |
2955 |
+to collect a royalty for further conveying from those to whom you convey |
2956 |
+the Program, the only way you could satisfy both those terms and this |
2957 |
+License would be to refrain entirely from conveying the Program. |
2958 |
+ |
2959 |
+ 13. Remote Network Interaction; Use with the GNU General Public License. |
2960 |
+ |
2961 |
+ Notwithstanding any other provision of this License, if you modify the |
2962 |
+Program, your modified version must prominently offer all users |
2963 |
+interacting with it remotely through a computer network (if your version |
2964 |
+supports such interaction) an opportunity to receive the Corresponding |
2965 |
+Source of your version by providing access to the Corresponding Source |
2966 |
+from a network server at no charge, through some standard or customary |
2967 |
+means of facilitating copying of software. This Corresponding Source |
2968 |
+shall include the Corresponding Source for any work covered by version 3 |
2969 |
+of the GNU General Public License that is incorporated pursuant to the |
2970 |
+following paragraph. |
2971 |
+ |
2972 |
+ Notwithstanding any other provision of this License, you have |
2973 |
+permission to link or combine any covered work with a work licensed |
2974 |
+under version 3 of the GNU General Public License into a single |
2975 |
+combined work, and to convey the resulting work. The terms of this |
2976 |
+License will continue to apply to the part which is the covered work, |
2977 |
+but the work with which it is combined will remain governed by version |
2978 |
+3 of the GNU General Public License. |
2979 |
+ |
2980 |
+ 14. Revised Versions of this License. |
2981 |
+ |
2982 |
+ The Free Software Foundation may publish revised and/or new versions of |
2983 |
+the GNU Affero General Public License from time to time. Such new versions |
2984 |
+will be similar in spirit to the present version, but may differ in detail to |
2985 |
+address new problems or concerns. |
2986 |
+ |
2987 |
+ Each version is given a distinguishing version number. If the |
2988 |
+Program specifies that a certain numbered version of the GNU Affero General |
2989 |
+Public License "or any later version" applies to it, you have the |
2990 |
+option of following the terms and conditions either of that numbered |
2991 |
+version or of any later version published by the Free Software |
2992 |
+Foundation. If the Program does not specify a version number of the |
2993 |
+GNU Affero General Public License, you may choose any version ever published |
2994 |
+by the Free Software Foundation. |
2995 |
+ |
2996 |
+ If the Program specifies that a proxy can decide which future |
2997 |
+versions of the GNU Affero General Public License can be used, that proxy's |
2998 |
+public statement of acceptance of a version permanently authorizes you |
2999 |
+to choose that version for the Program. |
3000 |
+ |
3001 |
+ Later license versions may give you additional or different |
3002 |
+permissions. However, no additional obligations are imposed on any |
3003 |
+author or copyright holder as a result of your choosing to follow a |
3004 |
+later version. |
3005 |
+ |
3006 |
+ 15. Disclaimer of Warranty. |
3007 |
+ |
3008 |
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
3009 |
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
3010 |
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
3011 |
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
3012 |
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
3013 |
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
3014 |
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
3015 |
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
3016 |
+ |
3017 |
+ 16. Limitation of Liability. |
3018 |
+ |
3019 |
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
3020 |
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
3021 |
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
3022 |
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
3023 |
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
3024 |
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
3025 |
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
3026 |
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
3027 |
+SUCH DAMAGES. |
3028 |
+ |
3029 |
+ 17. Interpretation of Sections 15 and 16. |
3030 |
+ |
3031 |
+ If the disclaimer of warranty and limitation of liability provided |
3032 |
+above cannot be given local legal effect according to their terms, |
3033 |
+reviewing courts shall apply local law that most closely approximates |
3034 |
+an absolute waiver of all civil liability in connection with the |
3035 |
+Program, unless a warranty or assumption of liability accompanies a |
3036 |
+copy of the Program in return for a fee. |
3037 |
+ |
3038 |
+ END OF TERMS AND CONDITIONS |
3039 |
+ |
3040 |
+ How to Apply These Terms to Your New Programs |
3041 |
+ |
3042 |
+ If you develop a new program, and you want it to be of the greatest |
3043 |
+possible use to the public, the best way to achieve this is to make it |
3044 |
+free software which everyone can redistribute and change under these terms. |
3045 |
+ |
3046 |
+ To do so, attach the following notices to the program. It is safest |
3047 |
+to attach them to the start of each source file to most effectively |
3048 |
+state the exclusion of warranty; and each file should have at least |
3049 |
+the "copyright" line and a pointer to where the full notice is found. |
3050 |
+ |
3051 |
+ <one line to give the program's name and a brief idea of what it does.> |
3052 |
+ Copyright (C) <year> <name of author> |
3053 |
+ |
3054 |
+ This program is free software: you can redistribute it and/or modify |
3055 |
+ it under the terms of the GNU Affero General Public License as published by |
3056 |
+ the Free Software Foundation, either version 3 of the License, or |
3057 |
+ (at your option) any later version. |
3058 |
+ |
3059 |
+ This program is distributed in the hope that it will be useful, |
3060 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
3061 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3062 |
+ GNU Affero General Public License for more details. |
3063 |
+ |
3064 |
+ You should have received a copy of the GNU Affero General Public License |
3065 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
3066 |
+ |
3067 |
+Also add information on how to contact you by electronic and paper mail. |
3068 |
+ |
3069 |
+ If your software can interact with users remotely through a computer |
3070 |
+network, you should also make sure that it provides a way for users to |
3071 |
+get its source. For example, if your program is a web application, its |
3072 |
+interface could display a "Source" link that leads users to an archive |
3073 |
+of the code. There are many ways you could offer source, and different |
3074 |
+solutions will be better for different programs; see section 13 for the |
3075 |
+specific requirements. |
3076 |
+ |
3077 |
+ You should also get your employer (if you work as a programmer) or school, |
3078 |
+if any, to sign a "copyright disclaimer" for the program, if necessary. |
3079 |
+For more information on this, and how to apply and follow the GNU AGPL, see |
3080 |
+<http://www.gnu.org/licenses/>. |
3081 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/cs_CZ.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/cs_CZ.inc |
3082 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/cs_CZ.inc 1970-01-01 01:00:00.000000000 +0100 |
3083 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/cs_CZ.inc 2013-11-24 00:38:51.000000000 +0100 |
3084 |
@@ -0,0 +1,68 @@ |
3085 |
+<?php |
3086 |
+ |
3087 |
+$labels = array(); |
3088 |
+$labels['navtitle'] = 'Ãkoly'; |
3089 |
+$labels['lists'] = 'Seznamy úkolů'; |
3090 |
+$labels['list'] = 'Seznam úkolů'; |
3091 |
+$labels['tags'] = 'Å tÃtky'; |
3092 |
+ |
3093 |
+$labels['newtask'] = 'Nový úkol'; |
3094 |
+$labels['createnewtask'] = 'VytvoÅit nový úkol (napÅ. sobota, posekat trávnÃk)'; |
3095 |
+$labels['createfrommail'] = 'Uložit úkol'; |
3096 |
+$labels['mark'] = 'OznaÄit'; |
3097 |
+$labels['unmark'] = 'OdznaÄit'; |
3098 |
+$labels['edit'] = 'Upravit'; |
3099 |
+$labels['delete'] = 'Smazat'; |
3100 |
+$labels['title'] = 'Titulek'; |
3101 |
+$labels['description'] = 'Popis'; |
3102 |
+$labels['datetime'] = 'Konec'; |
3103 |
+$labels['start'] = 'ZaÄátek'; |
3104 |
+$labels['alarms'] = 'PÅipomenutÃ'; |
3105 |
+ |
3106 |
+$labels['all'] = 'VÅ¡echny'; |
3107 |
+$labels['flagged'] = 'OznaÄkované'; |
3108 |
+$labels['complete'] = 'DokonÄené'; |
3109 |
+$labels['overdue'] = 'ZpoždÄné'; |
3110 |
+$labels['today'] = 'Dnes'; |
3111 |
+$labels['tomorrow'] = 'ZÃtra'; |
3112 |
+$labels['next7days'] = 'PÅÃÅ¡tÃch 7 dnů'; |
3113 |
+$labels['later'] = 'PozdÄji'; |
3114 |
+$labels['nodate'] = 'Žádné datum'; |
3115 |
+$labels['removetag'] = 'Odstranit'; |
3116 |
+ |
3117 |
+$labels['taskdetails'] = 'Podrobnosti'; |
3118 |
+$labels['newtask'] = 'Nový úkol'; |
3119 |
+$labels['edittask'] = 'Upravit úkol'; |
3120 |
+$labels['save'] = 'Uložit'; |
3121 |
+$labels['cancel'] = 'Storno'; |
3122 |
+$labels['addsubtask'] = 'PÅidat podúkol'; |
3123 |
+$labels['deletetask'] = 'Smazat úkol'; |
3124 |
+$labels['deletethisonly'] = 'Smazat pouze tento úkol'; |
3125 |
+$labels['deletewithchilds'] = 'Smazat se všemi podúkoly'; |
3126 |
+ |
3127 |
+$labels['tabsummary'] = 'Souhrn'; |
3128 |
+$labels['tabrecurrence'] = 'OpakovánÃ'; |
3129 |
+$labels['tabattachments'] = 'PÅÃlohy'; |
3130 |
+$labels['tabsharing'] = 'SdÃlenÃ'; |
3131 |
+ |
3132 |
+$labels['editlist'] = 'Upravit seznam'; |
3133 |
+$labels['createlist'] = 'PÅidat seznam'; |
3134 |
+$labels['listactions'] = 'Možnosti seznamu...'; |
3135 |
+$labels['listname'] = 'Název'; |
3136 |
+$labels['showalarms'] = 'Ukázat upozornÄnÃ'; |
3137 |
+$labels['import'] = 'Import'; |
3138 |
+ |
3139 |
+// date words |
3140 |
+$labels['on'] = ' '; |
3141 |
+$labels['at'] = ' '; |
3142 |
+$labels['this'] = 'tento'; |
3143 |
+$labels['next'] = 'pÅÃÅ¡tÃ'; |
3144 |
+ |
3145 |
+// messages |
3146 |
+$labels['savingdata'] = 'Ukládám data...'; |
3147 |
+$labels['errorsaving'] = 'Nelze uložit data.'; |
3148 |
+$labels['notasksfound'] = 'Nebyly nalezeny žádné úkoly vyhovujÃcà daným kritériÃm'; |
3149 |
+$labels['invalidstartduedates'] = 'PoÄáteÄnà datum nesmà být pozdÄjÅ¡Ã než koncové datum.'; |
3150 |
+$labels['deletetasktconfirm'] = 'Opravdu chcete smazat tento úkol?'; |
3151 |
+$labels['deleteparenttasktconfirm'] = 'Opravdu chcete smazat tento úkol a všechny jeho podúkoly?'; |
3152 |
+$labels['deletelistconfirm'] = 'Opravdu chcete smazat tento seznam vÄetnÄ vÅ¡ech úkolů?'; |
3153 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/de_CH.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/de_CH.inc |
3154 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/de_CH.inc 1970-01-01 01:00:00.000000000 +0100 |
3155 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/de_CH.inc 2013-11-24 00:38:51.000000000 +0100 |
3156 |
@@ -0,0 +1,68 @@ |
3157 |
+<?php |
3158 |
+ |
3159 |
+$labels = array(); |
3160 |
+$labels['navtitle'] = 'Aufgaben'; |
3161 |
+$labels['lists'] = 'Aufgabenlisten'; |
3162 |
+$labels['list'] = 'Liste'; |
3163 |
+$labels['tags'] = 'Tags'; |
3164 |
+ |
3165 |
+$labels['newtask'] = 'Neue Aufgabe'; |
3166 |
+$labels['createnewtask'] = 'Neue Aufgabe eingeben (z.B. Samstag, Rasenmähen)'; |
3167 |
+$labels['createfrommail'] = 'Als Aufgabe speichern'; |
3168 |
+$labels['mark'] = 'Markieren'; |
3169 |
+$labels['unmark'] = 'Markierung aufheben'; |
3170 |
+$labels['edit'] = 'Bearbeiten'; |
3171 |
+$labels['delete'] = 'Löschen'; |
3172 |
+$labels['title'] = 'Titel'; |
3173 |
+$labels['description'] = 'Beschreibung'; |
3174 |
+$labels['datetime'] = 'Fällig'; |
3175 |
+$labels['start'] = 'Beginn'; |
3176 |
+$labels['alarms'] = 'Erinnerung'; |
3177 |
+ |
3178 |
+$labels['all'] = 'Alle'; |
3179 |
+$labels['flagged'] = 'Markiert'; |
3180 |
+$labels['complete'] = 'Erledigt'; |
3181 |
+$labels['overdue'] = 'Ãberfällig'; |
3182 |
+$labels['today'] = 'Heute'; |
3183 |
+$labels['tomorrow'] = 'Morgen'; |
3184 |
+$labels['next7days'] = 'Nächste 7 Tage'; |
3185 |
+$labels['later'] = 'Später'; |
3186 |
+$labels['nodate'] = 'kein Datum'; |
3187 |
+$labels['removetag'] = 'Löschen'; |
3188 |
+ |
3189 |
+$labels['taskdetails'] = 'Details'; |
3190 |
+$labels['newtask'] = 'Neue Aufgabe'; |
3191 |
+$labels['edittask'] = 'Aufgabe bearbeiten'; |
3192 |
+$labels['save'] = 'Speichern'; |
3193 |
+$labels['cancel'] = 'Abbrechen'; |
3194 |
+$labels['addsubtask'] = 'Neue Teilaufgabe'; |
3195 |
+$labels['deletetask'] = 'Aufgabe löschen'; |
3196 |
+$labels['deletethisonly'] = 'Nur diese Aufgabe löschen'; |
3197 |
+$labels['deletewithchilds'] = 'Mit allen Teilaufhaben löschen'; |
3198 |
+ |
3199 |
+$labels['tabsummary'] = 'Ãbersicht'; |
3200 |
+$labels['tabrecurrence'] = 'Wiederholung'; |
3201 |
+$labels['tabattachments'] = 'Anhänge'; |
3202 |
+$labels['tabsharing'] = 'Freigabe'; |
3203 |
+ |
3204 |
+$labels['editlist'] = 'Liste bearbeiten'; |
3205 |
+$labels['createlist'] = 'Neue Liste'; |
3206 |
+$labels['listactions'] = 'Listenoptionen...'; |
3207 |
+$labels['listname'] = 'Name'; |
3208 |
+$labels['showalarms'] = 'Erinnerungen anzeigen'; |
3209 |
+$labels['import'] = 'Importieren'; |
3210 |
+ |
3211 |
+// date words |
3212 |
+$labels['on'] = 'am'; |
3213 |
+$labels['at'] = 'um'; |
3214 |
+$labels['this'] = 'diesen'; |
3215 |
+$labels['next'] = 'nächsten'; |
3216 |
+ |
3217 |
+// mesages |
3218 |
+$labels['savingdata'] = 'Daten werden gespeichert...'; |
3219 |
+$labels['errorsaving'] = 'Fehler beim Speichern.'; |
3220 |
+$labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.'; |
3221 |
+$labels['invalidstartduedates'] = 'Beginn der Aufgabe darf nicht grösser als das Enddatum sein.'; |
3222 |
+$labels['deletetasktconfirm'] = 'Möchten Sie diese Aufgabe wirklich löschen?'; |
3223 |
+$labels['deleteparenttasktconfirm'] = 'Möchten Sie diese Aufgabe inklusive aller Teilaufgaben wirklich löschen?'; |
3224 |
+$labels['deletelistconfirm'] = 'Möchten Sie diese Liste mit allen Aufgaben wirklich löschen?'; |
3225 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/de_DE.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/de_DE.inc |
3226 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/de_DE.inc 1970-01-01 01:00:00.000000000 +0100 |
3227 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/de_DE.inc 2013-11-24 00:38:51.000000000 +0100 |
3228 |
@@ -0,0 +1,69 @@ |
3229 |
+<?php |
3230 |
+ |
3231 |
+$labels = array(); |
3232 |
+$labels['navtitle'] = 'Aufgaben'; |
3233 |
+$labels['lists'] = 'Aufgabenlisten'; |
3234 |
+$labels['list'] = 'Liste'; |
3235 |
+$labels['tags'] = 'Tags'; |
3236 |
+ |
3237 |
+$labels['newtask'] = 'Neue Aufgabe'; |
3238 |
+$labels['createnewtask'] = 'Neue Aufgabe eingeben (z.B. Samstag, Rasenmähen)'; |
3239 |
+$labels['createfrommail'] = 'Als Aufgabe speichern'; |
3240 |
+$labels['mark'] = 'Markieren'; |
3241 |
+$labels['unmark'] = 'Markierung aufheben'; |
3242 |
+$labels['edit'] = 'Bearbeiten'; |
3243 |
+$labels['delete'] = 'Löschen'; |
3244 |
+$labels['title'] = 'Titel'; |
3245 |
+$labels['description'] = 'Beschreibung'; |
3246 |
+$labels['datetime'] = 'Fällig'; |
3247 |
+$labels['start'] = 'Beginn'; |
3248 |
+$labels['alarms'] = 'Erinnerung'; |
3249 |
+ |
3250 |
+$labels['all'] = 'Alle'; |
3251 |
+$labels['flagged'] = 'Markiert'; |
3252 |
+$labels['complete'] = 'Erledigt'; |
3253 |
+$labels['overdue'] = 'Ãberfällig'; |
3254 |
+$labels['today'] = 'Heute'; |
3255 |
+$labels['tomorrow'] = 'Morgen'; |
3256 |
+$labels['next7days'] = 'Nächste 7 Tage'; |
3257 |
+$labels['later'] = 'Später'; |
3258 |
+$labels['nodate'] = 'kein Datum'; |
3259 |
+$labels['removetag'] = 'Löschen'; |
3260 |
+ |
3261 |
+$labels['taskdetails'] = 'Details'; |
3262 |
+$labels['newtask'] = 'Neue Aufgabe'; |
3263 |
+$labels['edittask'] = 'Aufgabe bearbeiten'; |
3264 |
+$labels['save'] = 'Speichern'; |
3265 |
+$labels['cancel'] = 'Abbrechen'; |
3266 |
+$labels['addsubtask'] = 'Neue Teilaufgabe'; |
3267 |
+$labels['deletetask'] = 'Aufgabe löschen'; |
3268 |
+$labels['deletethisonly'] = 'Nur diese Aufgabe löschen'; |
3269 |
+$labels['deletewithchilds'] = 'Mit allen Teilaufhaben löschen'; |
3270 |
+ |
3271 |
+ |
3272 |
+$labels['tabsummary'] = 'Ãbersicht'; |
3273 |
+$labels['tabrecurrence'] = 'Wiederholung'; |
3274 |
+$labels['tabattachments'] = 'Anhänge'; |
3275 |
+$labels['tabsharing'] = 'Freigabe'; |
3276 |
+ |
3277 |
+$labels['editlist'] = 'Liste bearbeiten'; |
3278 |
+$labels['createlist'] = 'Neue Liste'; |
3279 |
+$labels['listactions'] = 'Listenoptionen...'; |
3280 |
+$labels['listname'] = 'Name'; |
3281 |
+$labels['showalarms'] = 'Erinnerungen anzeigen'; |
3282 |
+$labels['import'] = 'Importieren'; |
3283 |
+ |
3284 |
+// date words |
3285 |
+$labels['on'] = 'am'; |
3286 |
+$labels['at'] = 'um'; |
3287 |
+$labels['this'] = 'diesen'; |
3288 |
+$labels['next'] = 'nächsten'; |
3289 |
+ |
3290 |
+// mesages |
3291 |
+$labels['savingdata'] = 'Daten werden gespeichert...'; |
3292 |
+$labels['errorsaving'] = 'Fehler beim Speichern.'; |
3293 |
+$labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.'; |
3294 |
+$labels['invalidstartduedates'] = 'Beginn der Aufgabe darf nicht gröÃer als das Enddatum sein.'; |
3295 |
+$labels['deletetasktconfirm'] = 'Möchten Sie diese Aufgabe wirklich löschen?'; |
3296 |
+$labels['deleteparenttasktconfirm'] = 'Möchten Sie diese Aufgabe inklusive aller Teilaufgaben wirklich löschen?'; |
3297 |
+$labels['deletelistconfirm'] = 'Möchten Sie diese Liste mit allen Aufgaben wirklich löschen?'; |
3298 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/en_US.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/en_US.inc |
3299 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/en_US.inc 1970-01-01 01:00:00.000000000 +0100 |
3300 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/en_US.inc 2013-11-24 00:38:51.000000000 +0100 |
3301 |
@@ -0,0 +1,68 @@ |
3302 |
+<?php |
3303 |
+ |
3304 |
+$labels = array(); |
3305 |
+$labels['navtitle'] = 'Tasks'; |
3306 |
+$labels['lists'] = 'Tasklists'; |
3307 |
+$labels['list'] = 'Tasklist'; |
3308 |
+$labels['tags'] = 'Tags'; |
3309 |
+ |
3310 |
+$labels['newtask'] = 'New Task'; |
3311 |
+$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)'; |
3312 |
+$labels['createfrommail'] = 'Save as task'; |
3313 |
+$labels['mark'] = 'Mark'; |
3314 |
+$labels['unmark'] = 'Unmark'; |
3315 |
+$labels['edit'] = 'Edit'; |
3316 |
+$labels['delete'] = 'Delete'; |
3317 |
+$labels['title'] = 'Title'; |
3318 |
+$labels['description'] = 'Description'; |
3319 |
+$labels['datetime'] = 'Due'; |
3320 |
+$labels['start'] = 'Start'; |
3321 |
+$labels['alarms'] = 'Reminder'; |
3322 |
+ |
3323 |
+$labels['all'] = 'All'; |
3324 |
+$labels['flagged'] = 'Flagged'; |
3325 |
+$labels['complete'] = 'Complete'; |
3326 |
+$labels['overdue'] = 'Overdue'; |
3327 |
+$labels['today'] = 'Today'; |
3328 |
+$labels['tomorrow'] = 'Tomorrow'; |
3329 |
+$labels['next7days'] = 'Next 7 days'; |
3330 |
+$labels['later'] = 'Later'; |
3331 |
+$labels['nodate'] = 'no date'; |
3332 |
+$labels['removetag'] = 'Remove'; |
3333 |
+ |
3334 |
+$labels['taskdetails'] = 'Details'; |
3335 |
+$labels['newtask'] = 'New Task'; |
3336 |
+$labels['edittask'] = 'Edit Task'; |
3337 |
+$labels['save'] = 'Save'; |
3338 |
+$labels['cancel'] = 'Cancel'; |
3339 |
+$labels['addsubtask'] = 'Add subtask'; |
3340 |
+$labels['deletetask'] = 'Delete task'; |
3341 |
+$labels['deletethisonly'] = 'Delete this task only'; |
3342 |
+$labels['deletewithchilds'] = 'Delete with all subtasks'; |
3343 |
+ |
3344 |
+$labels['tabsummary'] = 'Summary'; |
3345 |
+$labels['tabrecurrence'] = 'Recurrence'; |
3346 |
+$labels['tabattachments'] = 'Attachments'; |
3347 |
+$labels['tabsharing'] = 'Sharing'; |
3348 |
+ |
3349 |
+$labels['editlist'] = 'Edit list'; |
3350 |
+$labels['createlist'] = 'Add list'; |
3351 |
+$labels['listactions'] = 'List options...'; |
3352 |
+$labels['listname'] = 'Name'; |
3353 |
+$labels['showalarms'] = 'Show alarms'; |
3354 |
+$labels['import'] = 'Import'; |
3355 |
+ |
3356 |
+// date words |
3357 |
+$labels['on'] = 'on'; |
3358 |
+$labels['at'] = 'at'; |
3359 |
+$labels['this'] = 'this'; |
3360 |
+$labels['next'] = 'next'; |
3361 |
+ |
3362 |
+// mesages |
3363 |
+$labels['savingdata'] = 'Saving data...'; |
3364 |
+$labels['errorsaving'] = 'Failed to save data.'; |
3365 |
+$labels['notasksfound'] = 'No tasks found for the given criteria'; |
3366 |
+$labels['invalidstartduedates'] = 'Start date must not be greater than due date.'; |
3367 |
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?'; |
3368 |
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?'; |
3369 |
+$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?'; |
3370 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/es_ES.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/es_ES.inc |
3371 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/es_ES.inc 1970-01-01 01:00:00.000000000 +0100 |
3372 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/es_ES.inc 2013-11-24 00:38:51.000000000 +0100 |
3373 |
@@ -0,0 +1,68 @@ |
3374 |
+<?php |
3375 |
+ |
3376 |
+$labels = array(); |
3377 |
+$labels['navtitle'] = 'Tareas'; |
3378 |
+$labels['lists'] = 'Tasklists'; |
3379 |
+$labels['list'] = 'Tasklist'; |
3380 |
+$labels['tags'] = 'Tags'; |
3381 |
+ |
3382 |
+$labels['newtask'] = 'New Task'; |
3383 |
+$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)'; |
3384 |
+$labels['createfrommail'] = 'Save as task'; |
3385 |
+$labels['mark'] = 'Mark'; |
3386 |
+$labels['unmark'] = 'Unmark'; |
3387 |
+$labels['edit'] = 'Edit'; |
3388 |
+$labels['delete'] = 'Borrar'; |
3389 |
+$labels['title'] = 'TÃtulo'; |
3390 |
+$labels['description'] = 'Description'; |
3391 |
+$labels['datetime'] = 'Date/Time'; |
3392 |
+$labels['start'] = 'Start'; |
3393 |
+$labels['alarms'] = 'Reminder'; |
3394 |
+ |
3395 |
+$labels['all'] = 'All'; |
3396 |
+$labels['flagged'] = 'Flagged'; |
3397 |
+$labels['complete'] = 'Complete'; |
3398 |
+$labels['overdue'] = 'Overdue'; |
3399 |
+$labels['today'] = 'Today'; |
3400 |
+$labels['tomorrow'] = 'Tomorrow'; |
3401 |
+$labels['next7days'] = 'Next 7 days'; |
3402 |
+$labels['later'] = 'Later'; |
3403 |
+$labels['nodate'] = 'no date'; |
3404 |
+$labels['removetag'] = 'Remove'; |
3405 |
+ |
3406 |
+$labels['taskdetails'] = 'Details'; |
3407 |
+$labels['newtask'] = 'New Task'; |
3408 |
+$labels['edittask'] = 'Edit Task'; |
3409 |
+$labels['save'] = 'Guardar'; |
3410 |
+$labels['cancel'] = 'Cancel'; |
3411 |
+$labels['addsubtask'] = 'Add subtask'; |
3412 |
+$labels['deletetask'] = 'Delete task'; |
3413 |
+$labels['deletethisonly'] = 'Delete this task only'; |
3414 |
+$labels['deletewithchilds'] = 'Delete with all subtasks'; |
3415 |
+ |
3416 |
+$labels['tabsummary'] = 'Summary'; |
3417 |
+$labels['tabrecurrence'] = 'Recurrence'; |
3418 |
+$labels['tabattachments'] = 'Attachments'; |
3419 |
+$labels['tabsharing'] = 'Sharing'; |
3420 |
+ |
3421 |
+$labels['editlist'] = 'Edit list'; |
3422 |
+$labels['createlist'] = 'Add list'; |
3423 |
+$labels['listactions'] = 'List options...'; |
3424 |
+$labels['listname'] = 'Nombre'; |
3425 |
+$labels['showalarms'] = 'Show alarms'; |
3426 |
+$labels['import'] = 'Import'; |
3427 |
+ |
3428 |
+// date words |
3429 |
+$labels['on'] = 'on'; |
3430 |
+$labels['at'] = 'at'; |
3431 |
+$labels['this'] = 'this'; |
3432 |
+$labels['next'] = 'next'; |
3433 |
+ |
3434 |
+// mesages |
3435 |
+$labels['savingdata'] = 'Guardando datos...'; |
3436 |
+$labels['errorsaving'] = 'Failed to save data.'; |
3437 |
+$labels['notasksfound'] = 'No tasks found for the given criteria'; |
3438 |
+$labels['invalidstartduedates'] = 'Start date must not be greater than due date.'; |
3439 |
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?'; |
3440 |
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?'; |
3441 |
+$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?'; |
3442 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/et_EE.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/et_EE.inc |
3443 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/et_EE.inc 1970-01-01 01:00:00.000000000 +0100 |
3444 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/et_EE.inc 2013-11-24 00:38:51.000000000 +0100 |
3445 |
@@ -0,0 +1,68 @@ |
3446 |
+<?php |
3447 |
+ |
3448 |
+$labels = array(); |
3449 |
+$labels['navtitle'] = 'Tasks'; |
3450 |
+$labels['lists'] = 'Tasklists'; |
3451 |
+$labels['list'] = 'Tasklist'; |
3452 |
+$labels['tags'] = 'Tags'; |
3453 |
+ |
3454 |
+$labels['newtask'] = 'New Task'; |
3455 |
+$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)'; |
3456 |
+$labels['createfrommail'] = 'Save as task'; |
3457 |
+$labels['mark'] = 'Mark'; |
3458 |
+$labels['unmark'] = 'Unmark'; |
3459 |
+$labels['edit'] = 'Edit'; |
3460 |
+$labels['delete'] = 'Kustuta'; |
3461 |
+$labels['title'] = 'Pealkiri'; |
3462 |
+$labels['description'] = 'Description'; |
3463 |
+$labels['datetime'] = 'Date/Time'; |
3464 |
+$labels['start'] = 'Start'; |
3465 |
+$labels['alarms'] = 'Reminder'; |
3466 |
+ |
3467 |
+$labels['all'] = 'All'; |
3468 |
+$labels['flagged'] = 'Flagged'; |
3469 |
+$labels['complete'] = 'Complete'; |
3470 |
+$labels['overdue'] = 'Overdue'; |
3471 |
+$labels['today'] = 'Today'; |
3472 |
+$labels['tomorrow'] = 'Tomorrow'; |
3473 |
+$labels['next7days'] = 'Next 7 days'; |
3474 |
+$labels['later'] = 'Later'; |
3475 |
+$labels['nodate'] = 'no date'; |
3476 |
+$labels['removetag'] = 'Remove'; |
3477 |
+ |
3478 |
+$labels['taskdetails'] = 'Details'; |
3479 |
+$labels['newtask'] = 'New Task'; |
3480 |
+$labels['edittask'] = 'Edit Task'; |
3481 |
+$labels['save'] = 'Salvesta'; |
3482 |
+$labels['cancel'] = 'Cancel'; |
3483 |
+$labels['addsubtask'] = 'Add subtask'; |
3484 |
+$labels['deletetask'] = 'Delete task'; |
3485 |
+$labels['deletethisonly'] = 'Delete this task only'; |
3486 |
+$labels['deletewithchilds'] = 'Delete with all subtasks'; |
3487 |
+ |
3488 |
+$labels['tabsummary'] = 'Summary'; |
3489 |
+$labels['tabrecurrence'] = 'Recurrence'; |
3490 |
+$labels['tabattachments'] = 'Attachments'; |
3491 |
+$labels['tabsharing'] = 'Sharing'; |
3492 |
+ |
3493 |
+$labels['editlist'] = 'Edit list'; |
3494 |
+$labels['createlist'] = 'Add list'; |
3495 |
+$labels['listactions'] = 'List options...'; |
3496 |
+$labels['listname'] = 'Nimi'; |
3497 |
+$labels['showalarms'] = 'Show alarms'; |
3498 |
+$labels['import'] = 'Import'; |
3499 |
+ |
3500 |
+// date words |
3501 |
+$labels['on'] = 'on'; |
3502 |
+$labels['at'] = 'at'; |
3503 |
+$labels['this'] = 'this'; |
3504 |
+$labels['next'] = 'next'; |
3505 |
+ |
3506 |
+// mesages |
3507 |
+$labels['savingdata'] = 'Saving data...'; |
3508 |
+$labels['errorsaving'] = 'Failed to save data.'; |
3509 |
+$labels['notasksfound'] = 'No tasks found for the given criteria'; |
3510 |
+$labels['invalidstartduedates'] = 'Start date must not be greater than due date.'; |
3511 |
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?'; |
3512 |
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?'; |
3513 |
+$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?'; |
3514 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/fr_FR.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/fr_FR.inc |
3515 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/fr_FR.inc 1970-01-01 01:00:00.000000000 +0100 |
3516 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/fr_FR.inc 2013-11-24 00:38:51.000000000 +0100 |
3517 |
@@ -0,0 +1,68 @@ |
3518 |
+<?php |
3519 |
+ |
3520 |
+$labels = array(); |
3521 |
+$labels['navtitle'] = 'Tâches'; |
3522 |
+$labels['lists'] = 'Liste des tâches'; |
3523 |
+$labels['list'] = 'Liste de tâche'; |
3524 |
+$labels['tags'] = 'Ãtiquettes'; |
3525 |
+ |
3526 |
+$labels['newtask'] = 'Nouvelle tâche'; |
3527 |
+$labels['createnewtask'] = 'Créer une nouvelle tâche (ex : Samedi, tondre la pelouse)'; |
3528 |
+$labels['createfrommail'] = 'Enregistrer la tâche'; |
3529 |
+$labels['mark'] = 'Sélectionner'; |
3530 |
+$labels['unmark'] = 'Désélectionner'; |
3531 |
+$labels['edit'] = 'Modifier'; |
3532 |
+$labels['delete'] = 'Supprimet'; |
3533 |
+$labels['title'] = 'Titre'; |
3534 |
+$labels['description'] = 'Description'; |
3535 |
+$labels['datetime'] = 'Date/heure'; |
3536 |
+$labels['start'] = 'Début'; |
3537 |
+$labels['alarms'] = 'Rappel'; |
3538 |
+ |
3539 |
+$labels['all'] = 'Tous'; |
3540 |
+$labels['flagged'] = 'Marqué'; |
3541 |
+$labels['complete'] = 'Terminé'; |
3542 |
+$labels['overdue'] = 'En retard'; |
3543 |
+$labels['today'] = 'Aujourd\'hui'; |
3544 |
+$labels['tomorrow'] = 'Demain'; |
3545 |
+$labels['next7days'] = '7 prochains jours'; |
3546 |
+$labels['later'] = 'Plus tard'; |
3547 |
+$labels['nodate'] = 'Pas de date'; |
3548 |
+$labels['removetag'] = 'Supprimer'; |
3549 |
+ |
3550 |
+$labels['taskdetails'] = 'Détails'; |
3551 |
+$labels['newtask'] = 'Nouvelle tâche'; |
3552 |
+$labels['edittask'] = 'Modifier la tâche'; |
3553 |
+$labels['save'] = 'Enregistrer'; |
3554 |
+$labels['cancel'] = 'Annuler'; |
3555 |
+$labels['addsubtask'] = 'Ajouter une sous-tâche'; |
3556 |
+$labels['deletetask'] = 'Supprimer la tâche'; |
3557 |
+$labels['deletethisonly'] = 'Supprimer seulement cette tâche'; |
3558 |
+$labels['deletewithchilds'] = 'Supprimer avec toutes les sous-tâches'; |
3559 |
+ |
3560 |
+$labels['tabsummary'] = 'Résumé'; |
3561 |
+$labels['tabrecurrence'] = 'Récurrence'; |
3562 |
+$labels['tabattachments'] = 'Pièces jointes'; |
3563 |
+$labels['tabsharing'] = 'Partage'; |
3564 |
+ |
3565 |
+$labels['editlist'] = 'Modifier une liste'; |
3566 |
+$labels['createlist'] = 'Ajouter une liste'; |
3567 |
+$labels['listactions'] = 'Liste des options'; |
3568 |
+$labels['listname'] = 'Nom'; |
3569 |
+$labels['showalarms'] = 'Montrer les alarmes'; |
3570 |
+$labels['import'] = 'Importer'; |
3571 |
+ |
3572 |
+// date words |
3573 |
+$labels['on'] = 'sur'; |
3574 |
+$labels['at'] = 'Ã '; |
3575 |
+$labels['this'] = 'ce'; |
3576 |
+$labels['next'] = 'suivant'; |
3577 |
+ |
3578 |
+// mesages |
3579 |
+$labels['savingdata'] = 'Enregistrer...'; |
3580 |
+$labels['errorsaving'] = 'Ãchec de l\'enregistrement'; |
3581 |
+$labels['notasksfound'] = 'Pas de tâche trouvé avec les critères sélectionnés'; |
3582 |
+$labels['invalidstartduedates'] = 'La date de début ne doit pas être supérieur à la date de fin'; |
3583 |
+$labels['deletetasktconfirm'] = 'Ãtes-vous sur de vouloir supprimer cette tâche?'; |
3584 |
+$labels['deleteparenttasktconfirm'] = 'Ãtes-vous sur de vouloir supprimer cette tâche et ses sous-tâches?'; |
3585 |
+$labels['deletelistconfirm'] = 'Ãtes-vous sur de vouloir supprimer cette liste et toutes ses tâches?'; |
3586 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/ja_JP.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/ja_JP.inc |
3587 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/ja_JP.inc 1970-01-01 01:00:00.000000000 +0100 |
3588 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/ja_JP.inc 2013-11-24 00:38:52.000000000 +0100 |
3589 |
@@ -0,0 +1,68 @@ |
3590 |
+<?php |
3591 |
+ |
3592 |
+$labels = array(); |
3593 |
+$labels['navtitle'] = 'Tasks'; |
3594 |
+$labels['lists'] = 'Tasklists'; |
3595 |
+$labels['list'] = 'Tasklist'; |
3596 |
+$labels['tags'] = 'Tags'; |
3597 |
+ |
3598 |
+$labels['newtask'] = 'New Task'; |
3599 |
+$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)'; |
3600 |
+$labels['createfrommail'] = 'Save as task'; |
3601 |
+$labels['mark'] = 'Mark'; |
3602 |
+$labels['unmark'] = 'Unmark'; |
3603 |
+$labels['edit'] = 'Edit'; |
3604 |
+$labels['delete'] = 'Delete'; |
3605 |
+$labels['title'] = 'Title'; |
3606 |
+$labels['description'] = 'Description'; |
3607 |
+$labels['datetime'] = 'Date/Time'; |
3608 |
+$labels['start'] = 'Start'; |
3609 |
+$labels['alarms'] = 'Reminder'; |
3610 |
+ |
3611 |
+$labels['all'] = 'All'; |
3612 |
+$labels['flagged'] = 'Flagged'; |
3613 |
+$labels['complete'] = 'Complete'; |
3614 |
+$labels['overdue'] = 'Overdue'; |
3615 |
+$labels['today'] = 'Today'; |
3616 |
+$labels['tomorrow'] = 'Tomorrow'; |
3617 |
+$labels['next7days'] = 'Next 7 days'; |
3618 |
+$labels['later'] = 'Later'; |
3619 |
+$labels['nodate'] = 'no date'; |
3620 |
+$labels['removetag'] = 'Remove'; |
3621 |
+ |
3622 |
+$labels['taskdetails'] = 'Details'; |
3623 |
+$labels['newtask'] = 'New Task'; |
3624 |
+$labels['edittask'] = 'Edit Task'; |
3625 |
+$labels['save'] = 'Save'; |
3626 |
+$labels['cancel'] = 'Cancel'; |
3627 |
+$labels['addsubtask'] = 'Add subtask'; |
3628 |
+$labels['deletetask'] = 'Delete task'; |
3629 |
+$labels['deletethisonly'] = 'Delete this task only'; |
3630 |
+$labels['deletewithchilds'] = 'Delete with all subtasks'; |
3631 |
+ |
3632 |
+$labels['tabsummary'] = 'Summary'; |
3633 |
+$labels['tabrecurrence'] = 'Recurrence'; |
3634 |
+$labels['tabattachments'] = 'Attachments'; |
3635 |
+$labels['tabsharing'] = 'Sharing'; |
3636 |
+ |
3637 |
+$labels['editlist'] = 'Edit list'; |
3638 |
+$labels['createlist'] = 'Add list'; |
3639 |
+$labels['listactions'] = 'List options...'; |
3640 |
+$labels['listname'] = 'Name'; |
3641 |
+$labels['showalarms'] = 'Show alarms'; |
3642 |
+$labels['import'] = 'Import'; |
3643 |
+ |
3644 |
+// date words |
3645 |
+$labels['on'] = 'on'; |
3646 |
+$labels['at'] = 'at'; |
3647 |
+$labels['this'] = 'this'; |
3648 |
+$labels['next'] = 'next'; |
3649 |
+ |
3650 |
+// mesages |
3651 |
+$labels['savingdata'] = 'Saving data...'; |
3652 |
+$labels['errorsaving'] = 'Failed to save data.'; |
3653 |
+$labels['notasksfound'] = 'No tasks found for the given criteria'; |
3654 |
+$labels['invalidstartduedates'] = 'Start date must not be greater than due date.'; |
3655 |
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?'; |
3656 |
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?'; |
3657 |
+$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?'; |
3658 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/nl_NL.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/nl_NL.inc |
3659 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/nl_NL.inc 1970-01-01 01:00:00.000000000 +0100 |
3660 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/nl_NL.inc 2013-11-24 00:38:52.000000000 +0100 |
3661 |
@@ -0,0 +1,68 @@ |
3662 |
+<?php |
3663 |
+ |
3664 |
+$labels = array(); |
3665 |
+$labels['navtitle'] = 'Taken'; |
3666 |
+$labels['lists'] = 'Takenlijsten'; |
3667 |
+$labels['list'] = 'Takenlijst'; |
3668 |
+$labels['tags'] = 'Tags'; |
3669 |
+ |
3670 |
+$labels['newtask'] = 'Nieuwe Taak'; |
3671 |
+$labels['createnewtask'] = 'Creer een nieuwe Taak (bijv. Zaterdag, gras maaien)'; |
3672 |
+$labels['createfrommail'] = 'Als taak opslaan'; |
3673 |
+$labels['mark'] = 'Markeren'; |
3674 |
+$labels['unmark'] = 'Demarkeren'; |
3675 |
+$labels['edit'] = 'Wijzigen'; |
3676 |
+$labels['delete'] = 'Verwijderen'; |
3677 |
+$labels['title'] = 'Titel'; |
3678 |
+$labels['description'] = 'Beschrijving'; |
3679 |
+$labels['datetime'] = 'Datum/Tijd'; |
3680 |
+$labels['start'] = 'Begin'; |
3681 |
+$labels['alarms'] = 'Herinnering'; |
3682 |
+ |
3683 |
+$labels['all'] = 'Alle'; |
3684 |
+$labels['flagged'] = 'Gemarkeerd'; |
3685 |
+$labels['complete'] = 'Compleet'; |
3686 |
+$labels['overdue'] = 'Achterstallig'; |
3687 |
+$labels['today'] = 'Vandaag'; |
3688 |
+$labels['tomorrow'] = 'Morgen'; |
3689 |
+$labels['next7days'] = 'Deze week'; |
3690 |
+$labels['later'] = 'Later'; |
3691 |
+$labels['nodate'] = 'geen datum'; |
3692 |
+$labels['removetag'] = 'Verwijder'; |
3693 |
+ |
3694 |
+$labels['taskdetails'] = 'Details'; |
3695 |
+$labels['newtask'] = 'Nieuwe Taak'; |
3696 |
+$labels['edittask'] = 'Taak Wijzigen'; |
3697 |
+$labels['save'] = 'Opslaan'; |
3698 |
+$labels['cancel'] = 'Annuleren'; |
3699 |
+$labels['addsubtask'] = 'Voeg subtaak toe'; |
3700 |
+$labels['deletetask'] = 'Verwijder taak'; |
3701 |
+$labels['deletethisonly'] = 'Verwijder alleen deze taak'; |
3702 |
+$labels['deletewithchilds'] = 'Verwijder inclusief subtaken'; |
3703 |
+ |
3704 |
+$labels['tabsummary'] = 'Samenvatting'; |
3705 |
+$labels['tabrecurrence'] = 'Herhaling'; |
3706 |
+$labels['tabattachments'] = 'Toebehoren'; |
3707 |
+$labels['tabsharing'] = 'Delen'; |
3708 |
+ |
3709 |
+$labels['editlist'] = 'Lijst wijzigen'; |
3710 |
+$labels['createlist'] = 'Lijst toevoegen'; |
3711 |
+$labels['listactions'] = 'Lijst opties...'; |
3712 |
+$labels['listname'] = 'Naam'; |
3713 |
+$labels['showalarms'] = 'Alarmen tonen'; |
3714 |
+$labels['import'] = 'Importeren'; |
3715 |
+ |
3716 |
+// date words |
3717 |
+$labels['on'] = 'op'; |
3718 |
+$labels['at'] = 'om'; |
3719 |
+$labels['this'] = 'deze'; |
3720 |
+$labels['next'] = 'volgende'; |
3721 |
+ |
3722 |
+// mesages |
3723 |
+$labels['savingdata'] = 'Data wordt opgeslagen...'; |
3724 |
+$labels['errorsaving'] = 'Fout in opslaan van data.'; |
3725 |
+$labels['notasksfound'] = 'Geen taken gevonden voor de gegeven criteria'; |
3726 |
+$labels['invalidstartduedates'] = 'Begin datum kan niet later zijn dan vervaldag.'; |
3727 |
+$labels['deletetasktconfirm'] = 'Wilt u deze taak echt verwijderen?'; |
3728 |
+$labels['deleteparenttasktconfirm'] = 'Wilt u deze taak en alle subtaken echt verwijderen?'; |
3729 |
+$labels['deletelistconfirm'] = 'Wilt u deze lijst met alle taken echt verwijderen?'; |
3730 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/pl_PL.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/pl_PL.inc |
3731 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/pl_PL.inc 1970-01-01 01:00:00.000000000 +0100 |
3732 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/pl_PL.inc 2013-11-24 00:38:52.000000000 +0100 |
3733 |
@@ -0,0 +1,68 @@ |
3734 |
+<?php |
3735 |
+ |
3736 |
+$labels = array(); |
3737 |
+$labels['navtitle'] = 'Tasks'; |
3738 |
+$labels['lists'] = 'Tasklists'; |
3739 |
+$labels['list'] = 'Tasklist'; |
3740 |
+$labels['tags'] = 'Tags'; |
3741 |
+ |
3742 |
+$labels['newtask'] = 'New Task'; |
3743 |
+$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)'; |
3744 |
+$labels['createfrommail'] = 'Save as task'; |
3745 |
+$labels['mark'] = 'Mark'; |
3746 |
+$labels['unmark'] = 'Unmark'; |
3747 |
+$labels['edit'] = 'Edit'; |
3748 |
+$labels['delete'] = 'UsuÅ'; |
3749 |
+$labels['title'] = 'Title'; |
3750 |
+$labels['description'] = 'Description'; |
3751 |
+$labels['datetime'] = 'Date/Time'; |
3752 |
+$labels['start'] = 'Start'; |
3753 |
+$labels['alarms'] = 'Reminder'; |
3754 |
+ |
3755 |
+$labels['all'] = 'All'; |
3756 |
+$labels['flagged'] = 'Flagged'; |
3757 |
+$labels['complete'] = 'Complete'; |
3758 |
+$labels['overdue'] = 'Overdue'; |
3759 |
+$labels['today'] = 'Today'; |
3760 |
+$labels['tomorrow'] = 'Tomorrow'; |
3761 |
+$labels['next7days'] = 'Next 7 days'; |
3762 |
+$labels['later'] = 'Later'; |
3763 |
+$labels['nodate'] = 'no date'; |
3764 |
+$labels['removetag'] = 'Remove'; |
3765 |
+ |
3766 |
+$labels['taskdetails'] = 'Details'; |
3767 |
+$labels['newtask'] = 'New Task'; |
3768 |
+$labels['edittask'] = 'Edit Task'; |
3769 |
+$labels['save'] = 'Save'; |
3770 |
+$labels['cancel'] = 'Cancel'; |
3771 |
+$labels['addsubtask'] = 'Add subtask'; |
3772 |
+$labels['deletetask'] = 'Delete task'; |
3773 |
+$labels['deletethisonly'] = 'Delete this task only'; |
3774 |
+$labels['deletewithchilds'] = 'Delete with all subtasks'; |
3775 |
+ |
3776 |
+$labels['tabsummary'] = 'Summary'; |
3777 |
+$labels['tabrecurrence'] = 'Recurrence'; |
3778 |
+$labels['tabattachments'] = 'Attachments'; |
3779 |
+$labels['tabsharing'] = 'Sharing'; |
3780 |
+ |
3781 |
+$labels['editlist'] = 'Edit list'; |
3782 |
+$labels['createlist'] = 'Add list'; |
3783 |
+$labels['listactions'] = 'List options...'; |
3784 |
+$labels['listname'] = 'Nazwa'; |
3785 |
+$labels['showalarms'] = 'Show alarms'; |
3786 |
+$labels['import'] = 'Import'; |
3787 |
+ |
3788 |
+// date words |
3789 |
+$labels['on'] = 'on'; |
3790 |
+$labels['at'] = 'at'; |
3791 |
+$labels['this'] = 'this'; |
3792 |
+$labels['next'] = 'next'; |
3793 |
+ |
3794 |
+// mesages |
3795 |
+$labels['savingdata'] = 'ZapisujÄ dane...'; |
3796 |
+$labels['errorsaving'] = 'Failed to save data.'; |
3797 |
+$labels['notasksfound'] = 'No tasks found for the given criteria'; |
3798 |
+$labels['invalidstartduedates'] = 'Start date must not be greater than due date.'; |
3799 |
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?'; |
3800 |
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?'; |
3801 |
+$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?'; |
3802 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/ru_RU.inc roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/ru_RU.inc |
3803 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/localization/ru_RU.inc 1970-01-01 01:00:00.000000000 +0100 |
3804 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/localization/ru_RU.inc 2013-11-24 00:38:52.000000000 +0100 |
3805 |
@@ -0,0 +1,68 @@ |
3806 |
+<?php |
3807 |
+ |
3808 |
+$labels = array(); |
3809 |
+$labels['navtitle'] = 'ÐадаÑи'; |
3810 |
+$labels['lists'] = 'СпиÑки задаÑ'; |
3811 |
+$labels['list'] = 'СпиÑок задаÑ'; |
3812 |
+$labels['tags'] = 'Теги'; |
3813 |
+ |
3814 |
+$labels['newtask'] = 'ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð´Ð°Ñа'; |
3815 |
+$labels['createnewtask'] = 'СоздаÑÑ Ð½Ð¾Ð²ÑÑ Ð·Ð°Ð´Ð°ÑÑ (пÑимеÑ: СÑббоÑа, покоÑиÑÑ Ð³Ð°Ð·Ð¾Ð½)'; |
3816 |
+$labels['createfrommail'] = 'СоÑ
ÑаниÑÑ ÐºÐ°Ðº задаÑÑ'; |
3817 |
+$labels['mark'] = 'ÐÑмеÑиÑÑ'; |
3818 |
+$labels['unmark'] = 'СнÑÑÑ'; |
3819 |
+$labels['edit'] = 'РедакÑиÑоваÑÑ'; |
3820 |
+$labels['delete'] = 'УдалиÑÑ'; |
3821 |
+$labels['title'] = 'Ðаголовок'; |
3822 |
+$labels['description'] = 'ÐпиÑание'; |
3823 |
+$labels['datetime'] = 'ÐаÑа/ÐÑемÑ'; |
3824 |
+$labels['start'] = 'ÐаÑало'; |
3825 |
+$labels['alarms'] = 'Ðапоминание'; |
3826 |
+ |
3827 |
+$labels['all'] = 'ÐÑе'; |
3828 |
+$labels['flagged'] = 'ÐÑмеÑеннÑе'; |
3829 |
+$labels['complete'] = 'ÐавеÑÑеннÑе'; |
3830 |
+$labels['overdue'] = 'ÐÑоÑÑоÑеннÑе'; |
3831 |
+$labels['today'] = 'СегоднÑ'; |
3832 |
+$labels['tomorrow'] = 'ÐавÑÑа'; |
3833 |
+$labels['next7days'] = 'ÐлижайÑÐ°Ñ Ð½ÐµÐ´ÐµÐ»Ñ'; |
3834 |
+$labels['later'] = 'Ðозднее'; |
3835 |
+$labels['nodate'] = 'без даÑÑ'; |
3836 |
+$labels['removetag'] = 'УбÑаÑÑ'; |
3837 |
+ |
3838 |
+$labels['taskdetails'] = 'ÐодÑобнее'; |
3839 |
+$labels['newtask'] = 'ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð´Ð°Ñа'; |
3840 |
+$labels['edittask'] = 'РедакÑиÑоваÑÑ Ð·Ð°Ð´Ð°ÑÑ'; |
3841 |
+$labels['save'] = 'СоÑ
ÑаниÑÑ'; |
3842 |
+$labels['cancel'] = 'ÐÑмена'; |
3843 |
+$labels['addsubtask'] = 'ÐобавиÑÑ Ð¿Ð¾Ð´Ð·Ð°Ð´Ð°ÑÑ'; |
3844 |
+$labels['deletetask'] = 'УдалиÑÑ Ð·Ð°Ð´Ð°ÑÑ'; |
3845 |
+$labels['deletethisonly'] = 'УдалиÑÑ ÑолÑко ÑÑÑ Ð·Ð°Ð´Ð°ÑÑ'; |
3846 |
+$labels['deletewithchilds'] = 'УдалиÑÑ Ð·Ð°Ð´Ð°ÑÑ Ð¸ вÑе ее подзадаÑи'; |
3847 |
+ |
3848 |
+$labels['tabsummary'] = 'Сводка'; |
3849 |
+$labels['tabrecurrence'] = 'ÐовÑоÑ'; |
3850 |
+$labels['tabattachments'] = 'ÐложениÑ'; |
3851 |
+$labels['tabsharing'] = 'ÐоделиÑÑÑÑ'; |
3852 |
+ |
3853 |
+$labels['editlist'] = 'ÐзмениÑÑ ÑпиÑок'; |
3854 |
+$labels['createlist'] = 'ÐобавиÑÑ ÑпиÑок'; |
3855 |
+$labels['listactions'] = 'ÐейÑÑÐ²Ð¸Ñ Ñо ÑпиÑком...'; |
3856 |
+$labels['listname'] = 'Ðазвание'; |
3857 |
+$labels['showalarms'] = 'ÐоказÑваÑÑ ÑведомлениÑ'; |
3858 |
+$labels['import'] = 'ÐмпоÑÑ'; |
3859 |
+ |
3860 |
+// date words |
3861 |
+$labels['on'] = 'в'; |
3862 |
+$labels['at'] = 'в'; |
3863 |
+$labels['this'] = 'ÑÑоÑ'; |
3864 |
+$labels['next'] = 'ÑледÑÑÑий'; |
3865 |
+ |
3866 |
+// mesages |
3867 |
+$labels['savingdata'] = 'СоÑ
Ñанение даннÑÑ
...'; |
3868 |
+$labels['errorsaving'] = 'Ðе ÑдалоÑÑ ÑоÑ
ÑаниÑÑ.'; |
3869 |
+$labels['notasksfound'] = 'Ðо Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð¼Ñ ÑÑÐ»Ð¾Ð²Ð¸Ñ Ð½Ðµ найдено задаÑ'; |
3870 |
+$labels['invalidstartduedates'] = 'ÐаÑалÑÐ½Ð°Ñ Ð´Ð°Ñа не Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑевÑÑаÑÑ ÐºÐ¾Ð½ÐµÑнÑÑ.'; |
3871 |
+$labels['deletetasktconfirm'] = 'ÐÑ Ð´ÐµÐ¹ÑÑвиÑелÑно Ñ
оÑиÑе ÑдалиÑÑ ÑÑÑ Ð·Ð°Ð´Ð°ÑÑ?'; |
3872 |
+$labels['deleteparenttasktconfirm'] = 'ÐÑ Ð´ÐµÐ¹ÑÑвиÑелÑно Ñ
оÑиÑе ÑдалиÑÑ ÑÑÑ Ð·Ð°Ð´Ð°ÑÑ Ð¸ вÑе ее подзадаÑи?'; |
3873 |
+$labels['deletelistconfirm'] = 'ÐÑ Ð´ÐµÐ¹ÑÑвиÑелÑно Ñ
оÑиÑе ÑдалиÑÑ ÑÑÐ¾Ñ ÑпиÑок и вÑе его задаÑи?'; |
3874 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/package.xml roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/package.xml |
3875 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/package.xml 1970-01-01 01:00:00.000000000 +0100 |
3876 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/package.xml 2013-11-24 00:38:51.000000000 +0100 |
3877 |
@@ -0,0 +1,92 @@ |
3878 |
+<?xml version="1.0" encoding="UTF-8"?> |
3879 |
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 |
3880 |
+ http://pear.php.net/dtd/tasks-1.0.xsd |
3881 |
+ http://pear.php.net/dtd/package-2.0 |
3882 |
+ http://pear.php.net/dtd/package-2.0.xsd"> |
3883 |
+ <name>tasklist</name> |
3884 |
+ <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri> |
3885 |
+ <summary>Task management plugin</summary> |
3886 |
+ <description>-</description> |
3887 |
+ <lead> |
3888 |
+ <name>Thomas Bruederli</name> |
3889 |
+ <user>bruederli</user> |
3890 |
+ <email>bruederli@kolabsys.com</email> |
3891 |
+ <active>yes</active> |
3892 |
+ </lead> |
3893 |
+ <date>2012-11-21</date> |
3894 |
+ <version> |
3895 |
+ <release>0.9-beta</release> |
3896 |
+ <api>0.9-beta</api> |
3897 |
+ </version> |
3898 |
+ <stability> |
3899 |
+ <release>beta</release> |
3900 |
+ <api>beta</api> |
3901 |
+ </stability> |
3902 |
+ <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license> |
3903 |
+ <notes>-</notes> |
3904 |
+ <contents> |
3905 |
+ <dir baseinstalldir="/" name="/"> |
3906 |
+ <file name="tasklist.php" role="php"> |
3907 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3908 |
+ </file> |
3909 |
+ <file name="tasklist_ui.php" role="php"> |
3910 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3911 |
+ </file> |
3912 |
+ <file name="tasklist_base.js" role="data"> |
3913 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3914 |
+ </file> |
3915 |
+ <file name="tasklist.js" role="data"> |
3916 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3917 |
+ </file> |
3918 |
+ <file name="jquery.tagedit.js" role="data"> |
3919 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3920 |
+ </file> |
3921 |
+ <file name="drivers/tasklist_driver.php" role="php"> |
3922 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3923 |
+ </file> |
3924 |
+ <file name="drivers/kolab/tasklist_kolab_driver.php" role="php"> |
3925 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3926 |
+ </file> |
3927 |
+ <file name="drivers/database/tasklist_database_driver.php" role="php"> |
3928 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3929 |
+ </file> |
3930 |
+ <file name="drivers/database/SQL/mysql.sql" role="data"> |
3931 |
+ <tasks:replace from="@package_version@" to="version" type="package-info"/> |
3932 |
+ </file> |
3933 |
+ |
3934 |
+ <file name="config.inc.php.dist" role="data"></file> |
3935 |
+ <file name="LICENSE" role="data"></file> |
3936 |
+ |
3937 |
+ <file name="localization/de_CH.inc" role="data"></file> |
3938 |
+ <file name="localization/en_US.inc" role="data"></file> |
3939 |
+ |
3940 |
+ <file name="skins/larry/tasklist.css" role="data"></file> |
3941 |
+ <file name="skins/larry/iehacks.css" role="data"></file> |
3942 |
+ <file name="skins/larry/buttons.png" role="data"></file> |
3943 |
+ <file name="skins/larry/sprites.png" role="data"></file> |
3944 |
+ <file name="skins/larry/templates/mainview.html" role="data"></file> |
3945 |
+ <file name="skins/larry/templates/taskedit.html" role="data"></file> |
3946 |
+ <file name="skins/larry/templates/attachment.html" role="data"></file> |
3947 |
+ </dir> |
3948 |
+ <!-- / --> |
3949 |
+ </contents> |
3950 |
+ <dependencies> |
3951 |
+ <required> |
3952 |
+ <php> |
3953 |
+ <min>5.3.1</min> |
3954 |
+ </php> |
3955 |
+ <pearinstaller> |
3956 |
+ <min>1.7.0</min> |
3957 |
+ </pearinstaller> |
3958 |
+ <package> |
3959 |
+ <name>libkolab</name> |
3960 |
+ <uri>http://git.kolab.org/roundcubemail-plugins-kolab/tree/plugins/libkolab</uri> |
3961 |
+ </package> |
3962 |
+ <package> |
3963 |
+ <name>libcalendaring</name> |
3964 |
+ <uri>http://git.kolab.org/roundcubemail-plugins-kolab/tree/plugins/libcalendaring</uri> |
3965 |
+ </package> |
3966 |
+ </required> |
3967 |
+ </dependencies> |
3968 |
+ <phprelease/> |
3969 |
+</package> |
3970 |
Les fichiers binaires roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/buttons.png et roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/buttons.png sont différents. |
3971 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/iehacks.css roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/iehacks.css |
3972 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/iehacks.css 1970-01-01 01:00:00.000000000 +0100 |
3973 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/iehacks.css 2013-11-24 00:38:52.000000000 +0100 |
3974 |
@@ -0,0 +1,90 @@ |
3975 |
+/** |
3976 |
+ * Roundcube Taklist plugin CSS hacks for IE < 9 |
3977 |
+ * |
3978 |
+ * Copyright (c) 2012, Kolab Systems AG <contact@kolabsys.com> |
3979 |
+ * |
3980 |
+ * The contents are subject to the Creative Commons Attribution-ShareAlike |
3981 |
+ * License. It is allowed to copy, distribute, transmit and to adapt the work |
3982 |
+ * by keeping credits to the original autors in the README file. |
3983 |
+ * See http://creativecommons.org/licenses/by-sa/3.0/ for details. |
3984 |
+ * |
3985 |
+ * $Id$ |
3986 |
+ */ |
3987 |
+ |
3988 |
+#tasksview { |
3989 |
+ background: transparent; |
3990 |
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#33ffffff,endColorstr=#33ffffff); |
3991 |
+ zoom: 1; |
3992 |
+} |
3993 |
+ |
3994 |
+#tasksview .buttonbar { |
3995 |
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dfdfdf', GradientType=0); |
3996 |
+} |
3997 |
+ |
3998 |
+html.ie #taskselector li .count:after { |
3999 |
+ bottom: -4px; |
4000 |
+} |
4001 |
+ |
4002 |
+#thelist .taskitem.dragging .taskhead { |
4003 |
+ filter: alpha(opacity=50); |
4004 |
+} |
4005 |
+ |
4006 |
+#thelist .taskhead.complete { |
4007 |
+ filter: alpha(opacity=60); |
4008 |
+} |
4009 |
+ |
4010 |
+#thelist .taskhead { |
4011 |
+ filter: progid:DXImageTransform.Microsoft.Shadow(color=#33666666,direction=180,strength=2); |
4012 |
+} |
4013 |
+ |
4014 |
+/*** Special hacks for IE7 only ***/ |
4015 |
+ |
4016 |
+html.ie7 #taskselector li .count { |
4017 |
+ position: relative; |
4018 |
+ top: 0px; |
4019 |
+ left: 5px; |
4020 |
+} |
4021 |
+ |
4022 |
+html.ie7 #taskselector li.selected .count { |
4023 |
+ background: #d9ecf4; |
4024 |
+ color: #004458; |
4025 |
+} |
4026 |
+ |
4027 |
+html.ie7 #taskselector li.selected.overdue .count { |
4028 |
+ background: #ff3800; |
4029 |
+ color: #fff; |
4030 |
+} |
4031 |
+ |
4032 |
+html.ie7 #tagslist li, |
4033 |
+html.ie7 #taskselector li { |
4034 |
+ float: left; |
4035 |
+} |
4036 |
+ |
4037 |
+html.ie7 .taskitem { |
4038 |
+ |
4039 |
+} |
4040 |
+ |
4041 |
+html.ie7 .taskhead .title { |
4042 |
+ position: relative; |
4043 |
+ top: -3px; |
4044 |
+} |
4045 |
+ |
4046 |
+html.ie7 .taskitem .childtoggle { |
4047 |
+ display: block; |
4048 |
+ /* workaround for text-indent which also offsets the background image */ |
4049 |
+ text-indent: 0; |
4050 |
+ font-size: 0; |
4051 |
+ line-height: 0; |
4052 |
+ text-align: right; |
4053 |
+ text-decoration: none; |
4054 |
+} |
4055 |
+ |
4056 |
+html.ie7 .taskhead .tags { |
4057 |
+ height: 18px; |
4058 |
+} |
4059 |
+ |
4060 |
+html.ie7 .taskhead .tags .tag { |
4061 |
+ display: inline-block; |
4062 |
+ padding-bottom: 2px; |
4063 |
+} |
4064 |
+ |
4065 |
Les fichiers binaires roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/sprites.png et roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/sprites.png sont différents. |
4066 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/tasklist.css roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/tasklist.css |
4067 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/tasklist.css 1970-01-01 01:00:00.000000000 +0100 |
4068 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/tasklist.css 2013-11-24 00:38:52.000000000 +0100 |
4069 |
@@ -0,0 +1,836 @@ |
4070 |
+/** |
4071 |
+ * Roundcube Taklist plugin styles for skin "Larry" |
4072 |
+ * |
4073 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
4074 |
+ * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com |
4075 |
+ * |
4076 |
+ * The contents are subject to the Creative Commons Attribution-ShareAlike |
4077 |
+ * License. It is allowed to copy, distribute, transmit and to adapt the work |
4078 |
+ * by keeping credits to the original autors in the README file. |
4079 |
+ * See http://creativecommons.org/licenses/by-sa/3.0/ for details. |
4080 |
+ */ |
4081 |
+ |
4082 |
+#taskbar a.button-tasklist span.button-inner { |
4083 |
+ background-image: url(buttons.png); |
4084 |
+ background-position: 0 0; |
4085 |
+} |
4086 |
+ |
4087 |
+#taskbar a.button-tasklist:hover span.button-inner, |
4088 |
+#taskbar a.button-tasklist.button-selected span.button-inner { |
4089 |
+ background-position: 0 -26px; |
4090 |
+} |
4091 |
+ |
4092 |
+ul.toolbarmenu li span.icon.taskadd { |
4093 |
+ background-image: url(buttons.png); |
4094 |
+ background-position: -4px -90px; |
4095 |
+} |
4096 |
+ |
4097 |
+div.uidialog { |
4098 |
+ display: none; |
4099 |
+} |
4100 |
+ |
4101 |
+body.attachmentwin #mainscreen { |
4102 |
+ top: 60px; |
4103 |
+} |
4104 |
+ |
4105 |
+body.attachmentwin #topnav .topright { |
4106 |
+ margin-top: 20px; |
4107 |
+} |
4108 |
+ |
4109 |
+#sidebar { |
4110 |
+ position: absolute; |
4111 |
+ top: 0; |
4112 |
+ left: 0; |
4113 |
+ bottom: 0; |
4114 |
+ width: 240px; |
4115 |
+} |
4116 |
+ |
4117 |
+.tasklistview #searchmenulink { |
4118 |
+ width: 15px; |
4119 |
+} |
4120 |
+ |
4121 |
+#tagsbox { |
4122 |
+ position: absolute; |
4123 |
+ top: 42px; |
4124 |
+ left: 0; |
4125 |
+ width: 100%; |
4126 |
+ height: 242px; |
4127 |
+} |
4128 |
+ |
4129 |
+#tasklistsbox { |
4130 |
+ position: absolute; |
4131 |
+ top: 300px; |
4132 |
+ left: 0; |
4133 |
+ width: 100%; |
4134 |
+ bottom: 0px; |
4135 |
+} |
4136 |
+ |
4137 |
+#taskselector { |
4138 |
+ margin: -4px 0 0; |
4139 |
+ padding: 0; |
4140 |
+} |
4141 |
+ |
4142 |
+#taskselector li { |
4143 |
+ display: inline-block; |
4144 |
+ position: relative; |
4145 |
+ font-size: 90%; |
4146 |
+ padding-right: 0.3em; |
4147 |
+} |
4148 |
+ |
4149 |
+#tagslist li, |
4150 |
+#taskselector li a { |
4151 |
+ display: inline-block; |
4152 |
+ color: #004458; |
4153 |
+ min-width: 4em; |
4154 |
+ padding: 0.2em 0.6em 0.3em 0.6em; |
4155 |
+ text-align: center; |
4156 |
+ text-decoration: none; |
4157 |
+ border: 1px solid #eee; |
4158 |
+ border-color: transparent; |
4159 |
+} |
4160 |
+ |
4161 |
+#taskselector li:first-child { |
4162 |
+ border-top: 0; |
4163 |
+ border-radius: 4px 4px 0 0; |
4164 |
+} |
4165 |
+ |
4166 |
+#taskselector li:last-child { |
4167 |
+ border-bottom: 0; |
4168 |
+ border-radius: 0 0 4px 4px; |
4169 |
+} |
4170 |
+ |
4171 |
+#taskselector li.overdue a { |
4172 |
+ color: #b72a2a; |
4173 |
+ font-weight: bold; |
4174 |
+} |
4175 |
+ |
4176 |
+#taskselector li.inactive a { |
4177 |
+ color: #97b3bf; |
4178 |
+} |
4179 |
+ |
4180 |
+#tagslist li.selected, |
4181 |
+#taskselector li.selected a { |
4182 |
+ color: #fff; |
4183 |
+ background: #005d76; |
4184 |
+ background: -moz-linear-gradient(top, #005d76 0%, #004558 100%); |
4185 |
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#005d76), color-stop(100%,#004558)); |
4186 |
+ background: -o-linear-gradient(top, #005d76 0%, #004558 100%); |
4187 |
+ background: -ms-linear-gradient(top, #005d76 0%, #004558 100%); |
4188 |
+ background: linear-gradient(top, #005d76 0%, #004558 100%); |
4189 |
+ box-shadow: inset 0 1px 1px 0 #003645; |
4190 |
+ -o-box-shadow: inset 0 1px 1px 0 #003645; |
4191 |
+ -webkit-box-shadow: inset 0 1px 1px 0 #003645; |
4192 |
+ -moz-box-shadow: inset 0 1px 1px 0 #003645; |
4193 |
+ border-color: #003645; |
4194 |
+ border-radius: 9px; |
4195 |
+ text-shadow: none; |
4196 |
+ outline: none; |
4197 |
+} |
4198 |
+ |
4199 |
+#taskselector li .count { |
4200 |
+ display: none; |
4201 |
+ position: absolute; |
4202 |
+ top: -18px; |
4203 |
+ right: 5px; |
4204 |
+ min-width: 1.8em; |
4205 |
+ padding: 2px 4px; |
4206 |
+ background: #004558; |
4207 |
+ background: -moz-linear-gradient(top, #005d76 0%, #004558 100%); |
4208 |
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#005d76), color-stop(100%,#004558)); |
4209 |
+ background: -o-linear-gradient(top, #005d76 0%, #004558 100%); |
4210 |
+ background: -ms-linear-gradient(top, #005d76 0%, #004558 100%); |
4211 |
+ background: linear-gradient(top, #005d76 0%, #004558 100%); |
4212 |
+ box-shadow: 0 1px 2px 0 rgba(24,24,24,0.6); |
4213 |
+ color: #fff; |
4214 |
+ border-radius: 3px; |
4215 |
+ text-align: center; |
4216 |
+ font-weight: bold; |
4217 |
+ font-size: 80%; |
4218 |
+ text-shadow: none; |
4219 |
+} |
4220 |
+ |
4221 |
+#taskselector li .count:after { |
4222 |
+ content: ""; |
4223 |
+ position: absolute; |
4224 |
+ bottom: -5px; |
4225 |
+ left: 50%; |
4226 |
+ margin-left: -5px; |
4227 |
+ border-style: solid; |
4228 |
+ border-width: 5px 5px 0; |
4229 |
+ border-color: #004558 transparent; |
4230 |
+ /* reduce the damage in FF3.0 */ |
4231 |
+ display: block; |
4232 |
+ width: 0; |
4233 |
+} |
4234 |
+ |
4235 |
+#taskselector li.overdue .count { |
4236 |
+ background: #ff3800; |
4237 |
+} |
4238 |
+ |
4239 |
+#taskselector li.overdue .count:after { |
4240 |
+ border-color: #ff3800 transparent; |
4241 |
+} |
4242 |
+ |
4243 |
+#tagslist { |
4244 |
+ padding: 0; |
4245 |
+ margin: 6px; |
4246 |
+ list-style: none; |
4247 |
+} |
4248 |
+ |
4249 |
+#tagslist li { |
4250 |
+ display: inline-block; |
4251 |
+ color: #004458; |
4252 |
+ margin-right: 0.5em; |
4253 |
+ margin-bottom: 0.4em; |
4254 |
+ min-width: 1.2em; |
4255 |
+ cursor: pointer; |
4256 |
+} |
4257 |
+ |
4258 |
+#tasklists li { |
4259 |
+ margin: 0; |
4260 |
+ height: 20px; |
4261 |
+ padding: 6px 8px 2px; |
4262 |
+ display: block; |
4263 |
+ position: relative; |
4264 |
+ white-space: nowrap; |
4265 |
+} |
4266 |
+ |
4267 |
+#tasklists li label { |
4268 |
+ display: block; |
4269 |
+} |
4270 |
+ |
4271 |
+#tasklists li span.listname { |
4272 |
+ display: block; |
4273 |
+ cursor: default; |
4274 |
+ padding-bottom: 2px; |
4275 |
+ padding-right: 30px; |
4276 |
+ margin-right: 20px; |
4277 |
+ color: #004458; |
4278 |
+ overflow: hidden; |
4279 |
+ text-overflow: ellipsis; |
4280 |
+ white-space: nowrap; |
4281 |
+ background: url(sprites.png) right 20px no-repeat; |
4282 |
+} |
4283 |
+ |
4284 |
+#tasklists li span.handle { |
4285 |
+ display: none; |
4286 |
+} |
4287 |
+ |
4288 |
+#tasklists li.selected span.listname { |
4289 |
+ font-weight: bold; |
4290 |
+} |
4291 |
+ |
4292 |
+#tasklists li.readonly span.listname { |
4293 |
+ background-position: right -142px; |
4294 |
+} |
4295 |
+ |
4296 |
+#tasklists li.other span.listname { |
4297 |
+ background-position: right -160px; |
4298 |
+} |
4299 |
+ |
4300 |
+#tasklists li.other.readonly span.listname { |
4301 |
+ background-position: right -178px; |
4302 |
+} |
4303 |
+ |
4304 |
+#tasklists li.shared span.listname { |
4305 |
+ background-position: right -196px; |
4306 |
+} |
4307 |
+ |
4308 |
+#tasklists li.shared.readonly span.listname { |
4309 |
+ background-position: right -214px; |
4310 |
+} |
4311 |
+ |
4312 |
+#tasklists li input { |
4313 |
+ position: absolute; |
4314 |
+ top: 5px; |
4315 |
+ right: 5px; |
4316 |
+} |
4317 |
+ |
4318 |
+#mainview-right { |
4319 |
+ position: absolute; |
4320 |
+ top: 0; |
4321 |
+ left: 256px; |
4322 |
+ right: 0; |
4323 |
+ bottom: 0; |
4324 |
+} |
4325 |
+ |
4326 |
+#taskstoolbar { |
4327 |
+ position: absolute; |
4328 |
+ top: -6px; |
4329 |
+ left: 0; |
4330 |
+ width: 100%; |
4331 |
+ height: 40px; |
4332 |
+ white-space: nowrap; |
4333 |
+} |
4334 |
+ |
4335 |
+#taskstoolbar a.button.newtask { |
4336 |
+ background-image: url(buttons.png); |
4337 |
+ background-position: center -53px; |
4338 |
+} |
4339 |
+ |
4340 |
+#quickaddbox { |
4341 |
+ position: absolute; |
4342 |
+ top: 7px; |
4343 |
+ left: 0; |
4344 |
+ width: 60%; |
4345 |
+ height: 32px; |
4346 |
+ white-space: nowrap; |
4347 |
+} |
4348 |
+ |
4349 |
+#quickaddinput { |
4350 |
+ width: 85%; |
4351 |
+ margin: 0; |
4352 |
+ padding: 3px 8px; |
4353 |
+ height: 18px; |
4354 |
+ background: #f1f1f1; |
4355 |
+ background: rgba(255, 255, 255, 0.7); |
4356 |
+ border-color: #a3a3a3; |
4357 |
+ font-weight: bold; |
4358 |
+} |
4359 |
+ |
4360 |
+#quickaddbox .button { |
4361 |
+ margin-left: 5px; |
4362 |
+ padding: 3px 10px; |
4363 |
+ font-weight: bold; |
4364 |
+} |
4365 |
+ |
4366 |
+#tasksview { |
4367 |
+ position: absolute; |
4368 |
+ top: 42px; |
4369 |
+ left: 0; |
4370 |
+ right: 0; |
4371 |
+ bottom: 0; |
4372 |
+ padding-bottom: 28px; |
4373 |
+ background: rgba(255, 255, 255, 0.2); |
4374 |
+ overflow: visible; |
4375 |
+} |
4376 |
+ |
4377 |
+#message.statusbar { |
4378 |
+ border-top: 1px solid #c3c3c3; |
4379 |
+} |
4380 |
+ |
4381 |
+#tasksview .scroller { |
4382 |
+ position: absolute; |
4383 |
+ left: 0; |
4384 |
+ top: 35px; |
4385 |
+ width: 100%; |
4386 |
+ bottom: 28px; |
4387 |
+ overflow: auto; |
4388 |
+} |
4389 |
+ |
4390 |
+#tasksview .buttonbar { |
4391 |
+ color: #777; |
4392 |
+ background: #eee; |
4393 |
+ background: -moz-linear-gradient(top, #eee 0%, #dfdfdf 100%); |
4394 |
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eee), color-stop(100%,#dfdfdf)); |
4395 |
+ background: -o-linear-gradient(top, #eee 0%, #dfdfdf 100%); |
4396 |
+ background: -ms-linear-gradient(top, #eee 0%, #dfdfdf 100%); |
4397 |
+ background: linear-gradient(top, #eee 0%, #dfdfdf 100%); |
4398 |
+ border-bottom: 1px solid #ccc; |
4399 |
+} |
4400 |
+ |
4401 |
+#thelist { |
4402 |
+ padding: 0; |
4403 |
+ margin: 1em; |
4404 |
+ list-style: none; |
4405 |
+} |
4406 |
+ |
4407 |
+#listmessagebox { |
4408 |
+ display: none; |
4409 |
+ font-size: 14px; |
4410 |
+ color: #666; |
4411 |
+ margin: 1.5em; |
4412 |
+ text-shadow: 0px 1px 1px #fff; |
4413 |
+ text-align:center; |
4414 |
+} |
4415 |
+ |
4416 |
+.taskitem { |
4417 |
+ position: relative; |
4418 |
+ display: block; |
4419 |
+ margin-bottom: 3px; |
4420 |
+} |
4421 |
+ |
4422 |
+.taskitem.dragging { |
4423 |
+ opacity: 0.5; |
4424 |
+} |
4425 |
+ |
4426 |
+.taskitem .childtasks { |
4427 |
+ position: relative; |
4428 |
+ padding: 0; |
4429 |
+ margin: 3px 0 0 20px; |
4430 |
+ list-style: none; |
4431 |
+} |
4432 |
+ |
4433 |
+.taskitem .childtoggle { |
4434 |
+ display: none; |
4435 |
+ position: absolute; |
4436 |
+ top: 4px; |
4437 |
+ left: -5px; |
4438 |
+ padding: 2px; |
4439 |
+ font-size: 10px; |
4440 |
+ color: #727272; |
4441 |
+ cursor: pointer; |
4442 |
+ |
4443 |
+ width: 14px; |
4444 |
+ height: 14px; |
4445 |
+ background: url(sprites.png) -2px -80px no-repeat; |
4446 |
+ text-indent: -1000px; |
4447 |
+ overflow: hidden; |
4448 |
+} |
4449 |
+ |
4450 |
+.taskitem .childtoggle.collapsed { |
4451 |
+ background-position: -18px -81px; |
4452 |
+} |
4453 |
+ |
4454 |
+.taskhead { |
4455 |
+ position: relative; |
4456 |
+ margin-left: 14px; |
4457 |
+ padding: 4px 5px 3px 5px; |
4458 |
+ border: 1px solid #fff; |
4459 |
+ border-radius: 5px; |
4460 |
+ background: #fff; |
4461 |
+ -webkit-box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); |
4462 |
+ -moz-box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); |
4463 |
+ box-shadow: 0 1px 1px 0 rgba(50, 50, 50, 0.5); |
4464 |
+ padding-right: 26em; |
4465 |
+ white-space: nowrap; |
4466 |
+ overflow: hidden; |
4467 |
+ text-overflow: ellipsis; |
4468 |
+ cursor: default; |
4469 |
+} |
4470 |
+ |
4471 |
+.taskhead.droptarget { |
4472 |
+ border-color: #4787b1; |
4473 |
+ box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4474 |
+ -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4475 |
+ -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4476 |
+ -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4477 |
+} |
4478 |
+ |
4479 |
+.taskhead .complete { |
4480 |
+ margin: -1px 1em 0 0; |
4481 |
+} |
4482 |
+ |
4483 |
+.taskhead .title { |
4484 |
+ font-size: 12px; |
4485 |
+} |
4486 |
+ |
4487 |
+.taskhead .flagged { |
4488 |
+ display: inline-block; |
4489 |
+ visibility: hidden; |
4490 |
+ width: 16px; |
4491 |
+ height: 16px; |
4492 |
+ background: url(sprites.png) -2px -3px no-repeat; |
4493 |
+ margin: -3px 1em 0 0; |
4494 |
+ vertical-align: middle; |
4495 |
+ cursor: pointer; |
4496 |
+} |
4497 |
+ |
4498 |
+.taskhead:hover .flagged { |
4499 |
+ visibility: visible; |
4500 |
+} |
4501 |
+ |
4502 |
+.taskhead.flagged .flagged { |
4503 |
+ visibility: visible; |
4504 |
+ background-position: -2px -23px; |
4505 |
+} |
4506 |
+ |
4507 |
+.taskhead .tags { |
4508 |
+ display: block; |
4509 |
+ position: absolute; |
4510 |
+ top: 3px; |
4511 |
+ right: 10em; |
4512 |
+ max-width: 14em; |
4513 |
+ height: 16px; |
4514 |
+ overflow: hidden; |
4515 |
+ padding-top: 1px; |
4516 |
+ text-align: right; |
4517 |
+} |
4518 |
+ |
4519 |
+.taskhead .tags .tag { |
4520 |
+ font-size: 85%; |
4521 |
+ background: #d9ecf4; |
4522 |
+ border: 1px solid #c2dae5; |
4523 |
+ border-radius: 4px; |
4524 |
+ padding: 1px 7px; |
4525 |
+ margin-right: 3px; |
4526 |
+} |
4527 |
+ |
4528 |
+.taskhead .date { |
4529 |
+ position: absolute; |
4530 |
+ top: 4px; |
4531 |
+ right: 30px; |
4532 |
+ text-align: right; |
4533 |
+ cursor: pointer; |
4534 |
+} |
4535 |
+ |
4536 |
+.taskhead.nodate .date { |
4537 |
+ color: #ddd; |
4538 |
+} |
4539 |
+ |
4540 |
+.taskhead.overdue .date { |
4541 |
+ color: #d00; |
4542 |
+} |
4543 |
+ |
4544 |
+.taskhead.nodate:hover .date { |
4545 |
+ color: #999; |
4546 |
+} |
4547 |
+ |
4548 |
+.taskhead .date input { |
4549 |
+ padding: 1px 2px; |
4550 |
+ border: 1px solid #ddd; |
4551 |
+ -webkit-box-shadow: none; |
4552 |
+ -moz-box-shadow: none; |
4553 |
+ box-shadow: none; |
4554 |
+ outline: none; |
4555 |
+ text-align: right; |
4556 |
+ width: 6em; |
4557 |
+ font-size: 11px; |
4558 |
+} |
4559 |
+ |
4560 |
+.taskhead .actions, |
4561 |
+.taskhead .delete { |
4562 |
+ display: block; |
4563 |
+ visibility: hidden; |
4564 |
+ position: absolute; |
4565 |
+ top: 3px; |
4566 |
+ right: 6px; |
4567 |
+ width: 18px; |
4568 |
+ height: 18px; |
4569 |
+ background: url(sprites.png) 0 -80px no-repeat; |
4570 |
+ text-indent: -1000px; |
4571 |
+ overflow: hidden; |
4572 |
+ cursor: pointer; |
4573 |
+} |
4574 |
+ |
4575 |
+.taskhead .delete { |
4576 |
+ background-position: 0 -40px; |
4577 |
+} |
4578 |
+ |
4579 |
+.taskhead:hover .actions, |
4580 |
+.taskhead:hover .delete { |
4581 |
+ visibility: visible; |
4582 |
+} |
4583 |
+ |
4584 |
+.taskhead.complete { |
4585 |
+ opacity: 0.6; |
4586 |
+} |
4587 |
+ |
4588 |
+.taskhead.complete .title { |
4589 |
+ text-decoration: line-through; |
4590 |
+} |
4591 |
+ |
4592 |
+.taskhead .progressbar { |
4593 |
+ position: absolute; |
4594 |
+ bottom: 1px; |
4595 |
+ left: 6px; |
4596 |
+ right: 6px; |
4597 |
+ height: 2px; |
4598 |
+} |
4599 |
+ |
4600 |
+.taskhead.complete .progressbar { |
4601 |
+ display: none; |
4602 |
+} |
4603 |
+ |
4604 |
+.taskhead .progressvalue { |
4605 |
+ height: 1px; |
4606 |
+ background: rgba(1, 124, 180, 0.2); |
4607 |
+ border-top: 1px solid #219de6; |
4608 |
+} |
4609 |
+ |
4610 |
+ul.toolbarmenu li span.add { |
4611 |
+ background-image: url(sprites.png); |
4612 |
+ background-position: 0 -100px; |
4613 |
+} |
4614 |
+ |
4615 |
+ul.toolbarmenu li span.delete { |
4616 |
+ background-position: 0 -1508px; |
4617 |
+} |
4618 |
+ |
4619 |
+.taskitem-draghelper { |
4620 |
+/* |
4621 |
+ width: 32px; |
4622 |
+ height: 26px; |
4623 |
+*/ |
4624 |
+ background: #444; |
4625 |
+ border: 1px solid #555; |
4626 |
+ border-radius: 4px; |
4627 |
+ box-shadow: 0 2px 6px 0 #333; |
4628 |
+ -moz-box-shadow: 0 2px 6px 0 #333; |
4629 |
+ -webkit-box-shadow: 0 2px 6px 0 #333; |
4630 |
+ -o-box-shadow: 0 2px 6px 0 #333; |
4631 |
+ z-index: 5000; |
4632 |
+ padding: 2px 10px; |
4633 |
+ font-size: 20px; |
4634 |
+ color: #ccc; |
4635 |
+ opacity: 0.92; |
4636 |
+ filter: alpha(opacity=90); |
4637 |
+ text-shadow: 0px 1px 1px #333; |
4638 |
+} |
4639 |
+ |
4640 |
+#rootdroppable { |
4641 |
+ display: none; |
4642 |
+ position: absolute; |
4643 |
+ top: 36px; |
4644 |
+ left: 1em; |
4645 |
+ right: 1em; |
4646 |
+ height: 5px; |
4647 |
+ background: #ddd; |
4648 |
+ border-radius: 3px; |
4649 |
+} |
4650 |
+ |
4651 |
+#rootdroppable.droptarget { |
4652 |
+ background: #4787b1; |
4653 |
+ box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); |
4654 |
+ -moz-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); |
4655 |
+ -webkit-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); |
4656 |
+ -o-box-shadow: 0 0 2px 1px rgba(71,135,177, 0.9); |
4657 |
+ |
4658 |
+} |
4659 |
+ |
4660 |
+/*** task edit form ***/ |
4661 |
+ |
4662 |
+#taskedit, |
4663 |
+#taskshow { |
4664 |
+ display:none; |
4665 |
+} |
4666 |
+ |
4667 |
+#taskedit { |
4668 |
+ position: relative; |
4669 |
+ top: -1.5em; |
4670 |
+ padding: 0.5em 0.1em; |
4671 |
+ margin: 0 -0.2em; |
4672 |
+} |
4673 |
+ |
4674 |
+#taskshow h2 { |
4675 |
+ margin-top: -0.5em; |
4676 |
+} |
4677 |
+ |
4678 |
+#taskshow label { |
4679 |
+ color: #999; |
4680 |
+} |
4681 |
+ |
4682 |
+#task-parent-title { |
4683 |
+ position: relative; |
4684 |
+ top: -0.6em; |
4685 |
+} |
4686 |
+ |
4687 |
+a.morelink { |
4688 |
+ font-size: 90%; |
4689 |
+ color: #0069a6; |
4690 |
+ text-decoration: none; |
4691 |
+ outline: none; |
4692 |
+} |
4693 |
+ |
4694 |
+a.morelink:hover { |
4695 |
+ text-decoration: underline; |
4696 |
+} |
4697 |
+ |
4698 |
+#taskedit .ui-tabs-panel { |
4699 |
+ min-height: 24em; |
4700 |
+} |
4701 |
+ |
4702 |
+#taskeditform input.text, |
4703 |
+#taskeditform textarea { |
4704 |
+ width: 97%; |
4705 |
+} |
4706 |
+ |
4707 |
+#taskeditform .formbuttons { |
4708 |
+ margin: 0.5em 0; |
4709 |
+} |
4710 |
+ |
4711 |
+#taskedit-attachments { |
4712 |
+ margin: 0.6em 0; |
4713 |
+} |
4714 |
+ |
4715 |
+#taskedit-attachments ul li { |
4716 |
+ display: block; |
4717 |
+ color: #333; |
4718 |
+ font-weight: bold; |
4719 |
+ padding: 8px 4px 3px 30px; |
4720 |
+ text-shadow: 0px 1px 1px #fff; |
4721 |
+ text-decoration: none; |
4722 |
+ white-space: nowrap; |
4723 |
+} |
4724 |
+ |
4725 |
+#taskedit-attachments ul li a.file { |
4726 |
+ padding: 0; |
4727 |
+} |
4728 |
+ |
4729 |
+#taskedit-attachments-form { |
4730 |
+ margin-top: 1em; |
4731 |
+ padding-top: 0.8em; |
4732 |
+ border-top: 2px solid #fafafa; |
4733 |
+} |
4734 |
+ |
4735 |
+div.form-section { |
4736 |
+ position: relative; |
4737 |
+ margin-top: 0.2em; |
4738 |
+ margin-bottom: 0.8em; |
4739 |
+} |
4740 |
+ |
4741 |
+.form-section label { |
4742 |
+ display: inline-block; |
4743 |
+ min-width: 7em; |
4744 |
+ padding-right: 0.5em; |
4745 |
+ margin-bottom: 0.3em; |
4746 |
+} |
4747 |
+ |
4748 |
+label.block { |
4749 |
+ display: block; |
4750 |
+ margin-bottom: 0.3em; |
4751 |
+} |
4752 |
+ |
4753 |
+#taskedit-completeness-slider { |
4754 |
+ display: inline-block; |
4755 |
+ margin-left: 2em; |
4756 |
+ width: 30em; |
4757 |
+ height: 0.8em; |
4758 |
+ border: 1px solid #ccc; |
4759 |
+} |
4760 |
+ |
4761 |
+#taskedit-tagline { |
4762 |
+ width: 97%; |
4763 |
+} |
4764 |
+ |
4765 |
+#taskedit .droptarget { |
4766 |
+ background-image: url(../../../../skins/larry/images/filedrop.png) !important; |
4767 |
+ background-position: center bottom !important; |
4768 |
+ background-repeat: no-repeat !important; |
4769 |
+} |
4770 |
+ |
4771 |
+#taskedit .droptarget.hover, |
4772 |
+#taskedit .droptarget.active { |
4773 |
+ border-color: #019bc6; |
4774 |
+ box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); |
4775 |
+ -moz-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); |
4776 |
+ -webkit-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); |
4777 |
+ -o-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); |
4778 |
+} |
4779 |
+ |
4780 |
+#taskedit .droptarget.hover { |
4781 |
+ background-color: #d9ecf4; |
4782 |
+ box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4783 |
+ -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4784 |
+ -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4785 |
+ -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); |
4786 |
+} |
4787 |
+ |
4788 |
+#task-attachments .attachmentslist li { |
4789 |
+ float: left; |
4790 |
+ margin-right: 1em; |
4791 |
+} |
4792 |
+ |
4793 |
+#task-attachments .attachmentslist li a { |
4794 |
+ outline: none; |
4795 |
+} |
4796 |
+ |
4797 |
+ |
4798 |
+/** |
4799 |
+ * Styles of the tagedit inputsforms |
4800 |
+ */ |
4801 |
+.tagedit-list { |
4802 |
+ width: 100%; |
4803 |
+ margin: 0; |
4804 |
+ padding: 4px 4px 0 5px; |
4805 |
+ overflow: auto; |
4806 |
+ min-height: 26px; |
4807 |
+ background: #fff; |
4808 |
+ border: 1px solid #b2b2b2; |
4809 |
+ border-radius: 4px; |
4810 |
+ box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); |
4811 |
+ -moz-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); |
4812 |
+ -webkit-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); |
4813 |
+ -o-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); |
4814 |
+} |
4815 |
+.tagedit-list li.tagedit-listelement { |
4816 |
+ list-style-type: none; |
4817 |
+ float: left; |
4818 |
+ margin: 0 4px 4px 0; |
4819 |
+ padding: 0; |
4820 |
+} |
4821 |
+ |
4822 |
+/* New Item input */ |
4823 |
+.tagedit-list li.tagedit-listelement-new input { |
4824 |
+ border: 0; |
4825 |
+ height: 100%; |
4826 |
+ padding: 4px 1px; |
4827 |
+ width: 15px; |
4828 |
+ background: #fff; |
4829 |
+ border-radius: 0; |
4830 |
+ box-shadow: none; |
4831 |
+ -moz-box-shadow: none; |
4832 |
+ -webkit-box-shadow: none; |
4833 |
+ -o-box-shadow: none; |
4834 |
+} |
4835 |
+.tagedit-list li.tagedit-listelement-new input:focus { |
4836 |
+ box-shadow: none; |
4837 |
+ -moz-box-shadow: none; |
4838 |
+ -webkit-box-shadow: none; |
4839 |
+ -o-box-shadow: none; |
4840 |
+ outline: none; |
4841 |
+} |
4842 |
+.tagedit-list li.tagedit-listelement-new input.tagedit-input-disabled { |
4843 |
+ display: none; |
4844 |
+} |
4845 |
+ |
4846 |
+/* Item that is put to the List */ |
4847 |
+.form-section span.tag-element, |
4848 |
+.tagedit-list li.tagedit-listelement-old { |
4849 |
+ padding: 3px 0 1px 6px; |
4850 |
+ background: #ddeef5; |
4851 |
+ background: -moz-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); |
4852 |
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#edf6fa), color-stop(100%,#d6e9f3)); |
4853 |
+ background: -o-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); |
4854 |
+ background: -ms-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); |
4855 |
+ background: linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); |
4856 |
+ border: 1px solid #c2dae5; |
4857 |
+ -moz-border-radius: 4px; |
4858 |
+ -webkit-border-radius: 4px; |
4859 |
+ border-radius: 4px; |
4860 |
+ color: #0d5165; |
4861 |
+} |
4862 |
+ |
4863 |
+.form-section span.tag-element { |
4864 |
+ margin-right: 0.6em; |
4865 |
+ padding: 2px 6px; |
4866 |
+/* cursor: pointer; */ |
4867 |
+} |
4868 |
+ |
4869 |
+.tagedit-list li.tagedit-listelement-old a.tagedit-close, |
4870 |
+.tagedit-list li.tagedit-listelement-old a.tagedit-break, |
4871 |
+.tagedit-list li.tagedit-listelement-old a.tagedit-delete, |
4872 |
+.tagedit-list li.tagedit-listelement-old a.tagedit-save { |
4873 |
+ text-indent: -2000px; |
4874 |
+ display: inline-block; |
4875 |
+ position: relative; |
4876 |
+ top: -1px; |
4877 |
+ width: 16px; |
4878 |
+ height: 16px; |
4879 |
+ margin: 0 2px 0 6px; |
4880 |
+ background: url(sprites.png) -2px -122px no-repeat; |
4881 |
+ cursor: pointer; |
4882 |
+} |
4883 |
+ |
4884 |
+ |
4885 |
+/** Special hacks for IE7 **/ |
4886 |
+/** They need to be in this file to also affect the task-create dialog embedded in mail view **/ |
4887 |
+ |
4888 |
+html.ie7 #taskedit-completeness-slider { |
4889 |
+ display: inline; |
4890 |
+} |
4891 |
+ |
4892 |
+html.ie7 .form-section span.tag-element, |
4893 |
+html.ie7 .tagedit-list li.tagedit-listelement-old { |
4894 |
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#edf6fa', endColorstr='#d6e9f3', GradientType=0); |
4895 |
+} |
4896 |
+ |
4897 |
+html.ie7 .tagedit-list li.tagedit-listelement span { |
4898 |
+ position: relative; |
4899 |
+ top: -3px; |
4900 |
+} |
4901 |
+ |
4902 |
+html.ie7 .tagedit-list li.tagedit-listelement-old a.tagedit-close { |
4903 |
+ left: 5px; |
4904 |
+} |
4905 |
+ |
4906 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/attachment.html roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/attachment.html |
4907 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/attachment.html 1970-01-01 01:00:00.000000000 +0100 |
4908 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/attachment.html 2013-11-24 00:38:52.000000000 +0100 |
4909 |
@@ -0,0 +1,33 @@ |
4910 |
+<roundcube:object name="doctype" value="html5" /> |
4911 |
+<html> |
4912 |
+<head> |
4913 |
+<title><roundcube:object name="pagetitle" /></title> |
4914 |
+<roundcube:include file="/includes/links.html" /> |
4915 |
+</head> |
4916 |
+<body class="extwin attachmentwin"> |
4917 |
+ |
4918 |
+<div id="header"> |
4919 |
+ <div id="topnav"> |
4920 |
+ <roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" border="0" alt="Logo" /> |
4921 |
+ <div class="topright"> |
4922 |
+ <a href="#close" class="closelink" onclick="self.close()"><roundcube:label name="close" /></a> |
4923 |
+ </div> |
4924 |
+ </div> |
4925 |
+ |
4926 |
+ <br style="clear:both" /> |
4927 |
+</div> |
4928 |
+ |
4929 |
+<div id="mainscreen"> |
4930 |
+ <div id="partheader" class="uibox"> |
4931 |
+ <roundcube:object name="plugin.attachmentcontrols" class="headers-table" /> |
4932 |
+ </div> |
4933 |
+ |
4934 |
+ <div id="attachmentcontainer" class="uibox"> |
4935 |
+ <roundcube:object name="plugin.attachmentframe" id="attachmentframe" class="header-table" style="width:100%" /> |
4936 |
+ </div> |
4937 |
+ |
4938 |
+</div> |
4939 |
+ |
4940 |
+</body> |
4941 |
+</html> |
4942 |
+ |
4943 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/mainview.html roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/mainview.html |
4944 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/mainview.html 1970-01-01 01:00:00.000000000 +0100 |
4945 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/mainview.html 2013-11-24 00:38:52.000000000 +0100 |
4946 |
@@ -0,0 +1,153 @@ |
4947 |
+<roundcube:object name="doctype" value="html5" /> |
4948 |
+<html> |
4949 |
+<head> |
4950 |
+<title><roundcube:object name="pagetitle" /></title> |
4951 |
+<roundcube:include file="/includes/links.html" /> |
4952 |
+<!--[if lte IE 8]><link rel="stylesheet" type="text/css" href="/this/iehacks.css" /><![endif]--> |
4953 |
+</head> |
4954 |
+<body class="tasklistview noscroll"> |
4955 |
+ |
4956 |
+<roundcube:include file="/includes/header.html" /> |
4957 |
+ |
4958 |
+<div id="mainscreen"> |
4959 |
+ <div id="sidebar"> |
4960 |
+ <div id="taskstoolbar" class="toolbar"> |
4961 |
+ <roundcube:button command="newtask" type="link" class="button newtask disabled" classAct="button newtask" classSel="button newtask pressed" label="tasklist.newtask" title="tasklist.newtask" /> |
4962 |
+ <roundcube:container name="toolbar" id="taskstoolbar" /> |
4963 |
+ </div> |
4964 |
+ |
4965 |
+ <div id="tagsbox" class="uibox listbox"> |
4966 |
+ <h2 class="boxtitle"><roundcube:label name="tasklist.tags" id="taglist" /></h2> |
4967 |
+ <div class="scroller"> |
4968 |
+ <roundcube:object name="plugin.tagslist" id="tagslist" /> |
4969 |
+ </div> |
4970 |
+ </div> |
4971 |
+ |
4972 |
+ <div id="tasklistsbox" class="uibox listbox"> |
4973 |
+ <h2 class="boxtitle"><roundcube:label name="tasklist.lists" /></h2> |
4974 |
+ <div class="scroller withfooter"> |
4975 |
+ <roundcube:object name="plugin.tasklists" id="tasklists" class="listing" /> |
4976 |
+ </div> |
4977 |
+ <div class="boxfooter"> |
4978 |
+ <roundcube:button command="list-create" type="link" title="tasklist.createlist" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="tasklistoptionslink" id="tasklistoptionsmenulink" type="link" title="tasklist.listactions" class="listbutton groupactions" onclick="UI.show_popup('tasklistoptionsmenu', undefined, { above:true });return false" innerClass="inner" content="⚙" /> |
4979 |
+ </div> |
4980 |
+ </div> |
4981 |
+ </div> |
4982 |
+ |
4983 |
+ <div id="mainview-right"> |
4984 |
+ |
4985 |
+ <div id="quickaddbox"> |
4986 |
+ <roundcube:object name="plugin.quickaddform" /> |
4987 |
+ </div> |
4988 |
+ |
4989 |
+ <div id="quicksearchbar"> |
4990 |
+ <roundcube:object name="plugin.searchform" id="quicksearchbox" /> |
4991 |
+ <a id="searchmenulink" class="iconbutton searchoptions" > </a> |
4992 |
+ <roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " /> |
4993 |
+ </div> |
4994 |
+ |
4995 |
+ <div id="tasksview" class="uibox"> |
4996 |
+ <div class="boxtitle buttonbar"> |
4997 |
+ <ul id="taskselector"> |
4998 |
+ <li class="all selected"><a href="#all"><roundcube:label name="tasklist.all" /><span class="count"></span></a></li> |
4999 |
+ <li class="overdue inactive"><a href="#overdue"><roundcube:label name="tasklist.overdue" /><span class="count"></span></a></li> |
5000 |
+ <li class="flagged"><a href="#flagged"><roundcube:label name="tasklist.flagged" /><span class="count"></span></a></li> |
5001 |
+ <li class="today"><a href="#today"><roundcube:label name="tasklist.today" /><span class="count"></span></a></li> |
5002 |
+ <li class="tomorrow"><a href="#tomorrow"><roundcube:label name="tasklist.tomorrow" /><span class="count"></span></a></li> |
5003 |
+ <li class="week"><a href="#week"><roundcube:label name="tasklist.next7days" /></a></li> |
5004 |
+ <li class="later"><a href="#later"><roundcube:label name="tasklist.later" /></a></li> |
5005 |
+ <li class="nodate"><a href="#nodate"><roundcube:label name="tasklist.nodate" ucfirst="true" /></a></li> |
5006 |
+ <li class="complete"><a href="#complete"><roundcube:label name="tasklist.complete" /><span class="count"></span></a></li> |
5007 |
+ </ul> |
5008 |
+ </div> |
5009 |
+ |
5010 |
+ <div class="scroller"> |
5011 |
+ <roundcube:object name="plugin.tasks" id="thelist" /> |
5012 |
+ <div id="listmessagebox"></div> |
5013 |
+ </div> |
5014 |
+ <div id="rootdroppable"></div> |
5015 |
+ <roundcube:object name="message" id="message" class="statusbar" /> |
5016 |
+ </div> |
5017 |
+ |
5018 |
+ </div> |
5019 |
+ |
5020 |
+</div> |
5021 |
+ |
5022 |
+<div id="taskitemmenu" class="popupmenu"> |
5023 |
+ <ul class="toolbarmenu iconized"> |
5024 |
+ <li><roundcube:button name="edit" type="link" onclick="rctasks.edit_task(rctasks.selected_task.id, 'edit'); return false" label="edit" class="icon active" innerclass="icon edit" /></li> |
5025 |
+ <li><roundcube:button name="delete" type="link" onclick="rctasks.delete_task(rctasks.selected_task.id); return false" label="delete" class="icon active" innerclass="icon delete" /></li> |
5026 |
+ <li><roundcube:button name="addchild" type="link" onclick="rctasks.add_childtask(rctasks.selected_task.id); return false" label="tasklist.addsubtask" class="icon active" innerclass="icon add" /></li> |
5027 |
+ </ul> |
5028 |
+</div> |
5029 |
+ |
5030 |
+<div id="tasklistoptionsmenu" class="popupmenu"> |
5031 |
+ <ul class="toolbarmenu"> |
5032 |
+ <li><roundcube:button command="list-edit" label="edit" classAct="active" /></li> |
5033 |
+ <li><roundcube:button command="list-remove" label="delete" classAct="active" /></li> |
5034 |
+ <!--<li><roundcube:button command="list-import" label="tasklist.import" classAct="active" /></li>--> |
5035 |
+ <roundcube:if condition="env:tasklist_driver == 'kolab'" /> |
5036 |
+ <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li> |
5037 |
+ <roundcube:endif /> |
5038 |
+ </ul> |
5039 |
+</div> |
5040 |
+ |
5041 |
+<div id="taskshow"> |
5042 |
+ <div class="form-section" id="task-parent-title"></div> |
5043 |
+ <div class="form-section"> |
5044 |
+ <h2 id="task-title"></h2> |
5045 |
+ </div> |
5046 |
+ <div id="task-description" class="form-section"> |
5047 |
+ </div> |
5048 |
+ <div id="task-tags" class="form-section"> |
5049 |
+ <label><roundcube:label name="tasklist.tags" /></label> |
5050 |
+ <span class="task-text"></span> |
5051 |
+ </div> |
5052 |
+ <div id="task-start" class="form-section"> |
5053 |
+ <label><roundcube:label name="tasklist.start" /></label> |
5054 |
+ <span class="task-text"></span> |
5055 |
+ <span id="task-starttime"></span> |
5056 |
+ </div> |
5057 |
+ <div id="task-date" class="form-section"> |
5058 |
+ <label><roundcube:label name="tasklist.datetime" /></label> |
5059 |
+ <span class="task-text"></span> |
5060 |
+ <span id="task-time"></span> |
5061 |
+ </div> |
5062 |
+ <div id="task-alarm" class="form-section"> |
5063 |
+ <label><roundcube:label name="tasklist.alarms" /></label> |
5064 |
+ <span class="task-text"></span> |
5065 |
+ </div> |
5066 |
+ <div id="task-list" class="form-section"> |
5067 |
+ <label><roundcube:label name="tasklist.list" /></label> |
5068 |
+ <span class="task-text"></span> |
5069 |
+ </div> |
5070 |
+ <div id="task-completeness" class="form-section"> |
5071 |
+ <label><roundcube:label name="tasklist.complete" /></label> |
5072 |
+ <span class="task-text"></span> |
5073 |
+ </div> |
5074 |
+ <div id="task-attachments" class="form-section"> |
5075 |
+ <label><roundcube:label name="attachments" /></label> |
5076 |
+ <div class="task-text"></div> |
5077 |
+ </div> |
5078 |
+</div> |
5079 |
+ |
5080 |
+<roundcube:include file="/templates/taskedit.html" /> |
5081 |
+ |
5082 |
+<div id="tasklistform" class="uidialog"> |
5083 |
+ <roundcube:object name="plugin.tasklist_editform" /> |
5084 |
+</div> |
5085 |
+ |
5086 |
+<script type="text/javascript"> |
5087 |
+ |
5088 |
+// UI startup |
5089 |
+var UI = new rcube_mail_ui(); |
5090 |
+ |
5091 |
+$(document).ready(function(e){ |
5092 |
+ UI.init(); |
5093 |
+ |
5094 |
+}); |
5095 |
+ |
5096 |
+</script> |
5097 |
+ |
5098 |
+</body> |
5099 |
+</html> |
5100 |
\ Pas de fin de ligne à la fin du fichier. |
5101 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/taskedit.html roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/taskedit.html |
5102 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/skins/larry/templates/taskedit.html 1970-01-01 01:00:00.000000000 +0100 |
5103 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/skins/larry/templates/taskedit.html 2013-11-24 00:38:52.000000000 +0100 |
5104 |
@@ -0,0 +1,59 @@ |
5105 |
+<div id="taskedit" class="uidialog uidialog-tabbed"> |
5106 |
+ <form id="taskeditform" action="#" method="post" enctype="multipart/form-data"> |
5107 |
+ <ul> |
5108 |
+ <li><a href="#taskedit-tab-1"><roundcube:label name="tasklist.tabsummary" /></a></li><li id="taskedit-tab-attachments"><a href="#taskedit-tab-2"><roundcube:label name="tasklist.tabattachments" /></a></li> |
5109 |
+ </ul> |
5110 |
+ <!-- basic info --> |
5111 |
+ <div id="taskedit-tab-1"> |
5112 |
+ <div class="form-section"> |
5113 |
+ <label for="taskedit-title"><roundcube:label name="tasklist.title" /></label> |
5114 |
+ <br /> |
5115 |
+ <input type="text" class="text" name="title" id="taskedit-title" size="60" tabindex="1" /> |
5116 |
+ </div> |
5117 |
+ <div class="form-section"> |
5118 |
+ <label for="taskedit-description"><roundcube:label name="tasklist.description" /></label> |
5119 |
+ <br /> |
5120 |
+ <textarea name="description" id="taskedit-description" class="text" rows="5" cols="60" tabindex="2"></textarea> |
5121 |
+ </div> |
5122 |
+ <div class="form-section"> |
5123 |
+ <label for="taskedit-tags"><roundcube:label name="tasklist.tags" /></label> |
5124 |
+ <roundcube:object name="plugin.tags_editline" id="taskedit-tagline" class="tagedit" tabindex="3" /> |
5125 |
+ </div> |
5126 |
+ <div class="form-section"> |
5127 |
+ <label for="taskedit-startdate"><roundcube:label name="tasklist.start" /></label> |
5128 |
+ <input type="text" name="startdate" size="10" id="taskedit-startdate" tabindex="23" /> |
5129 |
+ <input type="text" name="starttime" size="6" id="taskedit-starttime" tabindex="24" /> |
5130 |
+ <a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#taskedit-startdate,#taskedit-starttime"><roundcube:label name="tasklist.nodate" /></a> |
5131 |
+ </div> |
5132 |
+ <div class="form-section"> |
5133 |
+ <label for="taskedit-date"><roundcube:label name="tasklist.datetime" /></label> |
5134 |
+ <input type="text" name="date" size="10" id="taskedit-date" tabindex="20" /> |
5135 |
+ <input type="text" name="time" size="6" id="taskedit-time" tabindex="21" /> |
5136 |
+ <a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#taskedit-date,#taskedit-time"><roundcube:label name="tasklist.nodate" /></a> |
5137 |
+ </div> |
5138 |
+ <div class="form-section" id="taskedit-alarms"> |
5139 |
+ <label for="taskedit-alarm"><roundcube:label name="tasklist.alarms" /></label> |
5140 |
+ <roundcube:object name="plugin.alarm_select" /> |
5141 |
+ </div> |
5142 |
+ <div class="form-section"> |
5143 |
+ <label for="taskedit-completeness"><roundcube:label name="tasklist.complete" /></label> |
5144 |
+ <input type="text" name="title" id="taskedit-completeness" size="3" tabindex="25" /> % |
5145 |
+ <div id="taskedit-completeness-slider"></div> |
5146 |
+ </div> |
5147 |
+ <div class="form-section" id="tasklist-select"> |
5148 |
+ <label for="taskedit-tasklist"><roundcube:label name="tasklist.list" /></label> |
5149 |
+ <roundcube:object name="plugin.tasklist_select" id="taskedit-tasklist" tabindex="26" /> |
5150 |
+ </div> |
5151 |
+ </div> |
5152 |
+ <!-- attachments list (with upload form) --> |
5153 |
+ <div id="taskedit-tab-2"> |
5154 |
+ <div id="taskedit-attachments"> |
5155 |
+ <roundcube:object name="plugin.attachments_list" id="taskedit-attachment-list" class="attachmentslist" /> |
5156 |
+ </div> |
5157 |
+ <div id="taskedit-attachments-form"> |
5158 |
+ <roundcube:object name="plugin.attachments_form" id="taskedit-attachment-form" attachmentFieldSize="30" /> |
5159 |
+ </div> |
5160 |
+ <roundcube:object name="plugin.filedroparea" id="taskedit-tab-2" /> |
5161 |
+ </div> |
5162 |
+ </form> |
5163 |
+</div> |
5164 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist_base.js roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist_base.js |
5165 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist_base.js 1970-01-01 01:00:00.000000000 +0100 |
5166 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist_base.js 2013-11-24 00:38:51.000000000 +0100 |
5167 |
@@ -0,0 +1,94 @@ |
5168 |
+/** |
5169 |
+ * Client scripts for the Tasklist plugin |
5170 |
+ * |
5171 |
+ * @version @package_version@ |
5172 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
5173 |
+ * |
5174 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
5175 |
+ * |
5176 |
+ * This program is free software: you can redistribute it and/or modify |
5177 |
+ * it under the terms of the GNU Affero General Public License as |
5178 |
+ * published by the Free Software Foundation, either version 3 of the |
5179 |
+ * License, or (at your option) any later version. |
5180 |
+ * |
5181 |
+ * This program is distributed in the hope that it will be useful, |
5182 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
5183 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5184 |
+ * GNU Affero General Public License for more details. |
5185 |
+ * |
5186 |
+ * You should have received a copy of the GNU Affero General Public License |
5187 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
5188 |
+ */ |
5189 |
+ |
5190 |
+function rcube_tasklist(settings) |
5191 |
+{ |
5192 |
+ /* private vars */ |
5193 |
+ var ui_loaded = false; |
5194 |
+ var me = this; |
5195 |
+ |
5196 |
+ /* public members */ |
5197 |
+ this.ui; |
5198 |
+ |
5199 |
+ /* public methods */ |
5200 |
+ this.create_from_mail = create_from_mail; |
5201 |
+ this.mail2taskdialog = mail2task_dialog; |
5202 |
+ |
5203 |
+ |
5204 |
+ /** |
5205 |
+ * Open a new task dialog prefilled with contents from the currently selected mail message |
5206 |
+ */ |
5207 |
+ function create_from_mail() |
5208 |
+ { |
5209 |
+ var uid; |
5210 |
+ if ((uid = rcmail.get_single_uid())) { |
5211 |
+ // load calendar UI (scripts and edit dialog template) |
5212 |
+ if (!ui_loaded) { |
5213 |
+ $.when( |
5214 |
+ $.getScript('./plugins/tasklist/tasklist.js'), |
5215 |
+ $.getScript('./plugins/tasklist/jquery.tagedit.js'), |
5216 |
+ $.get(rcmail.url('tasks/inlineui'), function(html){ $(document.body).append(html); }, 'html') |
5217 |
+ ).then(function() { |
5218 |
+ // register attachments form |
5219 |
+ // rcmail.gui_object('attachmentlist', 'attachmentlist'); |
5220 |
+ |
5221 |
+ ui_loaded = true; |
5222 |
+ me.ui = new rcube_tasklist_ui(settings); |
5223 |
+ create_from_mail(); // start over |
5224 |
+ }); |
5225 |
+ return; |
5226 |
+ } |
5227 |
+ else { |
5228 |
+ // get message contents for task dialog |
5229 |
+ var lock = rcmail.set_busy(true, 'loading'); |
5230 |
+ rcmail.http_post('tasks/mail2task', { |
5231 |
+ '_mbox': rcmail.env.mailbox, |
5232 |
+ '_uid': uid |
5233 |
+ }, lock); |
5234 |
+ } |
5235 |
+ } |
5236 |
+ } |
5237 |
+ |
5238 |
+ /** |
5239 |
+ * Callback function to put the given task properties into the dialog |
5240 |
+ */ |
5241 |
+ function mail2task_dialog(prop) |
5242 |
+ { |
5243 |
+ this.ui.edit_task(null, 'new', prop); |
5244 |
+ } |
5245 |
+ |
5246 |
+} |
5247 |
+ |
5248 |
+/* tasklist plugin initialization (for email task) */ |
5249 |
+window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', function(evt) { |
5250 |
+ var tasks = new rcube_tasklist(rcmail.env.libcal_settings); |
5251 |
+ |
5252 |
+ rcmail.register_command('tasklist-create-from-mail', function() { tasks.create_from_mail() }); |
5253 |
+ rcmail.addEventListener('plugin.mail2taskdialog', function(p){ tasks.mail2taskdialog(p) }); |
5254 |
+ rcmail.addEventListener('plugin.unlock_saving', function(p){ tasks.ui && tasks.ui.unlock_saving(); }); |
5255 |
+ |
5256 |
+ if (rcmail.env.action != 'show') |
5257 |
+ rcmail.env.message_commands.push('tasklist-create-from-mail'); |
5258 |
+ else |
5259 |
+ rcmail.enable_command('tasklist-create-from-mail', true); |
5260 |
+}); |
5261 |
+ |
5262 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist.js roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist.js |
5263 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist.js 1970-01-01 01:00:00.000000000 +0100 |
5264 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist.js 2013-11-24 00:38:51.000000000 +0100 |
5265 |
@@ -0,0 +1,1669 @@ |
5266 |
+/** |
5267 |
+ * Client scripts for the Tasklist plugin |
5268 |
+ * |
5269 |
+ * @version @package_version@ |
5270 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
5271 |
+ * |
5272 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
5273 |
+ * |
5274 |
+ * This program is free software: you can redistribute it and/or modify |
5275 |
+ * it under the terms of the GNU Affero General Public License as |
5276 |
+ * published by the Free Software Foundation, either version 3 of the |
5277 |
+ * License, or (at your option) any later version. |
5278 |
+ * |
5279 |
+ * This program is distributed in the hope that it will be useful, |
5280 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
5281 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5282 |
+ * GNU Affero General Public License for more details. |
5283 |
+ * |
5284 |
+ * You should have received a copy of the GNU Affero General Public License |
5285 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
5286 |
+ */ |
5287 |
+ |
5288 |
+function rcube_tasklist_ui(settings) |
5289 |
+{ |
5290 |
+ // extend base class |
5291 |
+ rcube_libcalendaring.call(this, settings); |
5292 |
+ |
5293 |
+ /* constants */ |
5294 |
+ var FILTER_MASK_ALL = 0; |
5295 |
+ var FILTER_MASK_TODAY = 1; |
5296 |
+ var FILTER_MASK_TOMORROW = 2; |
5297 |
+ var FILTER_MASK_WEEK = 4; |
5298 |
+ var FILTER_MASK_LATER = 8; |
5299 |
+ var FILTER_MASK_NODATE = 16; |
5300 |
+ var FILTER_MASK_OVERDUE = 32; |
5301 |
+ var FILTER_MASK_FLAGGED = 64; |
5302 |
+ var FILTER_MASK_COMPLETE = 128; |
5303 |
+ |
5304 |
+ var filter_masks = { |
5305 |
+ all: FILTER_MASK_ALL, |
5306 |
+ today: FILTER_MASK_TODAY, |
5307 |
+ tomorrow: FILTER_MASK_TOMORROW, |
5308 |
+ week: FILTER_MASK_WEEK, |
5309 |
+ later: FILTER_MASK_LATER, |
5310 |
+ nodate: FILTER_MASK_NODATE, |
5311 |
+ overdue: FILTER_MASK_OVERDUE, |
5312 |
+ flagged: FILTER_MASK_FLAGGED, |
5313 |
+ complete: FILTER_MASK_COMPLETE |
5314 |
+ }; |
5315 |
+ |
5316 |
+ /* private vars */ |
5317 |
+ var selector = 'all'; |
5318 |
+ var tagsfilter = []; |
5319 |
+ var filtermask = FILTER_MASK_ALL; |
5320 |
+ var loadstate = { filter:-1, lists:'', search:null }; |
5321 |
+ var idcount = 0; |
5322 |
+ var saving_lock; |
5323 |
+ var ui_loading; |
5324 |
+ var taskcounts = {}; |
5325 |
+ var listindex = []; |
5326 |
+ var listdata = {}; |
5327 |
+ var tags = []; |
5328 |
+ var draghelper; |
5329 |
+ var search_request; |
5330 |
+ var search_query; |
5331 |
+ var completeness_slider; |
5332 |
+ var me = this; |
5333 |
+ |
5334 |
+ // general datepicker settings |
5335 |
+ var datepicker_settings = { |
5336 |
+ // translate from PHP format to datepicker format |
5337 |
+ dateFormat: settings['date_format'].replace(/M/g, 'm').replace(/mmmmm/, 'MM').replace(/mmm/, 'M').replace(/dddd/, 'DD').replace(/ddd/, 'D').replace(/yy/g, 'y'), |
5338 |
+ firstDay : settings['first_day'], |
5339 |
+// dayNamesMin: settings['days_short'], |
5340 |
+// monthNames: settings['months'], |
5341 |
+// monthNamesShort: settings['months'], |
5342 |
+ changeMonth: false, |
5343 |
+ showOtherMonths: true, |
5344 |
+ selectOtherMonths: true |
5345 |
+ }; |
5346 |
+ var extended_datepicker_settings; |
5347 |
+ |
5348 |
+ /* public members */ |
5349 |
+ this.tasklists = rcmail.env.tasklists; |
5350 |
+ this.selected_task; |
5351 |
+ this.selected_list; |
5352 |
+ |
5353 |
+ /* public methods */ |
5354 |
+ this.init = init; |
5355 |
+ this.edit_task = task_edit_dialog; |
5356 |
+ this.delete_task = delete_task; |
5357 |
+ this.add_childtask = add_childtask; |
5358 |
+ this.quicksearch = quicksearch; |
5359 |
+ this.reset_search = reset_search; |
5360 |
+ this.list_remove = list_remove; |
5361 |
+ this.list_edit_dialog = list_edit_dialog; |
5362 |
+ this.unlock_saving = unlock_saving; |
5363 |
+ |
5364 |
+ /* imports */ |
5365 |
+ var Q = this.quote_html; |
5366 |
+ var text2html = this.text2html; |
5367 |
+ var event_date_text = this.event_date_text; |
5368 |
+ var parse_datetime = this.parse_datetime; |
5369 |
+ var date2unixtime = this.date2unixtime; |
5370 |
+ var fromunixtime = this.fromunixtime; |
5371 |
+ var init_alarms_edit = this.init_alarms_edit; |
5372 |
+ |
5373 |
+ /** |
5374 |
+ * initialize the tasks UI |
5375 |
+ */ |
5376 |
+ function init() |
5377 |
+ { |
5378 |
+ // initialize task list selectors |
5379 |
+ for (var id in me.tasklists) { |
5380 |
+ if ((li = rcmail.get_folder_li(id, 'rcmlitasklist'))) { |
5381 |
+ init_tasklist_li(li, id); |
5382 |
+ } |
5383 |
+ |
5384 |
+ if (me.tasklists[id].editable && !me.selected_list) { |
5385 |
+ me.selected_list = id; |
5386 |
+ rcmail.enable_command('addtask', true); |
5387 |
+ $(li).click(); |
5388 |
+ } |
5389 |
+ } |
5390 |
+ |
5391 |
+ // register server callbacks |
5392 |
+ rcmail.addEventListener('plugin.data_ready', data_ready); |
5393 |
+ rcmail.addEventListener('plugin.refresh_task', update_taskitem); |
5394 |
+ rcmail.addEventListener('plugin.update_counts', update_counts); |
5395 |
+ rcmail.addEventListener('plugin.insert_tasklist', insert_list); |
5396 |
+ rcmail.addEventListener('plugin.update_tasklist', update_list); |
5397 |
+ rcmail.addEventListener('plugin.destroy_tasklist', destroy_list); |
5398 |
+ rcmail.addEventListener('plugin.reload_data', function(){ list_tasks(null); }); |
5399 |
+ rcmail.addEventListener('plugin.unlock_saving', unlock_saving); |
5400 |
+ |
5401 |
+ // start loading tasks |
5402 |
+ fetch_counts(); |
5403 |
+ list_tasks(); |
5404 |
+ |
5405 |
+ // register event handlers for UI elements |
5406 |
+ $('#taskselector a').click(function(e){ |
5407 |
+ if (!$(this).parent().hasClass('inactive')) |
5408 |
+ list_tasks(this.href.replace(/^.*#/, '')); |
5409 |
+ return false; |
5410 |
+ }); |
5411 |
+ |
5412 |
+ // quick-add a task |
5413 |
+ $(rcmail.gui_objects.quickaddform).submit(function(e){ |
5414 |
+ var tasktext = this.elements.text.value, |
5415 |
+ rec = { id:-(++idcount), title:tasktext, readonly:true, mask:0, complete:0 }; |
5416 |
+ |
5417 |
+ if (tasktext && tasktext.length) { |
5418 |
+ save_task({ tempid:rec.id, raw:tasktext, list:me.selected_list }, 'new'); |
5419 |
+ render_task(rec); |
5420 |
+ |
5421 |
+ $('#listmessagebox').hide(); |
5422 |
+ } |
5423 |
+ |
5424 |
+ // clear form |
5425 |
+ this.reset(); |
5426 |
+ return false; |
5427 |
+ }).find('input[type=text]').placeholder(rcmail.gettext('createnewtask','tasklist')); |
5428 |
+ |
5429 |
+ // click-handler on tags list |
5430 |
+ $(rcmail.gui_objects.tagslist).click(function(e){ |
5431 |
+ if (e.target.nodeName != 'LI') |
5432 |
+ return false; |
5433 |
+ |
5434 |
+ var item = $(e.target), |
5435 |
+ tag = item.data('value'); |
5436 |
+ |
5437 |
+ // reset selection on regular clicks |
5438 |
+ var index = $.inArray(tag, tagsfilter); |
5439 |
+ var shift = e.shiftKey || e.ctrlKey || e.metaKey; |
5440 |
+ |
5441 |
+ if (!shift) { |
5442 |
+ if (tagsfilter.length > 1) |
5443 |
+ index = -1; |
5444 |
+ |
5445 |
+ $('li', this).removeClass('selected'); |
5446 |
+ tagsfilter = []; |
5447 |
+ } |
5448 |
+ |
5449 |
+ // add tag to filter |
5450 |
+ if (index < 0) { |
5451 |
+ item.addClass('selected'); |
5452 |
+ tagsfilter.push(tag); |
5453 |
+ } |
5454 |
+ else if (shift) { |
5455 |
+ item.removeClass('selected'); |
5456 |
+ var a = tagsfilter.slice(0,index); |
5457 |
+ tagsfilter = a.concat(tagsfilter.slice(index+1)); |
5458 |
+ } |
5459 |
+ |
5460 |
+ list_tasks(); |
5461 |
+ |
5462 |
+ // clear text selection in IE after shift+click |
5463 |
+ if (shift && document.selection) |
5464 |
+ document.selection.empty(); |
5465 |
+ |
5466 |
+ e.preventDefault(); |
5467 |
+ return false; |
5468 |
+ }) |
5469 |
+ .mousedown(function(e){ |
5470 |
+ // disable content selection with the mouse |
5471 |
+ e.preventDefault(); |
5472 |
+ return false; |
5473 |
+ }); |
5474 |
+ |
5475 |
+ // click-handler on task list items (delegate) |
5476 |
+ $(rcmail.gui_objects.resultlist).click(function(e){ |
5477 |
+ var item = $(e.target); |
5478 |
+ var className = e.target.className; |
5479 |
+ |
5480 |
+ if (item.hasClass('childtoggle')) { |
5481 |
+ item = item.parent().find('.taskhead'); |
5482 |
+ className = 'childtoggle'; |
5483 |
+ } |
5484 |
+ else if (!item.hasClass('taskhead')) |
5485 |
+ item = item.closest('div.taskhead'); |
5486 |
+ |
5487 |
+ // ignore |
5488 |
+ if (!item.length) |
5489 |
+ return false; |
5490 |
+ |
5491 |
+ var id = item.data('id'), |
5492 |
+ li = item.parent(), |
5493 |
+ rec = listdata[id]; |
5494 |
+ |
5495 |
+ switch (className) { |
5496 |
+ case 'childtoggle': |
5497 |
+ rec.collapsed = !rec.collapsed; |
5498 |
+ li.children('.childtasks:first').toggle(); |
5499 |
+ $(e.target).toggleClass('collapsed').html(rec.collapsed ? '▶' : '▼'); |
5500 |
+ rcmail.http_post('tasks/task', { action:'collapse', t:{ id:rec.id, list:rec.list }, collapsed:rec.collapsed?1:0 }); |
5501 |
+ break; |
5502 |
+ |
5503 |
+ case 'complete': |
5504 |
+ if (rcmail.busy) |
5505 |
+ return false; |
5506 |
+ |
5507 |
+ rec.complete = e.target.checked ? 1 : 0; |
5508 |
+ li.toggleClass('complete'); |
5509 |
+ save_task(rec, 'edit'); |
5510 |
+ return true; |
5511 |
+ |
5512 |
+ case 'flagged': |
5513 |
+ if (rcmail.busy) |
5514 |
+ return false; |
5515 |
+ |
5516 |
+ rec.flagged = rec.flagged ? 0 : 1; |
5517 |
+ li.toggleClass('flagged'); |
5518 |
+ save_task(rec, 'edit'); |
5519 |
+ break; |
5520 |
+ |
5521 |
+ case 'date': |
5522 |
+ if (rcmail.busy) |
5523 |
+ return false; |
5524 |
+ |
5525 |
+ var link = $(e.target).html(''), |
5526 |
+ input = $('<input type="text" size="10" />').appendTo(link).val(rec.date || '') |
5527 |
+ |
5528 |
+ input.datepicker($.extend({ |
5529 |
+ onClose: function(dateText, inst) { |
5530 |
+ if (dateText != (rec.date || '')) { |
5531 |
+ rec.date = dateText; |
5532 |
+ save_task(rec, 'edit'); |
5533 |
+ } |
5534 |
+ input.datepicker('destroy').remove(); |
5535 |
+ link.html(dateText || rcmail.gettext('nodate','tasklist')); |
5536 |
+ } |
5537 |
+ }, extended_datepicker_settings) |
5538 |
+ ) |
5539 |
+ .datepicker('setDate', rec.date) |
5540 |
+ .datepicker('show'); |
5541 |
+ break; |
5542 |
+ |
5543 |
+ case 'delete': |
5544 |
+ delete_task(id); |
5545 |
+ break; |
5546 |
+ |
5547 |
+ case 'actions': |
5548 |
+ var pos, ref = $(e.target), |
5549 |
+ menu = $('#taskitemmenu'); |
5550 |
+ if (menu.is(':visible') && menu.data('refid') == id) { |
5551 |
+ menu.hide(); |
5552 |
+ } |
5553 |
+ else { |
5554 |
+ pos = ref.offset(); |
5555 |
+ pos.top += ref.outerHeight(); |
5556 |
+ pos.left += ref.width() - menu.outerWidth(); |
5557 |
+ menu.css({ top:pos.top+'px', left:pos.left+'px' }).show(); |
5558 |
+ menu.data('refid', id); |
5559 |
+ me.selected_task = rec; |
5560 |
+ } |
5561 |
+ e.bubble = false; |
5562 |
+ break; |
5563 |
+ |
5564 |
+ default: |
5565 |
+ if (e.target.nodeName != 'INPUT') |
5566 |
+ task_show_dialog(id); |
5567 |
+ break; |
5568 |
+ } |
5569 |
+ |
5570 |
+ return false; |
5571 |
+ }) |
5572 |
+ .dblclick(function(e){ |
5573 |
+ var id, rec, item = $(e.target); |
5574 |
+ if (!item.hasClass('taskhead')) |
5575 |
+ item = item.closest('div.taskhead'); |
5576 |
+ |
5577 |
+ if (!rcmail.busy && item.length && (id = item.data('id')) && (rec = listdata[id])) { |
5578 |
+ var list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] : {}; |
5579 |
+ if (rec.readonly || !list.editable) |
5580 |
+ task_show_dialog(id); |
5581 |
+ else |
5582 |
+ task_edit_dialog(id, 'edit'); |
5583 |
+ clearSelection(); |
5584 |
+ } |
5585 |
+ }); |
5586 |
+ |
5587 |
+ // handle global document clicks: close popup menus |
5588 |
+ $(document.body).click(clear_popups); |
5589 |
+ |
5590 |
+ // extended datepicker settings |
5591 |
+ var extended_datepicker_settings = $.extend({ |
5592 |
+ showButtonPanel: true, |
5593 |
+ beforeShow: function(input, inst) { |
5594 |
+ setTimeout(function(){ |
5595 |
+ $(input).datepicker('widget').find('button.ui-datepicker-close') |
5596 |
+ .html(rcmail.gettext('nodate','tasklist')) |
5597 |
+ .attr('onclick', '') |
5598 |
+ .click(function(e){ |
5599 |
+ $(input).datepicker('setDate', null).datepicker('hide'); |
5600 |
+ }); |
5601 |
+ }, 1); |
5602 |
+ } |
5603 |
+ }, datepicker_settings); |
5604 |
+ } |
5605 |
+ |
5606 |
+ /** |
5607 |
+ * initialize task edit form elements |
5608 |
+ */ |
5609 |
+ function init_taskedit() |
5610 |
+ { |
5611 |
+ $('#taskedit').tabs(); |
5612 |
+ |
5613 |
+ var completeness_slider_change = function(e, ui){ |
5614 |
+ var v = completeness_slider.slider('value'); |
5615 |
+ if (v >= 98) v = 100; |
5616 |
+ if (v <= 2) v = 0; |
5617 |
+ $('#taskedit-completeness').val(v); |
5618 |
+ }; |
5619 |
+ completeness_slider = $('#taskedit-completeness-slider').slider({ |
5620 |
+ range: 'min', |
5621 |
+ animate: 'fast', |
5622 |
+ slide: completeness_slider_change, |
5623 |
+ change: completeness_slider_change |
5624 |
+ }); |
5625 |
+ $('#taskedit-completeness').change(function(e){ |
5626 |
+ completeness_slider.slider('value', parseInt(this.value)) |
5627 |
+ }); |
5628 |
+ |
5629 |
+ // register events on alarm fields |
5630 |
+ init_alarms_edit('#taskedit'); |
5631 |
+ |
5632 |
+ $('#taskedit-date, #taskedit-startdate').datepicker(datepicker_settings); |
5633 |
+ |
5634 |
+ $('a.edit-nodate').click(function(){ |
5635 |
+ var sel = $(this).attr('rel'); |
5636 |
+ if (sel) $(sel).val(''); |
5637 |
+ return false; |
5638 |
+ }); |
5639 |
+ } |
5640 |
+ |
5641 |
+ /** |
5642 |
+ * Request counts from the server |
5643 |
+ */ |
5644 |
+ function fetch_counts() |
5645 |
+ { |
5646 |
+ var active = active_lists(); |
5647 |
+ if (active.length) |
5648 |
+ rcmail.http_request('counts', { lists:active.join(',') }); |
5649 |
+ else |
5650 |
+ update_counts({}); |
5651 |
+ } |
5652 |
+ |
5653 |
+ /** |
5654 |
+ * List tasks matching the given selector |
5655 |
+ */ |
5656 |
+ function list_tasks(sel) |
5657 |
+ { |
5658 |
+ if (rcmail.busy) |
5659 |
+ return; |
5660 |
+ |
5661 |
+ if (sel && filter_masks[sel] !== undefined) { |
5662 |
+ filtermask = filter_masks[sel]; |
5663 |
+ selector = sel; |
5664 |
+ } |
5665 |
+ |
5666 |
+ var active = active_lists(), |
5667 |
+ basefilter = filtermask == FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL, |
5668 |
+ reload = active.join(',') != loadstate.lists || basefilter != loadstate.filter || loadstate.search != search_query; |
5669 |
+ |
5670 |
+ if (active.length && reload) { |
5671 |
+ ui_loading = rcmail.set_busy(true, 'loading'); |
5672 |
+ rcmail.http_request('fetch', { filter:basefilter, lists:active.join(','), q:search_query }, true); |
5673 |
+ } |
5674 |
+ else if (reload) |
5675 |
+ data_ready({ data:[], lists:'', filter:basefilter, search:search_query }); |
5676 |
+ else |
5677 |
+ render_tasklist(); |
5678 |
+ |
5679 |
+ $('#taskselector li.selected').removeClass('selected'); |
5680 |
+ $('#taskselector li.'+selector).addClass('selected'); |
5681 |
+ } |
5682 |
+ |
5683 |
+ /** |
5684 |
+ * Remove all tasks of the given list from the UI |
5685 |
+ */ |
5686 |
+ function remove_tasks(list_id) |
5687 |
+ { |
5688 |
+ // remove all tasks of the given list from index |
5689 |
+ var newindex = $.grep(listindex, function(id, i){ |
5690 |
+ return listdata[id] && listdata[id].list != list_id; |
5691 |
+ }); |
5692 |
+ |
5693 |
+ listindex = newindex; |
5694 |
+ render_tasklist(); |
5695 |
+ |
5696 |
+ // avoid reloading |
5697 |
+ me.tasklists[list_id].active = false; |
5698 |
+ loadstate.lists = active_lists(); |
5699 |
+ } |
5700 |
+ |
5701 |
+ /** |
5702 |
+ * Callback if task data from server is ready |
5703 |
+ */ |
5704 |
+ function data_ready(response) |
5705 |
+ { |
5706 |
+ listdata = {}; |
5707 |
+ listindex = []; |
5708 |
+ loadstate.lists = response.lists; |
5709 |
+ loadstate.filter = response.filter; |
5710 |
+ loadstate.search = response.search; |
5711 |
+ |
5712 |
+ for (var id, i=0; i < response.data.length; i++) { |
5713 |
+ id = response.data[i].id; |
5714 |
+ listindex.push(id); |
5715 |
+ listdata[id] = response.data[i]; |
5716 |
+ listdata[id].children = []; |
5717 |
+ // register a forward-pointer to child tasks |
5718 |
+ if (listdata[id].parent_id && listdata[listdata[id].parent_id]) |
5719 |
+ listdata[listdata[id].parent_id].children.push(id); |
5720 |
+ } |
5721 |
+ |
5722 |
+ render_tasklist(); |
5723 |
+ append_tags(response.tags || []); |
5724 |
+ rcmail.set_busy(false, 'loading', ui_loading); |
5725 |
+ } |
5726 |
+ |
5727 |
+ /** |
5728 |
+ * |
5729 |
+ */ |
5730 |
+ function render_tasklist() |
5731 |
+ { |
5732 |
+ // clear display |
5733 |
+ var id, rec, |
5734 |
+ count = 0, |
5735 |
+ msgbox = $('#listmessagebox').hide(), |
5736 |
+ list = $(rcmail.gui_objects.resultlist).html(''); |
5737 |
+ |
5738 |
+ for (var i=0; i < listindex.length; i++) { |
5739 |
+ id = listindex[i]; |
5740 |
+ rec = listdata[id]; |
5741 |
+ if (match_filter(rec)) { |
5742 |
+ render_task(rec); |
5743 |
+ count++; |
5744 |
+ } |
5745 |
+ } |
5746 |
+ |
5747 |
+ fix_tree_toggles(); |
5748 |
+ |
5749 |
+ if (!count) |
5750 |
+ msgbox.html(rcmail.gettext('notasksfound','tasklist')).show(); |
5751 |
+ } |
5752 |
+ |
5753 |
+ /** |
5754 |
+ * Show/hide child toggle buttons on all visible task items |
5755 |
+ */ |
5756 |
+ function fix_tree_toggles() |
5757 |
+ { |
5758 |
+ $('.taskitem', rcmail.gui_objects.resultlist).each(function(i,elem){ |
5759 |
+ var li = $(elem), |
5760 |
+ rec = listdata[li.attr('rel')], |
5761 |
+ childs = $('.childtasks li', li); |
5762 |
+ |
5763 |
+ $('.childtoggle', li)[(childs.length ? 'show' : 'hide')](); |
5764 |
+ }) |
5765 |
+ } |
5766 |
+ |
5767 |
+ /** |
5768 |
+ * |
5769 |
+ */ |
5770 |
+ function append_tags(taglist) |
5771 |
+ { |
5772 |
+ // find new tags |
5773 |
+ var newtags = []; |
5774 |
+ for (var i=0; i < taglist.length; i++) { |
5775 |
+ if ($.inArray(taglist[i], tags) < 0) |
5776 |
+ newtags.push(taglist[i]); |
5777 |
+ } |
5778 |
+ tags = tags.concat(newtags); |
5779 |
+ |
5780 |
+ // append new tags to tag cloud |
5781 |
+ $.each(newtags, function(i, tag){ |
5782 |
+ $('<li>').attr('rel', tag).data('value', tag).html(Q(tag)).appendTo(rcmail.gui_objects.tagslist); |
5783 |
+ }); |
5784 |
+ |
5785 |
+ // re-sort tags list |
5786 |
+ $(rcmail.gui_objects.tagslist).children('li').sortElements(function(a,b){ |
5787 |
+ return $.text([a]).toLowerCase() > $.text([b]).toLowerCase() ? 1 : -1; |
5788 |
+ }); |
5789 |
+ } |
5790 |
+ |
5791 |
+ /** |
5792 |
+ * |
5793 |
+ */ |
5794 |
+ function update_counts(counts) |
5795 |
+ { |
5796 |
+ // got new data |
5797 |
+ if (counts) |
5798 |
+ taskcounts = counts; |
5799 |
+ |
5800 |
+ // iterate over all selector links and update counts |
5801 |
+ $('#taskselector a').each(function(i, elem){ |
5802 |
+ var link = $(elem), |
5803 |
+ f = link.parent().attr('class').replace(/\s\w+/, ''); |
5804 |
+ if (f != 'all') |
5805 |
+ link.children('span').html(taskcounts[f] || '')[(taskcounts[f] ? 'show' : 'hide')](); |
5806 |
+ }); |
5807 |
+ |
5808 |
+ // spacial case: overdue |
5809 |
+ $('#taskselector li.overdue')[(taskcounts.overdue ? 'removeClass' : 'addClass')]('inactive'); |
5810 |
+ } |
5811 |
+ |
5812 |
+ /** |
5813 |
+ * Callback from server to update a single task item |
5814 |
+ */ |
5815 |
+ function update_taskitem(rec) |
5816 |
+ { |
5817 |
+ // handle a list of task records |
5818 |
+ if ($.isArray(rec)) { |
5819 |
+ $.each(rec, function(i,r){ update_taskitem(r); }); |
5820 |
+ return; |
5821 |
+ } |
5822 |
+ |
5823 |
+ var id = rec.id, |
5824 |
+ oldid = rec.tempid || id, |
5825 |
+ oldindex = $.inArray(oldid, listindex), |
5826 |
+ list = me.tasklists[rec.list]; |
5827 |
+ |
5828 |
+ if (oldindex >= 0) |
5829 |
+ listindex[oldindex] = id; |
5830 |
+ else |
5831 |
+ listindex.push(id); |
5832 |
+ |
5833 |
+ listdata[id] = rec; |
5834 |
+ |
5835 |
+ // register a forward-pointer to child tasks |
5836 |
+ if (rec.parent_id && listdata[rec.parent_id] && listdata[rec.parent_id].children && $.inArray(id, listdata[rec.parent_id].children) >= 0) |
5837 |
+ listdata[rec.parent_id].children.push(id); |
5838 |
+ |
5839 |
+ if (list.active) |
5840 |
+ render_task(rec, oldid); |
5841 |
+ else |
5842 |
+ $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).remove(); |
5843 |
+ |
5844 |
+ append_tags(rec.tags || []); |
5845 |
+ fix_tree_toggles(); |
5846 |
+ } |
5847 |
+ |
5848 |
+ /** |
5849 |
+ * Submit the given (changed) task record to the server |
5850 |
+ */ |
5851 |
+ function save_task(rec, action) |
5852 |
+ { |
5853 |
+ if (!rcmail.busy) { |
5854 |
+ saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); |
5855 |
+ rcmail.http_post('tasks/task', { action:action, t:rec, filter:filtermask }); |
5856 |
+ $('button.ui-button:ui-button').button('option', 'disabled', rcmail.busy); |
5857 |
+ return true; |
5858 |
+ } |
5859 |
+ |
5860 |
+ return false; |
5861 |
+ } |
5862 |
+ |
5863 |
+ /** |
5864 |
+ * Remove saving lock and free the UI for new input |
5865 |
+ */ |
5866 |
+ function unlock_saving() |
5867 |
+ { |
5868 |
+ if (saving_lock) { |
5869 |
+ rcmail.set_busy(false, null, saving_lock); |
5870 |
+ $('button.ui-button:ui-button').button('option', 'disabled', false); |
5871 |
+ } |
5872 |
+ } |
5873 |
+ |
5874 |
+ /** |
5875 |
+ * Render the given task into the tasks list |
5876 |
+ */ |
5877 |
+ function render_task(rec, replace) |
5878 |
+ { |
5879 |
+ var tags_html = ''; |
5880 |
+ for (var j=0; rec.tags && j < rec.tags.length; j++) |
5881 |
+ tags_html += '<span class="tag">' + Q(rec.tags[j]) + '</span>'; |
5882 |
+ |
5883 |
+ var div = $('<div>').addClass('taskhead').html( |
5884 |
+ '<div class="progressbar"><div class="progressvalue" style="width:' + (rec.complete * 100) + '%"></div></div>' + |
5885 |
+ '<input type="checkbox" name="completed[]" value="1" class="complete" ' + (rec.complete == 1.0 ? 'checked="checked" ' : '') + '/>' + |
5886 |
+ '<span class="flagged"></span>' + |
5887 |
+ '<span class="title">' + Q(rec.title) + '</span>' + |
5888 |
+ '<span class="tags">' + tags_html + '</span>' + |
5889 |
+ '<span class="date">' + Q(rec.date || rcmail.gettext('nodate','tasklist')) + '</span>' + |
5890 |
+ '<a href="#" class="actions">V</a>' |
5891 |
+ ) |
5892 |
+ .data('id', rec.id) |
5893 |
+ .draggable({ |
5894 |
+ revert: 'invalid', |
5895 |
+ addClasses: false, |
5896 |
+ cursorAt: { left:-10, top:12 }, |
5897 |
+ helper: draggable_helper, |
5898 |
+ appendTo: 'body', |
5899 |
+ start: draggable_start, |
5900 |
+ stop: draggable_stop, |
5901 |
+ revertDuration: 300 |
5902 |
+ }); |
5903 |
+ |
5904 |
+ if (rec.complete == 1.0) |
5905 |
+ div.addClass('complete'); |
5906 |
+ if (rec.flagged) |
5907 |
+ div.addClass('flagged'); |
5908 |
+ if (!rec.date) |
5909 |
+ div.addClass('nodate'); |
5910 |
+ if ((rec.mask & FILTER_MASK_OVERDUE)) |
5911 |
+ div.addClass('overdue'); |
5912 |
+ |
5913 |
+ var li, inplace = false, parent = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : null; |
5914 |
+ if (replace && (li = $('li[rel="'+replace+'"]', rcmail.gui_objects.resultlist)) && li.length) { |
5915 |
+ li.children('div.taskhead').first().replaceWith(div); |
5916 |
+ li.attr('rel', rec.id); |
5917 |
+ inplace = true; |
5918 |
+ } |
5919 |
+ else { |
5920 |
+ li = $('<li>') |
5921 |
+ .attr('rel', rec.id) |
5922 |
+ .addClass('taskitem') |
5923 |
+ .append((rec.collapsed ? '<span class="childtoggle collapsed">▶' : '<span class="childtoggle expanded">▼') + '</span>') |
5924 |
+ .append(div) |
5925 |
+ .append('<ul class="childtasks" style="' + (rec.collapsed ? 'display:none' : '') + '"></ul>'); |
5926 |
+ |
5927 |
+ if (!parent || !parent.length) |
5928 |
+ li.appendTo(rcmail.gui_objects.resultlist); |
5929 |
+ } |
5930 |
+ |
5931 |
+ if (!inplace && parent && parent.length) |
5932 |
+ li.appendTo(parent); |
5933 |
+ |
5934 |
+ if (replace) { |
5935 |
+ resort_task(rec, li, true); |
5936 |
+ // TODO: remove the item after a while if it doesn't match the current filter anymore |
5937 |
+ } |
5938 |
+ } |
5939 |
+ |
5940 |
+ /** |
5941 |
+ * Move the given task item to the right place in the list |
5942 |
+ */ |
5943 |
+ function resort_task(rec, li, animated) |
5944 |
+ { |
5945 |
+ var dir = 0, index, slice, next_li, next_id, next_rec; |
5946 |
+ |
5947 |
+ // animated moving |
5948 |
+ var insert_animated = function(li, before, after) { |
5949 |
+ if (before && li.next().get(0) == before.get(0)) |
5950 |
+ return; // nothing to do |
5951 |
+ else if (after && li.prev().get(0) == after.get(0)) |
5952 |
+ return; // nothing to do |
5953 |
+ |
5954 |
+ var speed = 300; |
5955 |
+ li.slideUp(speed, function(){ |
5956 |
+ if (before) li.insertBefore(before); |
5957 |
+ else if (after) li.insertAfter(after); |
5958 |
+ li.slideDown(speed); |
5959 |
+ }); |
5960 |
+ } |
5961 |
+ |
5962 |
+ // remove from list index |
5963 |
+ var oldlist = listindex.join('%%%'); |
5964 |
+ var oldindex = $.inArray(rec.id, listindex); |
5965 |
+ if (oldindex >= 0) { |
5966 |
+ slice = listindex.slice(0,oldindex); |
5967 |
+ listindex = slice.concat(listindex.slice(oldindex+1)); |
5968 |
+ } |
5969 |
+ |
5970 |
+ // find the right place to insert the task item |
5971 |
+ li.siblings().each(function(i, elem){ |
5972 |
+ next_li = $(elem); |
5973 |
+ next_id = next_li.attr('rel'); |
5974 |
+ next_rec = listdata[next_id]; |
5975 |
+ |
5976 |
+ if (next_id == rec.id) { |
5977 |
+ next_li = null; |
5978 |
+ return 1; // continue |
5979 |
+ } |
5980 |
+ |
5981 |
+ if (next_rec && task_cmp(rec, next_rec) > 0) { |
5982 |
+ return 1; // continue; |
5983 |
+ } |
5984 |
+ else if (next_rec && next_li && task_cmp(rec, next_rec) < 0) { |
5985 |
+ if (animated) insert_animated(li, next_li); |
5986 |
+ else li.insertBefore(next_li); |
5987 |
+ next_li = null; |
5988 |
+ return false; |
5989 |
+ } |
5990 |
+ }); |
5991 |
+ |
5992 |
+ index = $.inArray(next_id, listindex); |
5993 |
+ |
5994 |
+ if (next_li) { |
5995 |
+ if (animated) insert_animated(li, null, next_li); |
5996 |
+ else li.insertAfter(next_li); |
5997 |
+ index++; |
5998 |
+ } |
5999 |
+ |
6000 |
+ // insert into list index |
6001 |
+ if (next_id && index >= 0) { |
6002 |
+ slice = listindex.slice(0,index); |
6003 |
+ slice.push(rec.id); |
6004 |
+ listindex = slice.concat(listindex.slice(index)); |
6005 |
+ } |
6006 |
+ else { // restore old list index |
6007 |
+ listindex = oldlist.split('%%%'); |
6008 |
+ } |
6009 |
+ } |
6010 |
+ |
6011 |
+ /** |
6012 |
+ * Compare function of two task records. |
6013 |
+ * (used for sorting) |
6014 |
+ */ |
6015 |
+ function task_cmp(a, b) |
6016 |
+ { |
6017 |
+ var d = Math.floor(a.complete) - Math.floor(b.complete); |
6018 |
+ if (!d) d = (b._hasdate-0) - (a._hasdate-0); |
6019 |
+ if (!d) d = (a.datetime||99999999999) - (b.datetime||99999999999); |
6020 |
+ return d; |
6021 |
+ } |
6022 |
+ |
6023 |
+ /** |
6024 |
+ * |
6025 |
+ */ |
6026 |
+ function get_all_childs(id) |
6027 |
+ { |
6028 |
+ var cid, childs = []; |
6029 |
+ for (var i=0; listdata[id].children && i < listdata[id].children.length; i++) { |
6030 |
+ cid = listdata[id].children[i]; |
6031 |
+ childs.push(cid); |
6032 |
+ childs = childs.concat(get_all_childs(cid)); |
6033 |
+ } |
6034 |
+ |
6035 |
+ return childs; |
6036 |
+ } |
6037 |
+ |
6038 |
+ |
6039 |
+ /* Helper functions for drag & drop functionality */ |
6040 |
+ |
6041 |
+ function draggable_helper() |
6042 |
+ { |
6043 |
+ if (!draghelper) |
6044 |
+ draghelper = $('<div class="taskitem-draghelper">✔</div>'); |
6045 |
+ |
6046 |
+ return draghelper; |
6047 |
+ } |
6048 |
+ |
6049 |
+ function draggable_start(event, ui) |
6050 |
+ { |
6051 |
+ $('.taskhead, #rootdroppable, #'+rcmail.gui_objects.folderlist.id+' li').droppable({ |
6052 |
+ hoverClass: 'droptarget', |
6053 |
+ accept: droppable_accept, |
6054 |
+ drop: draggable_dropped, |
6055 |
+ addClasses: false |
6056 |
+ }); |
6057 |
+ |
6058 |
+ $(this).parent().addClass('dragging'); |
6059 |
+ $('#rootdroppable').show(); |
6060 |
+ } |
6061 |
+ |
6062 |
+ function draggable_stop(event, ui) |
6063 |
+ { |
6064 |
+ $(this).parent().removeClass('dragging'); |
6065 |
+ $('#rootdroppable').hide(); |
6066 |
+ } |
6067 |
+ |
6068 |
+ function droppable_accept(draggable) |
6069 |
+ { |
6070 |
+ if (rcmail.busy) |
6071 |
+ return false; |
6072 |
+ |
6073 |
+ var drag_id = draggable.data('id'), |
6074 |
+ drop_id = $(this).data('id'), |
6075 |
+ drag_rec = listdata[drag_id] || {}, |
6076 |
+ drop_rec = listdata[drop_id]; |
6077 |
+ |
6078 |
+ // drop target is another list |
6079 |
+ if (drag_rec && $(this).data('type') == 'tasklist') { |
6080 |
+ var drop_list = me.tasklists[drop_id], |
6081 |
+ from_list = me.tasklists[drag_rec.list]; |
6082 |
+ return !drag_rec.parent_id && drop_id != drag_rec.list && drop_list && drop_list.editable && from_list && from_list.editable; |
6083 |
+ } |
6084 |
+ |
6085 |
+ if (drop_rec && drop_rec.list != drag_rec.list) |
6086 |
+ return false; |
6087 |
+ |
6088 |
+ if (drop_id == drag_rec.parent_id) |
6089 |
+ return false; |
6090 |
+ |
6091 |
+ while (drop_rec && drop_rec.parent_id) { |
6092 |
+ if (drop_rec.parent_id == drag_id) |
6093 |
+ return false; |
6094 |
+ drop_rec = listdata[drop_rec.parent_id]; |
6095 |
+ } |
6096 |
+ |
6097 |
+ return true; |
6098 |
+ } |
6099 |
+ |
6100 |
+ function draggable_dropped(event, ui) |
6101 |
+ { |
6102 |
+ var drop_id = $(this).data('id'), |
6103 |
+ task_id = ui.draggable.data('id'), |
6104 |
+ rec = listdata[task_id], |
6105 |
+ parent, li; |
6106 |
+ |
6107 |
+ // dropped on another list -> move |
6108 |
+ if ($(this).data('type') == 'tasklist') { |
6109 |
+ if (rec) { |
6110 |
+ save_task({ id:rec.id, list:drop_id, _fromlist:rec.list }, 'move'); |
6111 |
+ rec.list = drop_id; |
6112 |
+ } |
6113 |
+ } |
6114 |
+ // dropped on a new parent task or root |
6115 |
+ else { |
6116 |
+ parent = drop_id ? $('li[rel="'+drop_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : $(rcmail.gui_objects.resultlist) |
6117 |
+ |
6118 |
+ if (rec && parent.length) { |
6119 |
+ // submit changes to server |
6120 |
+ rec.parent_id = drop_id || 0; |
6121 |
+ save_task(rec, 'edit'); |
6122 |
+ |
6123 |
+ li = ui.draggable.parent(); |
6124 |
+ li.slideUp(300, function(){ |
6125 |
+ li.appendTo(parent); |
6126 |
+ resort_task(rec, li); |
6127 |
+ li.slideDown(300); |
6128 |
+ fix_tree_toggles(); |
6129 |
+ }); |
6130 |
+ } |
6131 |
+ } |
6132 |
+ } |
6133 |
+ |
6134 |
+ |
6135 |
+ /** |
6136 |
+ * Show task details in a dialog |
6137 |
+ */ |
6138 |
+ function task_show_dialog(id) |
6139 |
+ { |
6140 |
+ var $dialog = $('#taskshow'), rec; |
6141 |
+ |
6142 |
+ if ($dialog.is(':ui-dialog')) |
6143 |
+ $dialog.dialog('close'); |
6144 |
+ |
6145 |
+ if (!(rec = listdata[id]) || clear_popups({})) |
6146 |
+ return; |
6147 |
+ |
6148 |
+ me.selected_task = rec; |
6149 |
+ |
6150 |
+ // fill dialog data |
6151 |
+ $('#task-parent-title').html(Q(rec.parent_title || '')+' »').css('display', rec.parent_title ? 'block' : 'none'); |
6152 |
+ $('#task-title').html(Q(rec.title || '')); |
6153 |
+ $('#task-description').html(text2html(rec.description || '', 300, 6))[(rec.description ? 'show' : 'hide')](); |
6154 |
+ $('#task-date')[(rec.date ? 'show' : 'hide')]().children('.task-text').html(Q(rec.date || rcmail.gettext('nodate','tasklist'))); |
6155 |
+ $('#task-time').html(Q(rec.time || '')); |
6156 |
+ $('#task-start')[(rec.startdate ? 'show' : 'hide')]().children('.task-text').html(Q(rec.startdate || '')); |
6157 |
+ $('#task-starttime').html(Q(rec.starttime || '')); |
6158 |
+ $('#task-alarm')[(rec.alarms_text ? 'show' : 'hide')]().children('.task-text').html(Q(rec.alarms_text)); |
6159 |
+ $('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%'); |
6160 |
+ $('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : '')); |
6161 |
+ |
6162 |
+ var taglist = $('#task-tags')[(rec.tags && rec.tags.length ? 'show' : 'hide')]().children('.task-text').empty(); |
6163 |
+ if (rec.tags && rec.tags.length) { |
6164 |
+ $.each(rec.tags, function(i,val){ |
6165 |
+ $('<span>').addClass('tag-element').html(Q(val)).data('value', val).appendTo(taglist); |
6166 |
+ }); |
6167 |
+ } |
6168 |
+ |
6169 |
+ // build attachments list |
6170 |
+ $('#task-attachments').hide(); |
6171 |
+ if ($.isArray(rec.attachments)) { |
6172 |
+ task_show_attachments(rec.attachments || [], $('#task-attachments').children('.task-text'), rec); |
6173 |
+ if (rec.attachments.length > 0) { |
6174 |
+ $('#task-attachments').show(); |
6175 |
+ } |
6176 |
+ } |
6177 |
+ |
6178 |
+ // define dialog buttons |
6179 |
+ var buttons = []; |
6180 |
+ buttons.push({ |
6181 |
+ text: rcmail.gettext('edit','tasklist'), |
6182 |
+ click: function() { |
6183 |
+ task_edit_dialog(me.selected_task.id, 'edit'); |
6184 |
+ }, |
6185 |
+ disabled: rcmail.busy |
6186 |
+ }); |
6187 |
+ |
6188 |
+ buttons.push({ |
6189 |
+ text: rcmail.gettext('delete','tasklist'), |
6190 |
+ click: function() { |
6191 |
+ if (delete_task(me.selected_task.id)) |
6192 |
+ $dialog.dialog('close'); |
6193 |
+ }, |
6194 |
+ disabled: rcmail.busy |
6195 |
+ }); |
6196 |
+ |
6197 |
+ // open jquery UI dialog |
6198 |
+ $dialog.dialog({ |
6199 |
+ modal: false, |
6200 |
+ resizable: true, |
6201 |
+ closeOnEscape: true, |
6202 |
+ title: rcmail.gettext('taskdetails', 'tasklist'), |
6203 |
+ open: function() { |
6204 |
+ $dialog.parent().find('.ui-button').first().focus(); |
6205 |
+ }, |
6206 |
+ close: function() { |
6207 |
+ $dialog.dialog('destroy').appendTo(document.body); |
6208 |
+ }, |
6209 |
+ buttons: buttons, |
6210 |
+ minWidth: 500, |
6211 |
+ width: 580 |
6212 |
+ }).show(); |
6213 |
+ |
6214 |
+ // set dialog size according to content |
6215 |
+ me.dialog_resize($dialog.get(0), $dialog.height(), 580); |
6216 |
+ } |
6217 |
+ |
6218 |
+ /** |
6219 |
+ * Opens the dialog to edit a task |
6220 |
+ */ |
6221 |
+ function task_edit_dialog(id, action, presets) |
6222 |
+ { |
6223 |
+ $('#taskshow:ui-dialog').dialog('close'); |
6224 |
+ |
6225 |
+ var rec = listdata[id] || presets, |
6226 |
+ $dialog = $('<div>'), |
6227 |
+ editform = $('#taskedit'), |
6228 |
+ list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] : |
6229 |
+ (me.selected_list ? me.tasklists[me.selected_list] : { editable: action=='new' }); |
6230 |
+ |
6231 |
+ if (rcmail.busy || !list.editable || (action == 'edit' && (!rec || rec.readonly))) |
6232 |
+ return false; |
6233 |
+ |
6234 |
+ me.selected_task = $.extend({ alarms:'' }, rec); // clone task object |
6235 |
+ rec = me.selected_task; |
6236 |
+ |
6237 |
+ // assign temporary id |
6238 |
+ if (!me.selected_task.id) |
6239 |
+ me.selected_task.id = -(++idcount); |
6240 |
+ |
6241 |
+ // reset dialog first |
6242 |
+ $('#taskeditform').get(0).reset(); |
6243 |
+ |
6244 |
+ // fill form data |
6245 |
+ var title = $('#taskedit-title').val(rec.title || ''); |
6246 |
+ var description = $('#taskedit-description').val(rec.description || ''); |
6247 |
+ var recdate = $('#taskedit-date').val(rec.date || ''); |
6248 |
+ var rectime = $('#taskedit-time').val(rec.time || ''); |
6249 |
+ var recstartdate = $('#taskedit-startdate').val(rec.startdate || ''); |
6250 |
+ var recstarttime = $('#taskedit-starttime').val(rec.starttime || ''); |
6251 |
+ var complete = $('#taskedit-completeness').val((rec.complete || 0) * 100); |
6252 |
+ completeness_slider.slider('value', complete.val()); |
6253 |
+ var tasklist = $('#taskedit-tasklist').val(rec.list || 0).prop('disabled', rec.parent_id ? true : false); |
6254 |
+ |
6255 |
+ // tag-edit line |
6256 |
+ var tagline = $(rcmail.gui_objects.edittagline).empty(); |
6257 |
+ $.each(typeof rec.tags == 'object' && rec.tags.length ? rec.tags : [''], function(i,val){ |
6258 |
+ $('<input>') |
6259 |
+ .attr('name', 'tags[]') |
6260 |
+ .attr('tabindex', '3') |
6261 |
+ .addClass('tag') |
6262 |
+ .val(val) |
6263 |
+ .appendTo(tagline); |
6264 |
+ }); |
6265 |
+ |
6266 |
+ $('input.tag', rcmail.gui_objects.edittagline).tagedit({ |
6267 |
+ animSpeed: 100, |
6268 |
+ allowEdit: false, |
6269 |
+ checkNewEntriesCaseSensitive: false, |
6270 |
+ autocompleteOptions: { source: tags, minLength: 0 }, |
6271 |
+ texts: { removeLinkTitle: rcmail.gettext('removetag', 'tasklist') } |
6272 |
+ }); |
6273 |
+ |
6274 |
+ // set alarm(s) |
6275 |
+ if (rec.alarms || action != 'new') { |
6276 |
+ var valarms = (typeof rec.alarms == 'string' ? rec.alarms.split(';') : rec.alarms) || ['']; |
6277 |
+ for (var alarm, i=0; i < valarms.length; i++) { |
6278 |
+ alarm = String(valarms[i]).split(':'); |
6279 |
+ if (!alarm[1] && alarm[0]) alarm[1] = 'DISPLAY'; |
6280 |
+ $('#taskedit select.edit-alarm-type').val(alarm[1]); |
6281 |
+ |
6282 |
+ if (alarm[0].match(/@(\d+)/)) { |
6283 |
+ var ondate = fromunixtime(parseInt(RegExp.$1)); |
6284 |
+ $('#taskedit select.edit-alarm-offset').val('@'); |
6285 |
+ $('#taskedit input.edit-alarm-date').val(me.format_datetime(ondate, 1)); |
6286 |
+ $('#taskedit input.edit-alarm-time').val(me.format_datetime(ondate, 2)); |
6287 |
+ } |
6288 |
+ else if (alarm[0].match(/([-+])(\d+)([MHD])/)) { |
6289 |
+ $('#taskedit input.edit-alarm-value').val(RegExp.$2); |
6290 |
+ $('#taskedit select.edit-alarm-offset').val(''+RegExp.$1+RegExp.$3); |
6291 |
+ } |
6292 |
+ |
6293 |
+ break; // only one alarm is currently supported |
6294 |
+ } |
6295 |
+ } |
6296 |
+ // set correct visibility by triggering onchange handlers |
6297 |
+ $('#taskedit select.edit-alarm-type, #taskedit select.edit-alarm-offset').change(); |
6298 |
+ |
6299 |
+ // attachments |
6300 |
+ rcmail.enable_command('remove-attachment', list.editable); |
6301 |
+ me.selected_task.deleted_attachments = []; |
6302 |
+ // we're sharing some code for uploads handling with app.js |
6303 |
+ rcmail.env.attachments = []; |
6304 |
+ rcmail.env.compose_id = me.selected_task.id; // for rcmail.async_upload_form() |
6305 |
+ |
6306 |
+ if ($.isArray(rec.attachments)) { |
6307 |
+ task_show_attachments(rec.attachments, $('#taskedit-attachments'), rec, true); |
6308 |
+ } |
6309 |
+ else { |
6310 |
+ $('#taskedit-attachments > ul').empty(); |
6311 |
+ } |
6312 |
+ |
6313 |
+ // show/hide tabs according to calendar's feature support |
6314 |
+ $('#taskedit-tab-attachments')[(list.attachments||rec.attachments?'show':'hide')](); |
6315 |
+ |
6316 |
+ // activate the first tab |
6317 |
+ $('#taskedit').tabs('select', 0); |
6318 |
+ |
6319 |
+ // define dialog buttons |
6320 |
+ var buttons = {}; |
6321 |
+ buttons[rcmail.gettext('save', 'tasklist')] = function() { |
6322 |
+ // copy form field contents into task object to save |
6323 |
+ $.each({ title:title, description:description, date:recdate, time:rectime, startdate:recstartdate, starttime:recstarttime, list:tasklist }, function(key,input){ |
6324 |
+ me.selected_task[key] = input.val(); |
6325 |
+ }); |
6326 |
+ me.selected_task.tags = []; |
6327 |
+ me.selected_task.attachments = []; |
6328 |
+ |
6329 |
+ // do some basic input validation |
6330 |
+ if (!me.selected_task.title || !me.selected_task.title.length) { |
6331 |
+ title.focus(); |
6332 |
+ return false; |
6333 |
+ } |
6334 |
+ else if (me.selected_task.startdate && me.selected_task.date) { |
6335 |
+ var startdate = $.datepicker.parseDate(datepicker_settings.dateFormat, me.selected_task.startdate, datepicker_settings); |
6336 |
+ var duedate = $.datepicker.parseDate(datepicker_settings.dateFormat, me.selected_task.date, datepicker_settings); |
6337 |
+ if (startdate > duedate) { |
6338 |
+ alert(rcmail.gettext('invalidstartduedates', 'tasklist')); |
6339 |
+ return false; |
6340 |
+ } |
6341 |
+ } |
6342 |
+ |
6343 |
+ $('input[type="hidden"]', rcmail.gui_objects.edittagline).each(function(i,elem){ |
6344 |
+ if (elem.value) |
6345 |
+ me.selected_task.tags.push(elem.value); |
6346 |
+ }); |
6347 |
+ |
6348 |
+ // serialize alarm settings |
6349 |
+ var alarm = $('#taskedit select.edit-alarm-type').val(); |
6350 |
+ if (alarm) { |
6351 |
+ var val, offset = $('#taskedit select.edit-alarm-offset').val(); |
6352 |
+ if (offset == '@') |
6353 |
+ me.selected_task.alarms = '@' + date2unixtime(parse_datetime($('#taskedit input.edit-alarm-time').val(), $('#taskedit input.edit-alarm-date').val())) + ':' + alarm; |
6354 |
+ else if ((val = parseInt($('#taskedit input.edit-alarm-value').val())) && !isNaN(val) && val >= 0) |
6355 |
+ me.selected_task.alarms = offset[0] + val + offset[1] + ':' + alarm; |
6356 |
+ } |
6357 |
+ |
6358 |
+ // uploaded attachments list |
6359 |
+ for (var i in rcmail.env.attachments) { |
6360 |
+ if (i.match(/^rcmfile(.+)/)) |
6361 |
+ me.selected_task.attachments.push(RegExp.$1); |
6362 |
+ } |
6363 |
+ |
6364 |
+ // task assigned to a new list |
6365 |
+ if (me.selected_task.list && me.selected_task.list != rec.list) { |
6366 |
+ me.selected_task._fromlist = rec.list; |
6367 |
+ } |
6368 |
+ |
6369 |
+ me.selected_task.complete = complete.val() / 100; |
6370 |
+ if (isNaN(me.selected_task.complete)) |
6371 |
+ me.selected_task.complete = null; |
6372 |
+ |
6373 |
+ if (!me.selected_task.list && list.id) |
6374 |
+ me.selected_task.list = list.id; |
6375 |
+ |
6376 |
+ if (save_task(me.selected_task, action)) |
6377 |
+ $dialog.dialog('close'); |
6378 |
+ }; |
6379 |
+ |
6380 |
+ if (action != 'new') { |
6381 |
+ buttons[rcmail.gettext('delete', 'tasklist')] = function() { |
6382 |
+ if (delete_task(rec.id)) |
6383 |
+ $dialog.dialog('close'); |
6384 |
+ }; |
6385 |
+ } |
6386 |
+ |
6387 |
+ buttons[rcmail.gettext('cancel', 'tasklist')] = function() { |
6388 |
+ $dialog.dialog('close'); |
6389 |
+ }; |
6390 |
+ |
6391 |
+ // open jquery UI dialog |
6392 |
+ $dialog.dialog({ |
6393 |
+ modal: true, |
6394 |
+ resizable: (!bw.ie6 && !bw.ie7), // disable for performance reasons |
6395 |
+ closeOnEscape: false, |
6396 |
+ title: rcmail.gettext((action == 'edit' ? 'edittask' : 'newtask'), 'tasklist'), |
6397 |
+ close: function() { |
6398 |
+ editform.hide().appendTo(document.body); |
6399 |
+ $dialog.dialog('destroy').remove(); |
6400 |
+ }, |
6401 |
+ buttons: buttons, |
6402 |
+ minHeight: 460, |
6403 |
+ minWidth: 500, |
6404 |
+ width: 580 |
6405 |
+ }).append(editform.show()); // adding form content AFTERWARDS massively speeds up opening on IE |
6406 |
+ |
6407 |
+ title.select(); |
6408 |
+ |
6409 |
+ // set dialog size according to content |
6410 |
+ me.dialog_resize($dialog.get(0), $dialog.height(), 580); |
6411 |
+ } |
6412 |
+ |
6413 |
+ |
6414 |
+ /** |
6415 |
+ * Open a task attachment either in a browser window for inline view or download it |
6416 |
+ */ |
6417 |
+ function load_attachment(rec, att) |
6418 |
+ { |
6419 |
+ // can't open temp attachments |
6420 |
+ if (!rec.id || rec.id < 0) |
6421 |
+ return false; |
6422 |
+ |
6423 |
+ var qstring = '_id='+urlencode(att.id)+'&_t='+urlencode(rec.recurrence_id||rec.id)+'&_list='+urlencode(rec.list); |
6424 |
+ |
6425 |
+ // open attachment in frame if it's of a supported mimetype |
6426 |
+ // similar as in app.js and calendar_ui.js |
6427 |
+ if (att.id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) { |
6428 |
+ rcmail.attachment_win = window.open(rcmail.env.comm_path+'&_action=get-attachment&'+qstring+'&_frame=1', 'rcubetaskattachment'); |
6429 |
+ if (rcmail.attachment_win) { |
6430 |
+ window.setTimeout(function() { rcmail.attachment_win.focus(); }, 10); |
6431 |
+ return; |
6432 |
+ } |
6433 |
+ } |
6434 |
+ |
6435 |
+ rcmail.goto_url('get-attachment', qstring+'&_download=1', false); |
6436 |
+ }; |
6437 |
+ |
6438 |
+ /** |
6439 |
+ * Build task attachments list |
6440 |
+ */ |
6441 |
+ function task_show_attachments(list, container, rec, edit) |
6442 |
+ { |
6443 |
+ var i, id, len, content, li, elem, |
6444 |
+ ul = $('<ul>').addClass('attachmentslist'); |
6445 |
+ |
6446 |
+ for (i=0, len=list.length; i<len; i++) { |
6447 |
+ elem = list[i]; |
6448 |
+ li = $('<li>').addClass(elem.classname); |
6449 |
+ |
6450 |
+ if (edit) { |
6451 |
+ rcmail.env.attachments[elem.id] = elem; |
6452 |
+ // delete icon |
6453 |
+ content = $('<a>') |
6454 |
+ .attr('href', '#delete') |
6455 |
+ .attr('title', rcmail.gettext('delete')) |
6456 |
+ .addClass('delete') |
6457 |
+ .click({ id:elem.id }, function(e) { |
6458 |
+ remove_attachment(this, e.data.id); |
6459 |
+ return false; |
6460 |
+ }); |
6461 |
+ |
6462 |
+ if (!rcmail.env.deleteicon) { |
6463 |
+ content.html(rcmail.gettext('delete')); |
6464 |
+ } |
6465 |
+ else { |
6466 |
+ $('<img>').attr('src', rcmail.env.deleteicon).attr('alt', rcmail.gettext('delete')).appendTo(content); |
6467 |
+ } |
6468 |
+ |
6469 |
+ li.append(content); |
6470 |
+ } |
6471 |
+ |
6472 |
+ // name/link |
6473 |
+ $('<a>') |
6474 |
+ .attr('href', '#load') |
6475 |
+ .addClass('file') |
6476 |
+ .html(elem.name).click({ task:rec, att:elem }, function(e) { |
6477 |
+ load_attachment(e.data.task, e.data.att); |
6478 |
+ return false; |
6479 |
+ }).appendTo(li); |
6480 |
+ |
6481 |
+ ul.append(li); |
6482 |
+ } |
6483 |
+ |
6484 |
+ if (edit && rcmail.gui_objects.attachmentlist) { |
6485 |
+ ul.id = rcmail.gui_objects.attachmentlist.id; |
6486 |
+ rcmail.gui_objects.attachmentlist = ul.get(0); |
6487 |
+ } |
6488 |
+ |
6489 |
+ container.empty().append(ul); |
6490 |
+ }; |
6491 |
+ |
6492 |
+ /** |
6493 |
+ * |
6494 |
+ */ |
6495 |
+ var remove_attachment = function(elem, id) |
6496 |
+ { |
6497 |
+ $(elem.parentNode).hide(); |
6498 |
+ me.selected_task.deleted_attachments.push(id); |
6499 |
+ delete rcmail.env.attachments[id]; |
6500 |
+ }; |
6501 |
+ |
6502 |
+ /** |
6503 |
+ * |
6504 |
+ */ |
6505 |
+ function add_childtask(id) |
6506 |
+ { |
6507 |
+ if (rcmail.busy) |
6508 |
+ return false; |
6509 |
+ |
6510 |
+ var rec = listdata[id]; |
6511 |
+ task_edit_dialog(null, 'new', { parent_id:id, list:rec.list }); |
6512 |
+ } |
6513 |
+ |
6514 |
+ /** |
6515 |
+ * Delete the given task |
6516 |
+ */ |
6517 |
+ function delete_task(id) |
6518 |
+ { |
6519 |
+ var rec = listdata[id]; |
6520 |
+ if (!rec || rec.readonly || rcmail.busy) |
6521 |
+ return false; |
6522 |
+ |
6523 |
+ var html, buttons = [{ |
6524 |
+ text: rcmail.gettext('cancel', 'tasklist'), |
6525 |
+ click: function() { |
6526 |
+ $(this).dialog('close'); |
6527 |
+ } |
6528 |
+ }]; |
6529 |
+ |
6530 |
+ if (rec.children && rec.children.length) { |
6531 |
+ html = rcmail.gettext('deleteparenttasktconfirm','tasklist'); |
6532 |
+ buttons.push({ |
6533 |
+ text: rcmail.gettext('deletethisonly','tasklist'), |
6534 |
+ click: function() { |
6535 |
+ _delete_task(id, 0); |
6536 |
+ $(this).dialog('close'); |
6537 |
+ } |
6538 |
+ }); |
6539 |
+ buttons.push({ |
6540 |
+ text: rcmail.gettext('deletewithchilds','tasklist'), |
6541 |
+ click: function() { |
6542 |
+ _delete_task(id, 1); |
6543 |
+ $(this).dialog('close'); |
6544 |
+ } |
6545 |
+ }); |
6546 |
+ } |
6547 |
+ else { |
6548 |
+ html = rcmail.gettext('deletetasktconfirm','tasklist'); |
6549 |
+ buttons.push({ |
6550 |
+ text: rcmail.gettext('delete','tasklist'), |
6551 |
+ click: function() { |
6552 |
+ _delete_task(id, 0); |
6553 |
+ $(this).dialog('close'); |
6554 |
+ } |
6555 |
+ }); |
6556 |
+ } |
6557 |
+ |
6558 |
+ var $dialog = $('<div>').html(html); |
6559 |
+ $dialog.dialog({ |
6560 |
+ modal: true, |
6561 |
+ width: 520, |
6562 |
+ dialogClass: 'warning', |
6563 |
+ title: rcmail.gettext('deletetask', 'tasklist'), |
6564 |
+ buttons: buttons, |
6565 |
+ close: function(){ |
6566 |
+ $dialog.dialog('destroy').hide(); |
6567 |
+ } |
6568 |
+ }).addClass('tasklist-confirm').show(); |
6569 |
+ |
6570 |
+ return true; |
6571 |
+ } |
6572 |
+ |
6573 |
+ /** |
6574 |
+ * Subfunction to submit the delete command after confirm |
6575 |
+ */ |
6576 |
+ function _delete_task(id, mode) |
6577 |
+ { |
6578 |
+ var rec = listdata[id], |
6579 |
+ li = $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).hide(); |
6580 |
+ |
6581 |
+ saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); |
6582 |
+ rcmail.http_post('task', { action:'delete', t:{ id:rec.id, list:rec.list }, mode:mode, filter:filtermask }); |
6583 |
+ |
6584 |
+ // move childs to parent/root |
6585 |
+ if (mode != 1 && rec.children !== undefined) { |
6586 |
+ var parent_node = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > .childtasks', rcmail.gui_objects.resultlist) : null; |
6587 |
+ if (!parent_node || !parent_node.length) |
6588 |
+ parent_node = rcmail.gui_objects.resultlist; |
6589 |
+ |
6590 |
+ $.each(rec.children, function(i,cid) { |
6591 |
+ var child = listdata[cid]; |
6592 |
+ child.parent_id = rec.parent_id; |
6593 |
+ resort_task(child, $('li[rel="'+cid+'"]').appendTo(parent_node), true); |
6594 |
+ }); |
6595 |
+ } |
6596 |
+ |
6597 |
+ li.remove(); |
6598 |
+ } |
6599 |
+ |
6600 |
+ /** |
6601 |
+ * Check if the given task matches the current filtermask and tag selection |
6602 |
+ */ |
6603 |
+ function match_filter(rec) |
6604 |
+ { |
6605 |
+ var match = !filtermask || (filtermask & rec.mask) > 0; |
6606 |
+ |
6607 |
+ if (match && tagsfilter.length) { |
6608 |
+ match = rec.tags && rec.tags.length; |
6609 |
+ for (var i=0; match && i < tagsfilter.length; i++) { |
6610 |
+ if ($.inArray(tagsfilter[i], rec.tags) < 0) |
6611 |
+ match = false; |
6612 |
+ } |
6613 |
+ } |
6614 |
+ |
6615 |
+ return match; |
6616 |
+ } |
6617 |
+ |
6618 |
+ /** |
6619 |
+ * |
6620 |
+ */ |
6621 |
+ function list_edit_dialog(id) |
6622 |
+ { |
6623 |
+ var list = me.tasklists[id], |
6624 |
+ $dialog = $('#tasklistform'); |
6625 |
+ editform = $('#tasklisteditform'); |
6626 |
+ |
6627 |
+ if ($dialog.is(':ui-dialog')) |
6628 |
+ $dialog.dialog('close'); |
6629 |
+ |
6630 |
+ if (!list) |
6631 |
+ list = { name:'', editable:true, showalarms:true }; |
6632 |
+ |
6633 |
+ // fill edit form |
6634 |
+ var name = $('#taskedit-tasklistame').prop('disabled', !list.editable).val(list.editname || list.name), |
6635 |
+ alarms = $('#taskedit-showalarms').prop('checked', list.showalarms).get(0), |
6636 |
+ parent = $('#taskedit-parentfolder').val(list.parentfolder); |
6637 |
+ |
6638 |
+ // dialog buttons |
6639 |
+ var buttons = {}; |
6640 |
+ |
6641 |
+ buttons[rcmail.gettext('save','tasklist')] = function() { |
6642 |
+ // do some input validation |
6643 |
+ if (!name.val() || name.val().length < 2) { |
6644 |
+ alert(rcmail.gettext('invalidlistproperties', 'tasklist')); |
6645 |
+ name.select(); |
6646 |
+ return; |
6647 |
+ } |
6648 |
+ |
6649 |
+ // post data to server |
6650 |
+ var data = editform.serializeJSON(); |
6651 |
+ if (list.id) |
6652 |
+ data.id = list.id; |
6653 |
+ if (alarms) |
6654 |
+ data.showalarms = alarms.checked ? 1 : 0; |
6655 |
+ if (parent.length) |
6656 |
+ data.parentfolder = $('option:selected', parent).val(); |
6657 |
+ |
6658 |
+ saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); |
6659 |
+ rcmail.http_post('tasklist', { action:(list.id ? 'edit' : 'new'), l:data }); |
6660 |
+ $dialog.dialog('close'); |
6661 |
+ }; |
6662 |
+ |
6663 |
+ buttons[rcmail.gettext('cancel','tasklist')] = function() { |
6664 |
+ $dialog.dialog('close'); |
6665 |
+ }; |
6666 |
+ |
6667 |
+ // open jquery UI dialog |
6668 |
+ $dialog.dialog({ |
6669 |
+ modal: true, |
6670 |
+ resizable: true, |
6671 |
+ closeOnEscape: false, |
6672 |
+ title: rcmail.gettext((list.id ? 'editlist' : 'createlist'), 'tasklist'), |
6673 |
+ close: function() { $dialog.dialog('destroy').hide(); }, |
6674 |
+ buttons: buttons, |
6675 |
+ minWidth: 400, |
6676 |
+ width: 420 |
6677 |
+ }).show(); |
6678 |
+ } |
6679 |
+ |
6680 |
+ /** |
6681 |
+ * |
6682 |
+ */ |
6683 |
+ function list_remove(id) |
6684 |
+ { |
6685 |
+ var list = me.tasklists[id]; |
6686 |
+ if (list && list.editable && confirm(rcmail.gettext('deletelistconfirm', 'tasklist'))) { |
6687 |
+ saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); |
6688 |
+ rcmail.http_post('tasklist', { action:'remove', l:{ id:list.id } }); |
6689 |
+ return true; |
6690 |
+ } |
6691 |
+ return false; |
6692 |
+ } |
6693 |
+ |
6694 |
+ /** |
6695 |
+ * Callback from server to finally remove the given list |
6696 |
+ */ |
6697 |
+ function destroy_list(prop) |
6698 |
+ { |
6699 |
+ var list = me.tasklists[prop.id], |
6700 |
+ li = rcmail.get_folder_li(prop.id, 'rcmlitasklist'); |
6701 |
+ |
6702 |
+ if (li) { |
6703 |
+ $(li).remove(); |
6704 |
+ } |
6705 |
+ if (list) { |
6706 |
+ list.active = false; |
6707 |
+ // delete me.tasklists[prop.id]; |
6708 |
+ unlock_saving(); |
6709 |
+ remove_tasks(list.id); |
6710 |
+ } |
6711 |
+ } |
6712 |
+ |
6713 |
+ /** |
6714 |
+ * |
6715 |
+ */ |
6716 |
+ function insert_list(prop) |
6717 |
+ { |
6718 |
+ var li = $('<li>').attr('id', 'rcmlitasklist'+prop.id) |
6719 |
+ .append('<input type="checkbox" name="_list[]" value="'+prop.id+'" checked="checked" />') |
6720 |
+ .append('<span class="handle"> </span>') |
6721 |
+ .append('<span class="listname">'+Q(prop.name)+'</span>'); |
6722 |
+ $(rcmail.gui_objects.folderlist).append(li); |
6723 |
+ me.tasklists[prop.id] = prop; |
6724 |
+ init_tasklist_li(li.get(0), prop.id); |
6725 |
+ } |
6726 |
+ |
6727 |
+ /** |
6728 |
+ * |
6729 |
+ */ |
6730 |
+ function update_list(prop) |
6731 |
+ { |
6732 |
+ var id = prop.oldid || prop.id, |
6733 |
+ li = rcmail.get_folder_li(id, 'rcmlitasklist'); |
6734 |
+ |
6735 |
+ if (me.tasklists[id] && li) { |
6736 |
+ delete me.tasklists[id]; |
6737 |
+ me.tasklists[prop.id] = prop; |
6738 |
+ $(li).data('id', prop.id); |
6739 |
+ $('#'+li.id+' input').data('id', prop.id); |
6740 |
+ $('.listname', li).html(Q(prop.name)); |
6741 |
+ } |
6742 |
+ } |
6743 |
+ |
6744 |
+ /** |
6745 |
+ * Execute search |
6746 |
+ */ |
6747 |
+ function quicksearch() |
6748 |
+ { |
6749 |
+ var q; |
6750 |
+ if (rcmail.gui_objects.qsearchbox && (q = rcmail.gui_objects.qsearchbox.value)) { |
6751 |
+ var id = 'search-'+q; |
6752 |
+ var resources = []; |
6753 |
+ |
6754 |
+ for (var rid in me.tasklists) { |
6755 |
+ if (me.tasklists[rid].active) { |
6756 |
+ resources.push(rid); |
6757 |
+ } |
6758 |
+ } |
6759 |
+ id += '@'+resources.join(','); |
6760 |
+ |
6761 |
+ // ignore if query didn't change |
6762 |
+ if (search_request == id) |
6763 |
+ return; |
6764 |
+ |
6765 |
+ search_request = id; |
6766 |
+ search_query = q; |
6767 |
+ |
6768 |
+ list_tasks('all'); |
6769 |
+ } |
6770 |
+ else // empty search input equals reset |
6771 |
+ this.reset_search(); |
6772 |
+ } |
6773 |
+ |
6774 |
+ /** |
6775 |
+ * Reset search and get back to normal listing |
6776 |
+ */ |
6777 |
+ function reset_search() |
6778 |
+ { |
6779 |
+ $(rcmail.gui_objects.qsearchbox).val(''); |
6780 |
+ |
6781 |
+ if (search_request) { |
6782 |
+ search_request = search_query = null; |
6783 |
+ list_tasks(); |
6784 |
+ } |
6785 |
+ } |
6786 |
+ |
6787 |
+ |
6788 |
+ /**** Utility functions ****/ |
6789 |
+ |
6790 |
+ /** |
6791 |
+ * Clear any text selection |
6792 |
+ * (text is probably selected when double-clicking somewhere) |
6793 |
+ */ |
6794 |
+ function clearSelection() |
6795 |
+ { |
6796 |
+ if (document.selection && document.selection.empty) { |
6797 |
+ document.selection.empty() ; |
6798 |
+ } |
6799 |
+ else if (window.getSelection) { |
6800 |
+ var sel = window.getSelection(); |
6801 |
+ if (sel && sel.removeAllRanges) |
6802 |
+ sel.removeAllRanges(); |
6803 |
+ } |
6804 |
+ } |
6805 |
+ |
6806 |
+ /** |
6807 |
+ * Hide all open popup menus |
6808 |
+ */ |
6809 |
+ function clear_popups(e) |
6810 |
+ { |
6811 |
+ var count = 0, target = e.target; |
6812 |
+ if (target && target.className == 'inner') |
6813 |
+ target = e.target.parentNode; |
6814 |
+ |
6815 |
+ $('.popupmenu:visible').each(function(i, elem){ |
6816 |
+ var menu = $(elem), id = elem.id; |
6817 |
+ if (target.id != id+'link' && (!menu.data('sticky') || !target_overlaps(e.target, elem))) { |
6818 |
+ menu.hide(); |
6819 |
+ count++; |
6820 |
+ } |
6821 |
+ }); |
6822 |
+ return count; |
6823 |
+ } |
6824 |
+ |
6825 |
+ /** |
6826 |
+ * Check whether the event target is a descentand of the given element |
6827 |
+ */ |
6828 |
+ function target_overlaps(target, elem) |
6829 |
+ { |
6830 |
+ while (target.parentNode) { |
6831 |
+ if (target.parentNode == elem) |
6832 |
+ return true; |
6833 |
+ target = target.parentNode; |
6834 |
+ } |
6835 |
+ return false; |
6836 |
+ } |
6837 |
+ |
6838 |
+ /** |
6839 |
+ * |
6840 |
+ */ |
6841 |
+ function active_lists() |
6842 |
+ { |
6843 |
+ var active = []; |
6844 |
+ for (var id in me.tasklists) { |
6845 |
+ if (me.tasklists[id].active) |
6846 |
+ active.push(id); |
6847 |
+ } |
6848 |
+ return active; |
6849 |
+ } |
6850 |
+ |
6851 |
+ // resize and reposition (center) the dialog window |
6852 |
+ this.dialog_resize = function(id, height, width) |
6853 |
+ { |
6854 |
+ var win = $(window), w = win.width(), h = win.height(); |
6855 |
+ $(id).dialog('option', { height: Math.min(h-20, height+130), width: Math.min(w-20, width+50) }) |
6856 |
+ .dialog('option', 'position', ['center', 'center']); // only works in a separate call (!?) |
6857 |
+ }; |
6858 |
+ |
6859 |
+ /** |
6860 |
+ * Register event handlers on a tasklist (folder) item |
6861 |
+ */ |
6862 |
+ function init_tasklist_li(li, id) |
6863 |
+ { |
6864 |
+ $('#'+li.id+' input').click(function(e){ |
6865 |
+ var id = $(this).data('id'); |
6866 |
+ if (me.tasklists[id]) { // add or remove event source on click |
6867 |
+ me.tasklists[id].active = this.checked; |
6868 |
+ fetch_counts(); |
6869 |
+ if (!this.checked) remove_tasks(id); |
6870 |
+ else list_tasks(null); |
6871 |
+ rcmail.http_post('tasklist', { action:'subscribe', l:{ id:id, active:me.tasklists[id].active?1:0 } }); |
6872 |
+ } |
6873 |
+ }).data('id', id).get(0).checked = me.tasklists[id].active || false; |
6874 |
+ |
6875 |
+ $(li).click(function(e){ |
6876 |
+ var id = $(this).data('id'); |
6877 |
+ rcmail.select_folder(id, 'rcmlitasklist'); |
6878 |
+ rcmail.enable_command('list-edit', 'list-remove', 'list-import', me.tasklists[id].editable); |
6879 |
+ me.selected_list = id; |
6880 |
+ }) |
6881 |
+ .dblclick(function(e){ |
6882 |
+ list_edit_dialog($(this).data('id')); |
6883 |
+ }) |
6884 |
+ .data('id', id) |
6885 |
+ .data('type', 'tasklist') |
6886 |
+ .addClass(me.tasklists[id].editable ? null : 'readonly'); |
6887 |
+ } |
6888 |
+ |
6889 |
+ |
6890 |
+ // init dialog by default |
6891 |
+ init_taskedit(); |
6892 |
+} |
6893 |
+ |
6894 |
+ |
6895 |
+// extend jQuery |
6896 |
+// from http://james.padolsey.com/javascript/sorting-elements-with-jquery/ |
6897 |
+jQuery.fn.sortElements = (function(){ |
6898 |
+ var sort = [].sort; |
6899 |
+ |
6900 |
+ return function(comparator, getSortable) { |
6901 |
+ getSortable = getSortable || function(){ return this }; |
6902 |
+ |
6903 |
+ var last = null; |
6904 |
+ return sort.call(this, comparator).each(function(i){ |
6905 |
+ // at this point the array is sorted, so we can just detach each one from wherever it is, and add it after the last |
6906 |
+ var node = $(getSortable.call(this)); |
6907 |
+ var parent = node.parent(); |
6908 |
+ if (last) last.after(node); |
6909 |
+ else parent.prepend(node); |
6910 |
+ last = node; |
6911 |
+ }); |
6912 |
+ }; |
6913 |
+})(); |
6914 |
+ |
6915 |
+ |
6916 |
+/* tasklist plugin UI initialization */ |
6917 |
+var rctasks; |
6918 |
+window.rcmail && rcmail.addEventListener('init', function(evt) { |
6919 |
+ |
6920 |
+ rctasks = new rcube_tasklist_ui(rcmail.env.libcal_settings); |
6921 |
+ |
6922 |
+ // register button commands |
6923 |
+ rcmail.register_command('newtask', function(){ rctasks.edit_task(null, 'new', {}); }, true); |
6924 |
+ //rcmail.register_command('print', function(){ rctasks.print_list(); }, true); |
6925 |
+ |
6926 |
+ rcmail.register_command('list-create', function(){ rctasks.list_edit_dialog(null); }, true); |
6927 |
+ rcmail.register_command('list-edit', function(){ rctasks.list_edit_dialog(rctasks.selected_list); }, false); |
6928 |
+ rcmail.register_command('list-remove', function(){ rctasks.list_remove(rctasks.selected_list); }, false); |
6929 |
+ |
6930 |
+ rcmail.register_command('search', function(){ rctasks.quicksearch(); }, true); |
6931 |
+ rcmail.register_command('reset-search', function(){ rctasks.reset_search(); }, true); |
6932 |
+ |
6933 |
+ rctasks.init(); |
6934 |
+}); |
6935 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist.php roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist.php |
6936 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist.php 1970-01-01 01:00:00.000000000 +0100 |
6937 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist.php 2013-11-24 00:38:51.000000000 +0100 |
6938 |
@@ -0,0 +1,881 @@ |
6939 |
+<?php |
6940 |
+ |
6941 |
+/** |
6942 |
+ * Tasks plugin for Roundcube webmail |
6943 |
+ * |
6944 |
+ * @version @package_version@ |
6945 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
6946 |
+ * |
6947 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
6948 |
+ * |
6949 |
+ * This program is free software: you can redistribute it and/or modify |
6950 |
+ * it under the terms of the GNU Affero General Public License as |
6951 |
+ * published by the Free Software Foundation, either version 3 of the |
6952 |
+ * License, or (at your option) any later version. |
6953 |
+ * |
6954 |
+ * This program is distributed in the hope that it will be useful, |
6955 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
6956 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
6957 |
+ * GNU Affero General Public License for more details. |
6958 |
+ * |
6959 |
+ * You should have received a copy of the GNU Affero General Public License |
6960 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
6961 |
+ */ |
6962 |
+ |
6963 |
+class tasklist extends rcube_plugin |
6964 |
+{ |
6965 |
+ const FILTER_MASK_TODAY = 1; |
6966 |
+ const FILTER_MASK_TOMORROW = 2; |
6967 |
+ const FILTER_MASK_WEEK = 4; |
6968 |
+ const FILTER_MASK_LATER = 8; |
6969 |
+ const FILTER_MASK_NODATE = 16; |
6970 |
+ const FILTER_MASK_OVERDUE = 32; |
6971 |
+ const FILTER_MASK_FLAGGED = 64; |
6972 |
+ const FILTER_MASK_COMPLETE = 128; |
6973 |
+ |
6974 |
+ const SESSION_KEY = 'tasklist_temp'; |
6975 |
+ |
6976 |
+ public static $filter_masks = array( |
6977 |
+ 'today' => self::FILTER_MASK_TODAY, |
6978 |
+ 'tomorrow' => self::FILTER_MASK_TOMORROW, |
6979 |
+ 'week' => self::FILTER_MASK_WEEK, |
6980 |
+ 'later' => self::FILTER_MASK_LATER, |
6981 |
+ 'nodate' => self::FILTER_MASK_NODATE, |
6982 |
+ 'overdue' => self::FILTER_MASK_OVERDUE, |
6983 |
+ 'flagged' => self::FILTER_MASK_FLAGGED, |
6984 |
+ 'complete' => self::FILTER_MASK_COMPLETE, |
6985 |
+ ); |
6986 |
+ |
6987 |
+ public $task = '?(?!login|logout).*'; |
6988 |
+ public $rc; |
6989 |
+ public $lib; |
6990 |
+ public $driver; |
6991 |
+ public $timezone; |
6992 |
+ public $ui; |
6993 |
+ |
6994 |
+ private $collapsed_tasks = array(); |
6995 |
+ |
6996 |
+ |
6997 |
+ /** |
6998 |
+ * Plugin initialization. |
6999 |
+ */ |
7000 |
+ function init() |
7001 |
+ { |
7002 |
+ $this->require_plugin('libcalendaring'); |
7003 |
+ |
7004 |
+ $this->rc = rcube::get_instance(); |
7005 |
+ $this->lib = libcalendaring::get_instance(); |
7006 |
+ |
7007 |
+ $this->register_task('tasks', 'tasklist'); |
7008 |
+ |
7009 |
+ // load plugin configuration |
7010 |
+ $this->load_config(); |
7011 |
+ |
7012 |
+ // load localizations |
7013 |
+ $this->add_texts('localization/', $this->rc->task == 'tasks' && (!$this->rc->action || $this->rc->action == 'print')); |
7014 |
+ $this->rc->load_language($_SESSION['language'], array('tasks.tasks' => $this->gettext('navtitle'))); // add label for task title |
7015 |
+ |
7016 |
+ $this->timezone = $this->lib->timezone; |
7017 |
+ |
7018 |
+ if ($this->rc->task == 'tasks' && $this->rc->action != 'save-pref') { |
7019 |
+ $this->load_driver(); |
7020 |
+ |
7021 |
+ // register calendar actions |
7022 |
+ $this->register_action('index', array($this, 'tasklist_view')); |
7023 |
+ $this->register_action('task', array($this, 'task_action')); |
7024 |
+ $this->register_action('tasklist', array($this, 'tasklist_action')); |
7025 |
+ $this->register_action('counts', array($this, 'fetch_counts')); |
7026 |
+ $this->register_action('fetch', array($this, 'fetch_tasks')); |
7027 |
+ $this->register_action('inlineui', array($this, 'get_inline_ui')); |
7028 |
+ $this->register_action('mail2task', array($this, 'mail_message2task')); |
7029 |
+ $this->register_action('get-attachment', array($this, 'attachment_get')); |
7030 |
+ $this->register_action('upload', array($this, 'attachment_upload')); |
7031 |
+ |
7032 |
+ $this->collapsed_tasks = array_filter(explode(',', $this->rc->config->get('tasklist_collapsed_tasks', ''))); |
7033 |
+ } |
7034 |
+ else if ($this->rc->task == 'mail') { |
7035 |
+ // TODO: register hooks to catch ical/vtodo email attachments |
7036 |
+ if ($this->rc->action == 'show' || $this->rc->action == 'preview') { |
7037 |
+ // $this->add_hook('message_load', array($this, 'mail_message_load')); |
7038 |
+ // $this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html')); |
7039 |
+ } |
7040 |
+ |
7041 |
+ // add 'Create event' item to message menu |
7042 |
+ if ($this->api->output->type == 'html') { |
7043 |
+ $this->api->add_content(html::tag('li', null, |
7044 |
+ $this->api->output->button(array( |
7045 |
+ 'command' => 'tasklist-create-from-mail', |
7046 |
+ 'label' => 'tasklist.createfrommail', |
7047 |
+ 'type' => 'link', |
7048 |
+ 'classact' => 'icon taskaddlink active', |
7049 |
+ 'class' => 'icon taskaddlink', |
7050 |
+ 'innerclass' => 'icon taskadd', |
7051 |
+ ))), |
7052 |
+ 'messagemenu'); |
7053 |
+ } |
7054 |
+ } |
7055 |
+ |
7056 |
+ if (!$this->rc->output->ajax_call && !$this->rc->output->env['framed']) { |
7057 |
+ require_once($this->home . '/tasklist_ui.php'); |
7058 |
+ $this->ui = new tasklist_ui($this); |
7059 |
+ $this->ui->init(); |
7060 |
+ } |
7061 |
+ |
7062 |
+ // add hooks for alarms handling |
7063 |
+ $this->add_hook('pending_alarms', array($this, 'pending_alarms')); |
7064 |
+ $this->add_hook('dismiss_alarms', array($this, 'dismiss_alarms')); |
7065 |
+ } |
7066 |
+ |
7067 |
+ |
7068 |
+ /** |
7069 |
+ * Helper method to load the backend driver according to local config |
7070 |
+ */ |
7071 |
+ private function load_driver() |
7072 |
+ { |
7073 |
+ if (is_object($this->driver)) |
7074 |
+ return; |
7075 |
+ |
7076 |
+ $driver_name = $this->rc->config->get('tasklist_driver', 'database'); |
7077 |
+ $driver_class = 'tasklist_' . $driver_name . '_driver'; |
7078 |
+ |
7079 |
+ require_once($this->home . '/drivers/tasklist_driver.php'); |
7080 |
+ require_once($this->home . '/drivers/' . $driver_name . '/' . $driver_class . '.php'); |
7081 |
+ |
7082 |
+ switch ($driver_name) { |
7083 |
+ case "kolab": |
7084 |
+ $this->require_plugin('libkolab'); |
7085 |
+ default: |
7086 |
+ $this->driver = new $driver_class($this); |
7087 |
+ break; |
7088 |
+ } |
7089 |
+ |
7090 |
+ $this->rc->output->set_env('tasklist_driver', $driver_name); |
7091 |
+ } |
7092 |
+ |
7093 |
+ |
7094 |
+ /** |
7095 |
+ * Dispatcher for task-related actions initiated by the client |
7096 |
+ */ |
7097 |
+ public function task_action() |
7098 |
+ { |
7099 |
+ $filter = intval(get_input_value('filter', RCUBE_INPUT_GPC)); |
7100 |
+ $action = get_input_value('action', RCUBE_INPUT_GPC); |
7101 |
+ $rec = get_input_value('t', RCUBE_INPUT_POST, true); |
7102 |
+ $oldrec = $rec; |
7103 |
+ $success = $refresh = false; |
7104 |
+ |
7105 |
+ switch ($action) { |
7106 |
+ case 'new': |
7107 |
+ $oldrec = null; |
7108 |
+ $rec = $this->prepare_task($rec); |
7109 |
+ $rec['uid'] = $this->generate_uid(); |
7110 |
+ $temp_id = $rec['tempid']; |
7111 |
+ if ($success = $this->driver->create_task($rec)) { |
7112 |
+ $refresh = $this->driver->get_task($rec); |
7113 |
+ if ($temp_id) $refresh['tempid'] = $temp_id; |
7114 |
+ $this->cleanup_task($rec); |
7115 |
+ } |
7116 |
+ break; |
7117 |
+ |
7118 |
+ case 'edit': |
7119 |
+ $rec = $this->prepare_task($rec); |
7120 |
+ if ($success = $this->driver->edit_task($rec)) { |
7121 |
+ $refresh = $this->driver->get_task($rec); |
7122 |
+ $this->cleanup_task($rec); |
7123 |
+ } |
7124 |
+ break; |
7125 |
+ |
7126 |
+ case 'move': |
7127 |
+ foreach ((array)$rec['id'] as $id) { |
7128 |
+ $r = $rec; |
7129 |
+ $r['id'] = $id; |
7130 |
+ if ($this->driver->move_task($r)) { |
7131 |
+ $refresh[] = $this->driver->get_task($r); |
7132 |
+ $success = true; |
7133 |
+ |
7134 |
+ // move all childs, too |
7135 |
+ foreach ($this->driver->get_childs(array('id' => $rec['id'], 'list' => $rec['_fromlist'])) as $cid) { |
7136 |
+ $child = $rec; |
7137 |
+ $child['id'] = $cid; |
7138 |
+ if ($this->driver->move_task($child)) { |
7139 |
+ $r = $this->driver->get_task($child); |
7140 |
+ if ((bool)($filter & self::FILTER_MASK_COMPLETE) == ($r['complete'] == 1.0)) { |
7141 |
+ $refresh[] = $r; |
7142 |
+ } |
7143 |
+ } |
7144 |
+ } |
7145 |
+ } |
7146 |
+ } |
7147 |
+ break; |
7148 |
+ |
7149 |
+ case 'delete': |
7150 |
+ $mode = intval(get_input_value('mode', RCUBE_INPUT_POST)); |
7151 |
+ $oldrec = $this->driver->get_task($rec); |
7152 |
+ if ($success = $this->driver->delete_task($rec, false)) { |
7153 |
+ // delete/modify all childs |
7154 |
+ foreach ($this->driver->get_childs($rec, $mode) as $cid) { |
7155 |
+ $child = array('id' => $cid, 'list' => $rec['list']); |
7156 |
+ |
7157 |
+ if ($mode == 1) { // delete all childs |
7158 |
+ if ($this->driver->delete_task($child, false)) { |
7159 |
+ if ($this->driver->undelete) |
7160 |
+ $_SESSION['tasklist_undelete'][$rec['id']][] = $cid; |
7161 |
+ } |
7162 |
+ else |
7163 |
+ $success = false; |
7164 |
+ } |
7165 |
+ else { |
7166 |
+ $child['parent_id'] = strval($oldrec['parent_id']); |
7167 |
+ $this->driver->edit_task($child); |
7168 |
+ } |
7169 |
+ } |
7170 |
+ } |
7171 |
+ |
7172 |
+ if (!$success) |
7173 |
+ $this->rc->output->command('plugin.reload_data'); |
7174 |
+ break; |
7175 |
+ |
7176 |
+ case 'undelete': |
7177 |
+ if ($success = $this->driver->undelete_task($rec)) { |
7178 |
+ $refresh[] = $this->driver->get_task($rec); |
7179 |
+ foreach ((array)$_SESSION['tasklist_undelete'][$rec['id']] as $cid) { |
7180 |
+ if ($this->driver->undelete_task($rec)) { |
7181 |
+ $refresh[] = $this->driver->get_task($rec); |
7182 |
+ } |
7183 |
+ } |
7184 |
+ } |
7185 |
+ break; |
7186 |
+ |
7187 |
+ case 'collapse': |
7188 |
+ if (intval(get_input_value('collapsed', RCUBE_INPUT_GPC))) { |
7189 |
+ $this->collapsed_tasks[] = $rec['id']; |
7190 |
+ } |
7191 |
+ else { |
7192 |
+ $i = array_search($rec['id'], $this->collapsed_tasks); |
7193 |
+ if ($i !== false) |
7194 |
+ unset($this->collapsed_tasks[$i]); |
7195 |
+ } |
7196 |
+ |
7197 |
+ $this->rc->user->save_prefs(array('tasklist_collapsed_tasks' => join(',', array_unique($this->collapsed_tasks)))); |
7198 |
+ return; // avoid further actions |
7199 |
+ } |
7200 |
+ |
7201 |
+ if ($success) { |
7202 |
+ $this->rc->output->show_message('successfullysaved', 'confirmation'); |
7203 |
+ $this->update_counts($oldrec, $refresh); |
7204 |
+ } |
7205 |
+ else |
7206 |
+ $this->rc->output->show_message('tasklist.errorsaving', 'error'); |
7207 |
+ |
7208 |
+ // unlock client |
7209 |
+ $this->rc->output->command('plugin.unlock_saving'); |
7210 |
+ |
7211 |
+ if ($refresh) { |
7212 |
+ if ($refresh['id']) { |
7213 |
+ $this->encode_task($refresh); |
7214 |
+ } |
7215 |
+ else if (is_array($refresh)) { |
7216 |
+ foreach ($refresh as $i => $r) |
7217 |
+ $this->encode_task($refresh[$i]); |
7218 |
+ } |
7219 |
+ $this->rc->output->command('plugin.refresh_task', $refresh); |
7220 |
+ } |
7221 |
+ } |
7222 |
+ |
7223 |
+ /** |
7224 |
+ * repares new/edited task properties before save |
7225 |
+ */ |
7226 |
+ private function prepare_task($rec) |
7227 |
+ { |
7228 |
+ // try to be smart and extract date from raw input |
7229 |
+ if ($rec['raw']) { |
7230 |
+ foreach (array('today','tomorrow','sunday','monday','tuesday','wednesday','thursday','friday','saturday','sun','mon','tue','wed','thu','fri','sat') as $word) { |
7231 |
+ $locwords[] = '/^' . preg_quote(mb_strtolower($this->gettext($word))) . '\b/i'; |
7232 |
+ $normwords[] = $word; |
7233 |
+ $datewords[] = $word; |
7234 |
+ } |
7235 |
+ foreach (array('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','now','dec') as $month) { |
7236 |
+ $locwords[] = '/(' . preg_quote(mb_strtolower($this->gettext('long'.$month))) . '|' . preg_quote(mb_strtolower($this->gettext($month))) . ')\b/i'; |
7237 |
+ $normwords[] = $month; |
7238 |
+ $datewords[] = $month; |
7239 |
+ } |
7240 |
+ foreach (array('on','this','next','at') as $word) { |
7241 |
+ $fillwords[] = preg_quote(mb_strtolower($this->gettext($word))); |
7242 |
+ $fillwords[] = $word; |
7243 |
+ } |
7244 |
+ |
7245 |
+ $raw = trim($rec['raw']); |
7246 |
+ $date_str = ''; |
7247 |
+ |
7248 |
+ // translate localized keywords |
7249 |
+ $raw = preg_replace('/^(' . join('|', $fillwords) . ')\s*/i', '', $raw); |
7250 |
+ $raw = preg_replace($locwords, $normwords, $raw); |
7251 |
+ |
7252 |
+ // find date pattern |
7253 |
+ $date_pattern = '!^(\d+[./-]\s*)?((?:\d+[./-])|' . join('|', $datewords) . ')\.?(\s+\d{4})?[:;,]?\s+!i'; |
7254 |
+ if (preg_match($date_pattern, $raw, $m)) { |
7255 |
+ $date_str .= $m[1] . $m[2] . $m[3]; |
7256 |
+ $raw = preg_replace(array($date_pattern, '/^(' . join('|', $fillwords) . ')\s*/i'), '', $raw); |
7257 |
+ // add year to date string |
7258 |
+ if ($m[1] && !$m[3]) |
7259 |
+ $date_str .= date('Y'); |
7260 |
+ } |
7261 |
+ |
7262 |
+ // find time pattern |
7263 |
+ $time_pattern = '/^(\d+([:.]\d+)?(\s*[hapm.]+)?),?\s+/i'; |
7264 |
+ if (preg_match($time_pattern, $raw, $m)) { |
7265 |
+ $has_time = true; |
7266 |
+ $date_str .= ($date_str ? ' ' : 'today ') . $m[1]; |
7267 |
+ $raw = preg_replace($time_pattern, '', $raw); |
7268 |
+ } |
7269 |
+ |
7270 |
+ // yes, raw input matched a (valid) date |
7271 |
+ if (strlen($date_str) && strtotime($date_str) && ($date = new DateTime($date_str, $this->timezone))) { |
7272 |
+ $rec['date'] = $date->format('Y-m-d'); |
7273 |
+ if ($has_time) |
7274 |
+ $rec['time'] = $date->format('H:i'); |
7275 |
+ $rec['title'] = $raw; |
7276 |
+ } |
7277 |
+ else |
7278 |
+ $rec['title'] = $rec['raw']; |
7279 |
+ } |
7280 |
+ |
7281 |
+ // normalize input from client |
7282 |
+ if (isset($rec['complete'])) { |
7283 |
+ $rec['complete'] = floatval($rec['complete']); |
7284 |
+ if ($rec['complete'] > 1) |
7285 |
+ $rec['complete'] /= 100; |
7286 |
+ } |
7287 |
+ if (isset($rec['flagged'])) |
7288 |
+ $rec['flagged'] = intval($rec['flagged']); |
7289 |
+ |
7290 |
+ // fix for garbage input |
7291 |
+ if ($rec['description'] == 'null') |
7292 |
+ $rec['description'] = ''; |
7293 |
+ |
7294 |
+ foreach ($rec as $key => $val) { |
7295 |
+ if ($val === 'null') |
7296 |
+ $rec[$key] = null; |
7297 |
+ } |
7298 |
+ |
7299 |
+ if (!empty($rec['date'])) { |
7300 |
+ try { |
7301 |
+ $date = new DateTime($rec['date'] . ' ' . $rec['time'], $this->timezone); |
7302 |
+ $rec['date'] = $date->format('Y-m-d'); |
7303 |
+ if (!empty($rec['time'])) |
7304 |
+ $rec['time'] = $date->format('H:i'); |
7305 |
+ } |
7306 |
+ catch (Exception $e) { |
7307 |
+ $rec['date'] = $rec['time'] = null; |
7308 |
+ } |
7309 |
+ } |
7310 |
+ |
7311 |
+ if (!empty($rec['startdate'])) { |
7312 |
+ try { |
7313 |
+ $date = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone); |
7314 |
+ $rec['startdate'] = $date->format('Y-m-d'); |
7315 |
+ if (!empty($rec['starttime'])) |
7316 |
+ $rec['starttime'] = $date->format('H:i'); |
7317 |
+ } |
7318 |
+ catch (Exception $e) { |
7319 |
+ $rec['startdate'] = $rec['starttime'] = null; |
7320 |
+ } |
7321 |
+ } |
7322 |
+ |
7323 |
+ // alarms cannot work without a date |
7324 |
+ if ($rec['alarms'] && !$rec['date'] && !$rec['startdate'] && strpos($rec['alarms'], '@') === false) |
7325 |
+ $rec['alarms'] = ''; |
7326 |
+ |
7327 |
+ $attachments = array(); |
7328 |
+ $taskid = $rec['id']; |
7329 |
+ if (is_array($_SESSION[self::SESSION_KEY]) && $_SESSION[self::SESSION_KEY]['id'] == $taskid) { |
7330 |
+ if (!empty($_SESSION[self::SESSION_KEY]['attachments'])) { |
7331 |
+ foreach ($_SESSION[self::SESSION_KEY]['attachments'] as $id => $attachment) { |
7332 |
+ if (is_array($rec['attachments']) && in_array($id, $rec['attachments'])) { |
7333 |
+ $attachments[$id] = $this->rc->plugins->exec_hook('attachment_get', $attachment); |
7334 |
+ unset($attachments[$id]['abort'], $attachments[$id]['group']); |
7335 |
+ } |
7336 |
+ } |
7337 |
+ } |
7338 |
+ } |
7339 |
+ |
7340 |
+ $rec['attachments'] = $attachments; |
7341 |
+ |
7342 |
+ if (is_numeric($rec['id']) && $rec['id'] < 0) |
7343 |
+ unset($rec['id']); |
7344 |
+ |
7345 |
+ return $rec; |
7346 |
+ } |
7347 |
+ |
7348 |
+ |
7349 |
+ /** |
7350 |
+ * Releases some resources after successful save |
7351 |
+ */ |
7352 |
+ private function cleanup_task(&$rec) |
7353 |
+ { |
7354 |
+ // remove temp. attachment files |
7355 |
+ if (!empty($_SESSION[self::SESSION_KEY]) && ($taskid = $_SESSION[self::SESSION_KEY]['id'])) { |
7356 |
+ $this->rc->plugins->exec_hook('attachments_cleanup', array('group' => $taskid)); |
7357 |
+ $this->rc->session->remove(self::SESSION_KEY); |
7358 |
+ } |
7359 |
+ } |
7360 |
+ |
7361 |
+ |
7362 |
+ /** |
7363 |
+ * Dispatcher for tasklist actions initiated by the client |
7364 |
+ */ |
7365 |
+ public function tasklist_action() |
7366 |
+ { |
7367 |
+ $action = get_input_value('action', RCUBE_INPUT_GPC); |
7368 |
+ $list = get_input_value('l', RCUBE_INPUT_POST, true); |
7369 |
+ $success = false; |
7370 |
+ |
7371 |
+ if (isset($list['showalarms'])) |
7372 |
+ $list['showalarms'] = intval($list['showalarms']); |
7373 |
+ |
7374 |
+ switch ($action) { |
7375 |
+ case 'new': |
7376 |
+ $list += array('showalarms' => true, 'active' => true, 'editable' => true); |
7377 |
+ if ($insert_id = $this->driver->create_list($list)) { |
7378 |
+ $list['id'] = $insert_id; |
7379 |
+ $this->rc->output->command('plugin.insert_tasklist', $list); |
7380 |
+ $success = true; |
7381 |
+ } |
7382 |
+ break; |
7383 |
+ |
7384 |
+ case 'edit': |
7385 |
+ if ($newid = $this->driver->edit_list($list)) { |
7386 |
+ $list['oldid'] = $list['id']; |
7387 |
+ $list['id'] = $newid; |
7388 |
+ $this->rc->output->command('plugin.update_tasklist', $list); |
7389 |
+ $success = true; |
7390 |
+ } |
7391 |
+ break; |
7392 |
+ |
7393 |
+ case 'subscribe': |
7394 |
+ $success = $this->driver->subscribe_list($list); |
7395 |
+ break; |
7396 |
+ |
7397 |
+ case 'remove': |
7398 |
+ if (($success = $this->driver->remove_list($list))) |
7399 |
+ $this->rc->output->command('plugin.destroy_tasklist', $list); |
7400 |
+ break; |
7401 |
+ } |
7402 |
+ |
7403 |
+ if ($success) |
7404 |
+ $this->rc->output->show_message('successfullysaved', 'confirmation'); |
7405 |
+ else |
7406 |
+ $this->rc->output->show_message('tasklist.errorsaving', 'error'); |
7407 |
+ |
7408 |
+ $this->rc->output->command('plugin.unlock_saving'); |
7409 |
+ } |
7410 |
+ |
7411 |
+ /** |
7412 |
+ * Get counts for active tasks divided into different selectors |
7413 |
+ */ |
7414 |
+ public function fetch_counts() |
7415 |
+ { |
7416 |
+ if (isset($_REQUEST['lists'])) { |
7417 |
+ $lists = get_input_value('lists', RCUBE_INPUT_GPC); |
7418 |
+ } |
7419 |
+ else { |
7420 |
+ foreach ($this->driver->get_lists() as $list) { |
7421 |
+ if ($list['active']) |
7422 |
+ $lists[] = $list['id']; |
7423 |
+ } |
7424 |
+ } |
7425 |
+ $counts = $this->driver->count_tasks($lists); |
7426 |
+ $this->rc->output->command('plugin.update_counts', $counts); |
7427 |
+ } |
7428 |
+ |
7429 |
+ /** |
7430 |
+ * Adjust the cached counts after changing a task |
7431 |
+ */ |
7432 |
+ public function update_counts($oldrec, $newrec) |
7433 |
+ { |
7434 |
+ // rebuild counts until this function is finally implemented |
7435 |
+ $this->fetch_counts(); |
7436 |
+ |
7437 |
+ // $this->rc->output->command('plugin.update_counts', $counts); |
7438 |
+ } |
7439 |
+ |
7440 |
+ /** |
7441 |
+ * |
7442 |
+ */ |
7443 |
+ public function fetch_tasks() |
7444 |
+ { |
7445 |
+ $f = intval(get_input_value('filter', RCUBE_INPUT_GPC)); |
7446 |
+ $search = get_input_value('q', RCUBE_INPUT_GPC); |
7447 |
+ $filter = array('mask' => $f, 'search' => $search); |
7448 |
+ $lists = get_input_value('lists', RCUBE_INPUT_GPC);; |
7449 |
+/* |
7450 |
+ // convert magic date filters into a real date range |
7451 |
+ switch ($f) { |
7452 |
+ case self::FILTER_MASK_TODAY: |
7453 |
+ $today = new DateTime('now', $this->timezone); |
7454 |
+ $filter['from'] = $filter['to'] = $today->format('Y-m-d'); |
7455 |
+ break; |
7456 |
+ |
7457 |
+ case self::FILTER_MASK_TOMORROW: |
7458 |
+ $tomorrow = new DateTime('now + 1 day', $this->timezone); |
7459 |
+ $filter['from'] = $filter['to'] = $tomorrow->format('Y-m-d'); |
7460 |
+ break; |
7461 |
+ |
7462 |
+ case self::FILTER_MASK_OVERDUE: |
7463 |
+ $yesterday = new DateTime('yesterday', $this->timezone); |
7464 |
+ $filter['to'] = $yesterday->format('Y-m-d'); |
7465 |
+ break; |
7466 |
+ |
7467 |
+ case self::FILTER_MASK_WEEK: |
7468 |
+ $today = new DateTime('now', $this->timezone); |
7469 |
+ $filter['from'] = $today->format('Y-m-d'); |
7470 |
+ $weekend = new DateTime('now + 7 days', $this->timezone); |
7471 |
+ $filter['to'] = $weekend->format('Y-m-d'); |
7472 |
+ break; |
7473 |
+ |
7474 |
+ case self::FILTER_MASK_LATER: |
7475 |
+ $date = new DateTime('now + 8 days', $this->timezone); |
7476 |
+ $filter['from'] = $date->format('Y-m-d'); |
7477 |
+ break; |
7478 |
+ |
7479 |
+ } |
7480 |
+*/ |
7481 |
+ $data = $tags = $this->task_tree = $this->task_titles = array(); |
7482 |
+ foreach ($this->driver->list_tasks($filter, $lists) as $rec) { |
7483 |
+ if ($rec['parent_id']) { |
7484 |
+ $this->task_tree[$rec['id']] = $rec['parent_id']; |
7485 |
+ } |
7486 |
+ $this->encode_task($rec); |
7487 |
+ if (!empty($rec['tags'])) |
7488 |
+ $tags = array_merge($tags, (array)$rec['tags']); |
7489 |
+ |
7490 |
+ // apply filter; don't trust the driver on this :-) |
7491 |
+ if ((!$f && $rec['complete'] < 1.0) || ($rec['mask'] & $f)) |
7492 |
+ $data[] = $rec; |
7493 |
+ } |
7494 |
+ |
7495 |
+ // sort tasks according to their hierarchy level and due date |
7496 |
+ array_walk($data, array($this, 'task_walk_tree')); |
7497 |
+ usort($data, array($this, 'task_sort_cmp')); |
7498 |
+ |
7499 |
+ $this->rc->output->command('plugin.data_ready', array('filter' => $f, 'lists' => $lists, 'search' => $search, 'data' => $data, 'tags' => array_values(array_unique($tags)))); |
7500 |
+ } |
7501 |
+ |
7502 |
+ /** |
7503 |
+ * Prepare the given task record before sending it to the client |
7504 |
+ */ |
7505 |
+ private function encode_task(&$rec) |
7506 |
+ { |
7507 |
+ $rec['mask'] = $this->filter_mask($rec); |
7508 |
+ $rec['flagged'] = intval($rec['flagged']); |
7509 |
+ $rec['complete'] = floatval($rec['complete']); |
7510 |
+ $rec['changed'] = is_object($rec['changed']) ? $rec['changed']->format('U') : null; |
7511 |
+ |
7512 |
+ if ($rec['date']) { |
7513 |
+ try { |
7514 |
+ $date = new DateTime($rec['date'] . ' ' . $rec['time'], $this->timezone); |
7515 |
+ $rec['datetime'] = intval($date->format('U')); |
7516 |
+ $rec['date'] = $date->format($this->rc->config->get('date_format', 'Y-m-d')); |
7517 |
+ $rec['_hasdate'] = 1; |
7518 |
+ } |
7519 |
+ catch (Exception $e) { |
7520 |
+ $rec['date'] = $rec['datetime'] = null; |
7521 |
+ } |
7522 |
+ } |
7523 |
+ else { |
7524 |
+ $rec['date'] = $rec['datetime'] = null; |
7525 |
+ $rec['_hasdate'] = 0; |
7526 |
+ } |
7527 |
+ |
7528 |
+ if ($rec['startdate']) { |
7529 |
+ try { |
7530 |
+ $date = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone); |
7531 |
+ $rec['startdatetime'] = intval($date->format('U')); |
7532 |
+ $rec['startdate'] = $date->format($this->rc->config->get('date_format', 'Y-m-d')); |
7533 |
+ } |
7534 |
+ catch (Exception $e) { |
7535 |
+ $rec['startdate'] = $rec['startdatetime'] = null; |
7536 |
+ } |
7537 |
+ } |
7538 |
+ |
7539 |
+ if ($rec['alarms']) |
7540 |
+ $rec['alarms_text'] = libcalendaring::alarms_text($rec['alarms']); |
7541 |
+ |
7542 |
+ foreach ((array)$rec['attachments'] as $k => $attachment) { |
7543 |
+ $rec['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']); |
7544 |
+ } |
7545 |
+ |
7546 |
+ if (in_array($rec['id'], $this->collapsed_tasks)) |
7547 |
+ $rec['collapsed'] = true; |
7548 |
+ |
7549 |
+ $this->task_titles[$rec['id']] = $rec['title']; |
7550 |
+ } |
7551 |
+ |
7552 |
+ /** |
7553 |
+ * Callback function for array_walk over all tasks. |
7554 |
+ * Sets tree depth and parent titles |
7555 |
+ */ |
7556 |
+ private function task_walk_tree(&$rec) |
7557 |
+ { |
7558 |
+ $rec['_depth'] = 0; |
7559 |
+ $parent_id = $this->task_tree[$rec['id']]; |
7560 |
+ while ($parent_id) { |
7561 |
+ $rec['_depth']++; |
7562 |
+ $rec['parent_title'] = $this->task_titles[$parent_id]; |
7563 |
+ $parent_id = $this->task_tree[$parent_id]; |
7564 |
+ } |
7565 |
+ } |
7566 |
+ |
7567 |
+ /** |
7568 |
+ * Compare function for task list sorting. |
7569 |
+ * Nested tasks need to be sorted to the end. |
7570 |
+ */ |
7571 |
+ private function task_sort_cmp($a, $b) |
7572 |
+ { |
7573 |
+ $d = $a['_depth'] - $b['_depth']; |
7574 |
+ if (!$d) $d = $b['_hasdate'] - $a['_hasdate']; |
7575 |
+ if (!$d) $d = $a['datetime'] - $b['datetime']; |
7576 |
+ return $d; |
7577 |
+ } |
7578 |
+ |
7579 |
+ /** |
7580 |
+ * Compute the filter mask of the given task |
7581 |
+ * |
7582 |
+ * @param array Hash array with Task record properties |
7583 |
+ * @return int Filter mask |
7584 |
+ */ |
7585 |
+ public function filter_mask($rec) |
7586 |
+ { |
7587 |
+ static $today, $tomorrow, $weeklimit; |
7588 |
+ |
7589 |
+ if (!$today) { |
7590 |
+ $today_date = new DateTime('now', $this->timezone); |
7591 |
+ $today = $today_date->format('Y-m-d'); |
7592 |
+ $tomorrow_date = new DateTime('now + 1 day', $this->timezone); |
7593 |
+ $tomorrow = $tomorrow_date->format('Y-m-d'); |
7594 |
+ $week_date = new DateTime('now + 7 days', $this->timezone); |
7595 |
+ $weeklimit = $week_date->format('Y-m-d'); |
7596 |
+ } |
7597 |
+ |
7598 |
+ $mask = 0; |
7599 |
+ $start = $rec['startdate'] ?: '1900-00-00'; |
7600 |
+ $duedate = $rec['date'] ?: '3000-00-00'; |
7601 |
+ |
7602 |
+ if ($rec['flagged']) |
7603 |
+ $mask |= self::FILTER_MASK_FLAGGED; |
7604 |
+ if ($rec['complete'] == 1.0) |
7605 |
+ $mask |= self::FILTER_MASK_COMPLETE; |
7606 |
+ |
7607 |
+ if (empty($rec['date'])) |
7608 |
+ $mask |= self::FILTER_MASK_NODATE; |
7609 |
+ else if ($rec['date'] < $today) |
7610 |
+ $mask |= self::FILTER_MASK_OVERDUE; |
7611 |
+ |
7612 |
+ if ($duedate <= $today || ($rec['startdate'] && $start <= $today)) |
7613 |
+ $mask |= self::FILTER_MASK_TODAY; |
7614 |
+ if ($duedate <= $tomorrow || ($rec['startdate'] && $start <= $tomorrow)) |
7615 |
+ $mask |= self::FILTER_MASK_TOMORROW; |
7616 |
+ if ($start > $tomorrow || ($duedate > $tomorrow && $duedate <= $weeklimit)) |
7617 |
+ $mask |= self::FILTER_MASK_WEEK; |
7618 |
+ if ($start > $weeklimit || ($rec['date'] && $duedate > $weeklimit)) |
7619 |
+ $mask |= self::FILTER_MASK_LATER; |
7620 |
+ |
7621 |
+ return $mask; |
7622 |
+ } |
7623 |
+ |
7624 |
+ |
7625 |
+ /******* UI functions ********/ |
7626 |
+ |
7627 |
+ /** |
7628 |
+ * Render main view of the tasklist task |
7629 |
+ */ |
7630 |
+ public function tasklist_view() |
7631 |
+ { |
7632 |
+ $this->ui->init(); |
7633 |
+ $this->ui->init_templates(); |
7634 |
+ $this->rc->output->set_pagetitle($this->gettext('navtitle')); |
7635 |
+ $this->rc->output->send('tasklist.mainview'); |
7636 |
+ } |
7637 |
+ |
7638 |
+ |
7639 |
+ /** |
7640 |
+ * |
7641 |
+ */ |
7642 |
+ public function get_inline_ui() |
7643 |
+ { |
7644 |
+ foreach (array('save','cancel','savingdata') as $label) |
7645 |
+ $texts['tasklist.'.$label] = $this->gettext($label); |
7646 |
+ |
7647 |
+ $texts['tasklist.newtask'] = $this->gettext('createfrommail'); |
7648 |
+ |
7649 |
+ $this->ui->init_templates(); |
7650 |
+ echo $this->api->output->parse('tasklist.taskedit', false, false); |
7651 |
+ echo html::tag('script', array('type' => 'text/javascript'), |
7652 |
+ "rcmail.set_env('tasklists', " . json_encode($this->api->output->env['tasklists']) . ");\n". |
7653 |
+// "rcmail.set_env('deleteicon', '" . $this->api->output->env['deleteicon'] . "');\n". |
7654 |
+// "rcmail.set_env('cancelicon', '" . $this->api->output->env['cancelicon'] . "');\n". |
7655 |
+// "rcmail.set_env('loadingicon', '" . $this->api->output->env['loadingicon'] . "');\n". |
7656 |
+ "rcmail.add_label(" . json_encode($texts) . ");\n" |
7657 |
+ ); |
7658 |
+ exit; |
7659 |
+ } |
7660 |
+ |
7661 |
+ |
7662 |
+ /** |
7663 |
+ * Handler for pending_alarms plugin hook triggered by the calendar module on keep-alive requests. |
7664 |
+ * This will check for pending notifications and pass them to the client |
7665 |
+ */ |
7666 |
+ public function pending_alarms($p) |
7667 |
+ { |
7668 |
+ $this->load_driver(); |
7669 |
+ if ($alarms = $this->driver->pending_alarms($p['time'] ?: time())) { |
7670 |
+ foreach ($alarms as $alarm) { |
7671 |
+ // encode alarm object to suit the expectations of the calendaring code |
7672 |
+ if ($alarm['date']) |
7673 |
+ $alarm['start'] = new DateTime($alarm['date'].' '.$alarm['time'], $this->timezone); |
7674 |
+ |
7675 |
+ $alarm['id'] = 'task:' . $alarm['id']; // prefix ID with task: |
7676 |
+ $alarm['allday'] = empty($alarm['time']) ? 1 : 0; |
7677 |
+ $p['alarms'][] = $alarm; |
7678 |
+ } |
7679 |
+ } |
7680 |
+ |
7681 |
+ return $p; |
7682 |
+ } |
7683 |
+ |
7684 |
+ /** |
7685 |
+ * Handler for alarm dismiss hook triggered by the calendar module |
7686 |
+ */ |
7687 |
+ public function dismiss_alarms($p) |
7688 |
+ { |
7689 |
+ $this->load_driver(); |
7690 |
+ foreach ((array)$p['ids'] as $id) { |
7691 |
+ if (strpos($id, 'task:') === 0) |
7692 |
+ $p['success'] |= $this->driver->dismiss_alarm(substr($id, 5), $p['snooze']); |
7693 |
+ } |
7694 |
+ |
7695 |
+ return $p; |
7696 |
+ } |
7697 |
+ |
7698 |
+ |
7699 |
+ /******* Attachment handling *******/ |
7700 |
+ |
7701 |
+ /** |
7702 |
+ * Handler for attachments upload |
7703 |
+ */ |
7704 |
+ public function attachment_upload() |
7705 |
+ { |
7706 |
+ $this->lib->attachment_upload(self::SESSION_KEY); |
7707 |
+ } |
7708 |
+ |
7709 |
+ /** |
7710 |
+ * Handler for attachments download/displaying |
7711 |
+ */ |
7712 |
+ public function attachment_get() |
7713 |
+ { |
7714 |
+ // show loading page |
7715 |
+ if (!empty($_GET['_preload'])) { |
7716 |
+ return $this->lib->attachment_loading_page(); |
7717 |
+ } |
7718 |
+ |
7719 |
+ $task = get_input_value('_t', RCUBE_INPUT_GPC); |
7720 |
+ $list = get_input_value('_list', RCUBE_INPUT_GPC); |
7721 |
+ $id = get_input_value('_id', RCUBE_INPUT_GPC); |
7722 |
+ |
7723 |
+ $task = array('id' => $task, 'list' => $list); |
7724 |
+ $attachment = $this->driver->get_attachment($id, $task); |
7725 |
+ |
7726 |
+ // show part page |
7727 |
+ if (!empty($_GET['_frame'])) { |
7728 |
+ $this->lib->attachment = $attachment; |
7729 |
+ $this->register_handler('plugin.attachmentframe', array($this->lib, 'attachment_frame')); |
7730 |
+ $this->register_handler('plugin.attachmentcontrols', array($this->lib, 'attachment_header')); |
7731 |
+ $this->rc->output->send('tasklist.attachment'); |
7732 |
+ } |
7733 |
+ // deliver attachment content |
7734 |
+ else if ($attachment) { |
7735 |
+ $attachment['body'] = $this->driver->get_attachment_body($id, $task); |
7736 |
+ $this->lib->attachment_get($attachment); |
7737 |
+ } |
7738 |
+ |
7739 |
+ // if we arrive here, the requested part was not found |
7740 |
+ header('HTTP/1.1 404 Not Found'); |
7741 |
+ exit; |
7742 |
+ } |
7743 |
+ |
7744 |
+ |
7745 |
+ /******* Email related function *******/ |
7746 |
+ |
7747 |
+ public function mail_message2task() |
7748 |
+ { |
7749 |
+ $uid = get_input_value('_uid', RCUBE_INPUT_POST); |
7750 |
+ $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); |
7751 |
+ $task = array(); |
7752 |
+ |
7753 |
+ // establish imap connection |
7754 |
+ $imap = $this->rc->get_storage(); |
7755 |
+ $imap->set_mailbox($mbox); |
7756 |
+ $message = new rcube_message($uid); |
7757 |
+ |
7758 |
+ if ($message->headers) { |
7759 |
+ $task['title'] = trim($message->subject); |
7760 |
+ $task['description'] = trim($message->first_text_part()); |
7761 |
+ $task['id'] = -$uid; |
7762 |
+ |
7763 |
+ $this->load_driver(); |
7764 |
+ |
7765 |
+ // copy mail attachments to task |
7766 |
+ if ($message->attachments && $this->driver->attachments) { |
7767 |
+ if (!is_array($_SESSION[self::SESSION_KEY]) || $_SESSION[self::SESSION_KEY]['id'] != $task['id']) { |
7768 |
+ $_SESSION[self::SESSION_KEY] = array(); |
7769 |
+ $_SESSION[self::SESSION_KEY]['id'] = $task['id']; |
7770 |
+ $_SESSION[self::SESSION_KEY]['attachments'] = array(); |
7771 |
+ } |
7772 |
+ |
7773 |
+ foreach ((array)$message->attachments as $part) { |
7774 |
+ $attachment = array( |
7775 |
+ 'data' => $imap->get_message_part($uid, $part->mime_id, $part), |
7776 |
+ 'size' => $part->size, |
7777 |
+ 'name' => $part->filename, |
7778 |
+ 'mimetype' => $part->mimetype, |
7779 |
+ 'group' => $task['id'], |
7780 |
+ ); |
7781 |
+ |
7782 |
+ $attachment = $this->rc->plugins->exec_hook('attachment_save', $attachment); |
7783 |
+ |
7784 |
+ if ($attachment['status'] && !$attachment['abort']) { |
7785 |
+ $id = $attachment['id']; |
7786 |
+ $attachment['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']); |
7787 |
+ |
7788 |
+ // store new attachment in session |
7789 |
+ unset($attachment['status'], $attachment['abort'], $attachment['data']); |
7790 |
+ $_SESSION[self::SESSION_KEY]['attachments'][$id] = $attachment; |
7791 |
+ |
7792 |
+ $attachment['id'] = 'rcmfile' . $attachment['id']; // add prefix to consider it 'new' |
7793 |
+ $task['attachments'][] = $attachment; |
7794 |
+ } |
7795 |
+ } |
7796 |
+ } |
7797 |
+ |
7798 |
+ $this->rc->output->command('plugin.mail2taskdialog', $task); |
7799 |
+ } |
7800 |
+ else { |
7801 |
+ $this->rc->output->command('display_message', $this->gettext('messageopenerror'), 'error'); |
7802 |
+ } |
7803 |
+ |
7804 |
+ $this->rc->output->send(); |
7805 |
+ } |
7806 |
+ |
7807 |
+ |
7808 |
+ /******* Utility functions *******/ |
7809 |
+ |
7810 |
+ /** |
7811 |
+ * Generate a unique identifier for an event |
7812 |
+ */ |
7813 |
+ public function generate_uid() |
7814 |
+ { |
7815 |
+ return strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16)); |
7816 |
+ } |
7817 |
+ |
7818 |
+} |
7819 |
+ |
7820 |
diff -Nur roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist_ui.php roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist_ui.php |
7821 |
--- roundcube-0.9.5-old/root/opt/roundcube/plugins/tasklist/tasklist_ui.php 1970-01-01 01:00:00.000000000 +0100 |
7822 |
+++ roundcube-0.9.5/root/opt/roundcube/plugins/tasklist/tasklist_ui.php 2013-11-24 00:38:51.000000000 +0100 |
7823 |
@@ -0,0 +1,282 @@ |
7824 |
+<?php |
7825 |
+/** |
7826 |
+ * User Interface class for the Tasklist plugin |
7827 |
+ * |
7828 |
+ * @version @package_version@ |
7829 |
+ * @author Thomas Bruederli <bruederli@kolabsys.com> |
7830 |
+ * |
7831 |
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> |
7832 |
+ * |
7833 |
+ * This program is free software: you can redistribute it and/or modify |
7834 |
+ * it under the terms of the GNU Affero General Public License as |
7835 |
+ * published by the Free Software Foundation, either version 3 of the |
7836 |
+ * License, or (at your option) any later version. |
7837 |
+ * |
7838 |
+ * This program is distributed in the hope that it will be useful, |
7839 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
7840 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
7841 |
+ * GNU Affero General Public License for more details. |
7842 |
+ * |
7843 |
+ * You should have received a copy of the GNU Affero General Public License |
7844 |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. |
7845 |
+ */ |
7846 |
+ |
7847 |
+ |
7848 |
+class tasklist_ui |
7849 |
+{ |
7850 |
+ private $rc; |
7851 |
+ private $plugin; |
7852 |
+ private $ready = false; |
7853 |
+ |
7854 |
+ function __construct($plugin) |
7855 |
+ { |
7856 |
+ $this->plugin = $plugin; |
7857 |
+ $this->rc = $plugin->rc; |
7858 |
+ } |
7859 |
+ |
7860 |
+ /** |
7861 |
+ * Calendar UI initialization and requests handlers |
7862 |
+ */ |
7863 |
+ public function init() |
7864 |
+ { |
7865 |
+ if ($this->ready) // already done |
7866 |
+ return; |
7867 |
+ |
7868 |
+ // add taskbar button |
7869 |
+ $this->plugin->add_button(array( |
7870 |
+ 'command' => 'tasks', |
7871 |
+ 'class' => 'button-tasklist', |
7872 |
+ 'classsel' => 'button-tasklist button-selected', |
7873 |
+ 'innerclass' => 'button-inner', |
7874 |
+ 'label' => 'tasklist.navtitle', |
7875 |
+ ), 'taskbar'); |
7876 |
+ |
7877 |
+ $this->plugin->include_stylesheet($this->plugin->local_skin_path() . '/tasklist.css'); |
7878 |
+ $this->plugin->include_script('tasklist_base.js'); |
7879 |
+ |
7880 |
+ // copy config to client |
7881 |
+ // $this->rc->output->set_env('tasklist_settings', $settings); |
7882 |
+ |
7883 |
+ $this->ready = true; |
7884 |
+ } |
7885 |
+ |
7886 |
+ /** |
7887 |
+ * Register handler methods for the template engine |
7888 |
+ */ |
7889 |
+ public function init_templates() |
7890 |
+ { |
7891 |
+ $this->plugin->register_handler('plugin.tasklists', array($this, 'tasklists')); |
7892 |
+ $this->plugin->register_handler('plugin.tasklist_select', array($this, 'tasklist_select')); |
7893 |
+ $this->plugin->register_handler('plugin.category_select', array($this, 'category_select')); |
7894 |
+ $this->plugin->register_handler('plugin.searchform', array($this->rc->output, 'search_form')); |
7895 |
+ $this->plugin->register_handler('plugin.quickaddform', array($this, 'quickadd_form')); |
7896 |
+ $this->plugin->register_handler('plugin.tasklist_editform', array($this, 'tasklist_editform')); |
7897 |
+ $this->plugin->register_handler('plugin.tasks', array($this, 'tasks_resultview')); |
7898 |
+ $this->plugin->register_handler('plugin.tagslist', array($this, 'tagslist')); |
7899 |
+ $this->plugin->register_handler('plugin.tags_editline', array($this, 'tags_editline')); |
7900 |
+ $this->plugin->register_handler('plugin.alarm_select', array($this, 'alarm_select')); |
7901 |
+ $this->plugin->register_handler('plugin.attachments_form', array($this, 'attachments_form')); |
7902 |
+ $this->plugin->register_handler('plugin.attachments_list', array($this, 'attachments_list')); |
7903 |
+ $this->plugin->register_handler('plugin.filedroparea', array($this, 'file_drop_area')); |
7904 |
+ |
7905 |
+ $this->plugin->include_script('jquery.tagedit.js'); |
7906 |
+ $this->plugin->include_script('tasklist.js'); |
7907 |
+ } |
7908 |
+ |
7909 |
+ /** |
7910 |
+ * |
7911 |
+ */ |
7912 |
+ function tasklists($attrib = array()) |
7913 |
+ { |
7914 |
+ $lists = $this->plugin->driver->get_lists(); |
7915 |
+ |
7916 |
+ $li = ''; |
7917 |
+ foreach ((array)$lists as $id => $prop) { |
7918 |
+ if ($attrib['activeonly'] && !$prop['active']) |
7919 |
+ continue; |
7920 |
+ |
7921 |
+ unset($prop['user_id']); |
7922 |
+ $prop['alarms'] = $this->plugin->driver->alarms; |
7923 |
+ $prop['undelete'] = $this->plugin->driver->undelete; |
7924 |
+ $prop['sortable'] = $this->plugin->driver->sortable; |
7925 |
+ $prop['attachments'] = $this->plugin->driver->attachments; |
7926 |
+ $jsenv[$id] = $prop; |
7927 |
+ |
7928 |
+ $html_id = html_identifier($id); |
7929 |
+ $class = 'tasks-' . asciiwords($id, true); |
7930 |
+ |
7931 |
+ if (!$prop['editable']) |
7932 |
+ $class .= ' readonly'; |
7933 |
+ if ($prop['class_name']) |
7934 |
+ $class .= ' '.$prop['class_name']; |
7935 |
+ |
7936 |
+ $li .= html::tag('li', array('id' => 'rcmlitasklist' . $html_id, 'class' => $class), |
7937 |
+ html::tag('input', array('type' => 'checkbox', 'name' => '_list[]', 'value' => $id, 'checked' => $prop['active'])) . |
7938 |
+ html::span('handle', ' ') . |
7939 |
+ html::span('listname', $prop['name'])); |
7940 |
+ } |
7941 |
+ |
7942 |
+ $this->rc->output->set_env('tasklists', $jsenv); |
7943 |
+ $this->rc->output->add_gui_object('folderlist', $attrib['id']); |
7944 |
+ |
7945 |
+ return html::tag('ul', $attrib, $li, html::$common_attrib); |
7946 |
+ } |
7947 |
+ |
7948 |
+ |
7949 |
+ /** |
7950 |
+ * Render a HTML select box for list selection |
7951 |
+ */ |
7952 |
+ function tasklist_select($attrib = array()) |
7953 |
+ { |
7954 |
+ $attrib['name'] = 'list'; |
7955 |
+ $attrib['is_escaped'] = true; |
7956 |
+ $select = new html_select($attrib); |
7957 |
+ |
7958 |
+ foreach ((array)$this->plugin->driver->get_lists() as $id => $prop) { |
7959 |
+ if ($prop['editable']) |
7960 |
+ $select->add($prop['name'], $id); |
7961 |
+ } |
7962 |
+ |
7963 |
+ return $select->show(null); |
7964 |
+ } |
7965 |
+ |
7966 |
+ |
7967 |
+ function tasklist_editform($attrib = array()) |
7968 |
+ { |
7969 |
+ $fields = array( |
7970 |
+ 'name' => array( |
7971 |
+ 'id' => 'taskedit-tasklistame', |
7972 |
+ 'label' => $this->plugin->gettext('listname'), |
7973 |
+ 'value' => html::tag('input', array('id' => 'taskedit-tasklistame', 'name' => 'name', 'type' => 'text', 'class' => 'text', 'size' => 40)), |
7974 |
+ ), |
7975 |
+/* |
7976 |
+ 'color' => array( |
7977 |
+ 'id' => 'taskedit-color', |
7978 |
+ 'label' => $this->plugin->gettext('color'), |
7979 |
+ 'value' => html::tag('input', array('id' => 'taskedit-color', 'name' => 'color', 'type' => 'text', 'class' => 'text colorpicker', 'size' => 6)), |
7980 |
+ ), |
7981 |
+*/ |
7982 |
+ 'showalarms' => array( |
7983 |
+ 'id' => 'taskedit-showalarms', |
7984 |
+ 'label' => $this->plugin->gettext('showalarms'), |
7985 |
+ 'value' => html::tag('input', array('id' => 'taskedit-showalarms', 'name' => 'color', 'type' => 'checkbox')), |
7986 |
+ ), |
7987 |
+ ); |
7988 |
+ |
7989 |
+ return html::tag('form', array('action' => "#", 'method' => "post", 'id' => 'tasklisteditform'), |
7990 |
+ $this->plugin->driver->tasklist_edit_form($fields) |
7991 |
+ ); |
7992 |
+ } |
7993 |
+ |
7994 |
+ /** |
7995 |
+ * Render HTML form for alarm configuration |
7996 |
+ */ |
7997 |
+ function alarm_select($attrib = array()) |
7998 |
+ { |
7999 |
+ return $this->plugin->lib->alarm_select($attrib, $this->plugin->driver->alarm_types, $this->plugin->driver->alarm_absolute); |
8000 |
+ } |
8001 |
+ |
8002 |
+ /** |
8003 |
+ * |
8004 |
+ */ |
8005 |
+ function quickadd_form($attrib) |
8006 |
+ { |
8007 |
+ $attrib += array('action' => $this->rc->url('add'), 'method' => 'post', 'id' => 'quickaddform'); |
8008 |
+ |
8009 |
+ $input = new html_inputfield(array('name' => 'text', 'id' => 'quickaddinput')); |
8010 |
+ $button = html::tag('input', array('type' => 'submit', 'value' => '+', 'class' => 'button mainaction')); |
8011 |
+ |
8012 |
+ $this->rc->output->add_gui_object('quickaddform', $attrib['id']); |
8013 |
+ return html::tag('form', $attrib, $input->show() . $button); |
8014 |
+ } |
8015 |
+ |
8016 |
+ /** |
8017 |
+ * The result view |
8018 |
+ */ |
8019 |
+ function tasks_resultview($attrib) |
8020 |
+ { |
8021 |
+ $attrib += array('id' => 'rcmtaskslist'); |
8022 |
+ |
8023 |
+ $this->rc->output->add_gui_object('resultlist', $attrib['id']); |
8024 |
+ |
8025 |
+ unset($attrib['name']); |
8026 |
+ return html::tag('ul', $attrib, ''); |
8027 |
+ } |
8028 |
+ |
8029 |
+ /** |
8030 |
+ * Container for a tags cloud |
8031 |
+ */ |
8032 |
+ function tagslist($attrib) |
8033 |
+ { |
8034 |
+ $attrib += array('id' => 'rcmtasktagslist'); |
8035 |
+ unset($attrib['name']); |
8036 |
+ |
8037 |
+ $this->rc->output->add_gui_object('tagslist', $attrib['id']); |
8038 |
+ return html::tag('ul', $attrib, ''); |
8039 |
+ } |
8040 |
+ |
8041 |
+ /** |
8042 |
+ * Interactive UI element to add/remove tags |
8043 |
+ */ |
8044 |
+ function tags_editline($attrib) |
8045 |
+ { |
8046 |
+ $attrib += array('id' => 'rcmtasktagsedit'); |
8047 |
+ $this->rc->output->add_gui_object('edittagline', $attrib['id']); |
8048 |
+ |
8049 |
+ $input = new html_inputfield(array('name' => 'tags[]', 'class' => 'tag', 'size' => $attrib['size'], 'tabindex' => $attrib['tabindex'])); |
8050 |
+ return html::div($attrib, $input->show('')); |
8051 |
+ } |
8052 |
+ |
8053 |
+ /** |
8054 |
+ * Generate HTML element for attachments list |
8055 |
+ */ |
8056 |
+ function attachments_list($attrib = array()) |
8057 |
+ { |
8058 |
+ if (!$attrib['id']) |
8059 |
+ $attrib['id'] = 'rcmtaskattachmentlist'; |
8060 |
+ |
8061 |
+ $this->rc->output->add_gui_object('attachmentlist', $attrib['id']); |
8062 |
+ |
8063 |
+ return html::tag('ul', $attrib, '', html::$common_attrib); |
8064 |
+ } |
8065 |
+ |
8066 |
+ /** |
8067 |
+ * Generate the form for event attachments upload |
8068 |
+ */ |
8069 |
+ function attachments_form($attrib = array()) |
8070 |
+ { |
8071 |
+ // add ID if not given |
8072 |
+ if (!$attrib['id']) |
8073 |
+ $attrib['id'] = 'rcmtaskuploadform'; |
8074 |
+ |
8075 |
+ // Get max filesize, enable upload progress bar |
8076 |
+ $max_filesize = rcube_upload_init(); |
8077 |
+ |
8078 |
+ $button = new html_inputfield(array('type' => 'button')); |
8079 |
+ $input = new html_inputfield(array( |
8080 |
+ 'type' => 'file', |
8081 |
+ 'name' => '_attachments[]', |
8082 |
+ 'multiple' => 'multiple', |
8083 |
+ 'size' => $attrib['attachmentfieldsize'], |
8084 |
+ )); |
8085 |
+ |
8086 |
+ return html::div($attrib, |
8087 |
+ html::div(null, $input->show()) . |
8088 |
+ html::div('formbuttons', $button->show(rcube_label('upload'), array('class' => 'button mainaction', |
8089 |
+ 'onclick' => JS_OBJECT_NAME . ".upload_file(this.form)"))) . |
8090 |
+ html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) |
8091 |
+ ); |
8092 |
+ } |
8093 |
+ |
8094 |
+ /** |
8095 |
+ * Register UI object for HTML5 drag & drop file upload |
8096 |
+ */ |
8097 |
+ function file_drop_area($attrib = array()) |
8098 |
+ { |
8099 |
+ if ($attrib['id']) { |
8100 |
+ $this->rc->output->add_gui_object('filedrop', $attrib['id']); |
8101 |
+ $this->rc->output->set_env('filedrop', array('action' => 'upload', 'fieldname' => '_attachments')); |
8102 |
+ } |
8103 |
+ } |
8104 |
+ |
8105 |
+} |