1: <?php
2: /**
3: * Nag driver classes for the Kolab IMAP server.
4: *
5: * Copyright 2004-2012 Horde LLC (http://www.horde.org/)
6: *
7: * See the enclosed file COPYING for license information (GPL). If you
8: * did not receive this file, see http://www.horde.org/licenses/gpl.
9: *
10: * @author Gunnar Wrobel <wrobel@pardus.de>
11: * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
12: * @author Stuart Binge <omicron@mighty.co.za>
13: * @package Nag
14: */
15: class Nag_Driver_Kolab extends Nag_Driver
16: {
17: /**
18: * The Kolab_Storage backend.
19: *
20: * @var Horde_Kolab_Storage
21: */
22: protected $_kolab;
23:
24: /**
25: * The current tasklist.
26: *
27: * @var Horde_Kolab_Storage_Data
28: */
29: private $_data;
30:
31: /**
32: * Constructs a new Kolab storage object.
33: *
34: * @param string $tasklist The tasklist to load.
35: * @param array $params A hash containing connection parameters.
36: */
37: public function __construct($tasklist, $params = array())
38: {
39: $this->_tasklist = $tasklist;
40: $this->_kolab = $GLOBALS['injector']->getInstance('Horde_Kolab_Storage');
41: }
42:
43: /**
44: * Return the Kolab data handler for the current tasklist.
45: *
46: * @return Horde_Kolab_Storage_Date The data handler.
47: */
48: private function _getData()
49: {
50: if (empty($this->_tasklist)) {
51: throw new Nag_Exception(
52: 'The tasklist has been left undefined but is required!'
53: );
54: }
55: if ($this->_data === null) {
56: $this->_data = $this->_getDataForTasklist($this->_tasklist);
57: }
58: return $this->_data;
59: }
60:
61: /**
62: * Return the Kolab data handler for the specified tasklist.
63: *
64: * @param string $tasklist The tasklist name.
65: *
66: * @return Horde_Kolab_Storage_Date The data handler.
67: */
68: private function _getDataForTasklist($tasklist)
69: {
70: return $this->_kolab->getData(
71: $GLOBALS['nag_shares']->getShare($tasklist)->get('folder'),
72: 'task'
73: );
74: }
75:
76: /**
77: * Retrieves one task from the backend.
78: *
79: * @param string $taskId The id of the task to retrieve.
80: *
81: * @return Nag_Task A Nag_Task object.
82: * @throws Horde_Exception_NotFound
83: * @throws Nag_Exception
84: */
85: public function get($taskId)
86: {
87: if ($this->_getData()->objectIdExists($taskId)) {
88: $task = $this->_getData()->getObject($taskId);
89: $nag_task = $this->_buildTask($task);
90: $nag_task['tasklist_id'] = $this->_tasklist;
91: return new Nag_Task($nag_task);
92: } else {
93: throw new Horde_Exception_NotFound(_("Not Found"));
94: }
95: }
96:
97: /**
98: * Build a task based a data array
99: *
100: * @param array $task The data for the task
101: *
102: * @return array The converted data array representing the task
103: */
104: function _buildTask($task)
105: {
106: $task['task_id'] = $task['uid'];
107:
108: $task['category'] = $task['categories'];
109: unset($task['categories']);
110:
111: $task['name'] = $task['summary'];
112: unset($task['summary']);
113:
114: if (isset($task['due-date'])) {
115: $task['due'] = $task['due-date'];
116: unset($task['due-date']);
117: }
118:
119: if (isset($task['start-date'])) {
120: $task['start'] = $task['start-date'];
121: unset($task['start-date']);
122: }
123:
124: $task['desc'] = $task['body'];
125: unset($task['body']);
126:
127: if (!empty($task['completed'])) {
128: $task['completed'] = 1;
129: } else {
130: $task['completed'] = 0;
131: }
132:
133: if ($task['sensitivity'] == 'public') {
134: $task['private'] = false;
135: } else {
136: $task['private'] = true;
137: }
138: unset($task['sensitivity']);
139:
140: $share = $GLOBALS['nag_shares']->getShare($this->_tasklist);
141: $task['owner'] = $share->get('owner');
142:
143: return $task;
144: }
145:
146:
147: /**
148: * Retrieves one task from the database by UID.
149: *
150: * @param string $uid The UID of the task to retrieve.
151: *
152: * @return array The array of task attributes.
153: */
154: public function getByUID($uid)
155: {
156: return $this->_wrapper->getByUID($uid);
157: }
158:
159: /**
160: * Adds a task to the backend storage.
161: *
162: * @param string $name The name (short) of the task.
163: * @param string $desc The description (long) of the task.
164: * @param integer $start The start date of the task.
165: * @param integer $due The due date of the task.
166: * @param integer $priority The priority of the task.
167: * @param float $estimate The estimated time to complete the task.
168: * @param integer $completed The completion state of the task.
169: * @param string $category The category of the task.
170: * @param integer $alarm The alarm associated with the task.
171: * @param array $methods The overridden alarm notification methods.
172: * @param string $uid A Unique Identifier for the task.
173: * @param string $parent The parent task id.
174: * @param boolean $private Whether the task is private.
175: * @param string $owner The owner of the event.
176: * @param string $assignee The assignee of the event.
177: *
178: * @return string The Nag ID of the new task.
179: * @throws Nag_Exception
180: */
181: protected function _add(
182: $name, $desc, $start = 0, $due = 0, $priority = 0,
183: $estimate = 0.0, $completed = 0, $category = '', $alarm = 0,
184: array $methods = null, $uid = null, $parent = '', $private = false,
185: $owner = null, $assignee = null
186: ) {
187: $object = $this->_getObject(
188: $name,
189: $desc,
190: $start,
191: $due,
192: $priority,
193: $estimate,
194: $completed,
195: $category,
196: $alarm,
197: $methods,
198: $parent,
199: $private,
200: $owner,
201: $assignee
202: );
203: if (is_null($uid)) {
204: $object['uid'] = $this->_getData()->generateUid();
205: } else {
206: $object['uid'] = $uid;
207: }
208: $this->_getData()->create($object);
209: return $object['uid'] = $uid;
210: }
211:
212: /**
213: * Modifies an existing task.
214: *
215: * @param string $taskId The task to modify.
216: * @param string $name The name (short) of the task.
217: * @param string $desc The description (long) of the task.
218: * @param integer $start The start date of the task.
219: * @param integer $due The due date of the task.
220: * @param integer $priority The priority of the task.
221: * @param float $estimate The estimated time to complete the task.
222: * @param integer $completed The completion state of the task.
223: * @param string $category The category of the task.
224: * @param integer $alarm The alarm associated with the task.
225: * @param array $methods The overridden alarm notification
226: * methods.
227: * @param string $parent The parent task id.
228: * @param boolean $private Whether the task is private.
229: * @param string $owner The owner of the event.
230: * @param string $assignee The assignee of the event.
231: * @param integer $completed_date The task's completion date.
232: *
233: * @return boolean Indicates if the modification was successfull.
234: */
235: protected function _modify($taskId, $name, $desc, $start = 0, $due = 0,
236: $priority = 0, $estimate = 0.0, $completed = 0,
237: $category = '', $alarm = 0, $methods = null,
238: $parent = null, $private = false, $owner = null,
239: $assignee = null, $completed_date = null)
240: {
241: $object = $this->_getObject(
242: $name,
243: $desc,
244: $start,
245: $due,
246: $priority,
247: $estimate,
248: $completed,
249: $category,
250: $alarm,
251: $methods,
252: $parent,
253: $private,
254: $owner,
255: $assignee,
256: $completed_date
257: );
258: $object['uid'] = $taskId;
259: $this->_getData()->modify($object);
260: return true;
261: }
262:
263: /**
264: * Retrieve the Kolab object representations for the task.
265: *
266: * @param string $name The name (short) of the task.
267: * @param string $desc The description (long) of the task.
268: * @param integer $start The start date of the task.
269: * @param integer $due The due date of the task.
270: * @param integer $priority The priority of the task.
271: * @param float $estimate The estimated time to complete the task.
272: * @param integer $completed The completion state of the task.
273: * @param string $category The category of the task.
274: * @param integer $alarm The alarm associated with the task.
275: * @param array $methods The overridden alarm notification methods.
276: * @param string $parent The parent task id.
277: * @param boolean $private Whether the task is private.
278: * @param string $owner The owner of the event.
279: * @param string $assignee The assignee of the event.
280: * @param integer $completed_date The task's completion date.
281: *
282: * @return array The Kolab object.
283: *
284: * @throws Nag_Exception
285: */
286: private function _getObject(
287: $name, $desc, $start = 0, $due = 0, $priority = 0,
288: $estimate = 0.0, $completed = 0, $category = '', $alarm = 0,
289: array $methods = null, $parent = '', $private = false, $owner = null,
290: $assignee = null, $completed_date = null
291: ) {
292: $object = array(
293: 'summary' => $name,
294: 'body' => $desc,
295: //@todo: Match Horde/Kolab priority values
296: 'priority' => $priority,
297: //@todo: Extend to Kolab multiple categories (tagger)
298: 'categories' => $category,
299: 'parent' => $parent,
300: );
301: if ($start !== 0) {
302: $object['start-date'] = $start;
303: }
304: if ($due !== 0) {
305: $object['due-date'] = $due;
306: }
307: if ($completed) {
308: $object['completed'] = 100;
309: $object['status'] = 'completed';
310: } else {
311: $object['completed'] = 0;
312: $object['status'] = 'not-started';
313: }
314: if ($alarm !== 0) {
315: $object['alarm'] = $alarm;
316: }
317: if ($private) {
318: $object['sensitivity'] = 'private';
319: } else {
320: $object['sensitivity'] = 'public';
321: }
322: if ($completed_date !== null) {
323: $object['completed_date'] = $completed_date;
324: }
325: if ($estimate !== 0.0) {
326: $object['estimate'] = number_format($estimate, 2);
327: }
328: if ($methods !== null) {
329: $object['horde-alarm-methods'] = serialize($methods);
330: }
331: if ($owner !== null) {
332: //@todo: Display name
333: $object['creator'] = array(
334: 'smtp-address' => $owner,
335: );
336: }
337: if ($assignee !== null) {
338: //@todo: Display name
339: $object['organizer'] = array(
340: 'smtp-address' => $assignee,
341: );
342: }
343: return $object;
344: }
345:
346: /**
347: * Moves a task to a different tasklist.
348: *
349: * @param string $taskId The task to move.
350: * @param string $newTasklist The new tasklist.
351: */
352: protected function _move($taskId, $newTasklist)
353: {
354: return $this->_getData()->move(
355: $taskId,
356: $GLOBALS['nag_shares']->getShare($newTasklist)->get('folder')
357: );
358: }
359:
360: /**
361: * Deletes a task from the backend.
362: *
363: * @param string $taskId The task to delete.
364: */
365: protected function _delete($taskId)
366: {
367: $this->_getData()->delete($taskId);
368: }
369:
370: /**
371: * Deletes all tasks from the backend.
372: */
373: public function deleteAll()
374: {
375: $this->_getData()->deleteAll();
376: }
377:
378: /**
379: * Retrieves tasks from the Kolab server.
380: *
381: * @param integer $completed Which tasks to retrieve (1 = all tasks,
382: * 0 = incomplete tasks, 2 = complete tasks).
383: *
384: * @return mixed True on success, PEAR_Error on failure.
385: */
386: function retrieve($completed = Nag::VIEW_ALL)
387: {
388: $dict = array();
389: $this->tasks = new Nag_Task();
390:
391: $task_list = $this->_getData()->getObjects();
392: if (empty($task_list)) {
393: return true;
394: }
395:
396: foreach ($task_list as $task) {
397: $tuid = $task['uid'];
398: $nag_task = $this->_buildTask($task);
399: $nag_task['tasklist_id'] = $this->_tasklist;
400: $t = new Nag_Task($nag_task);
401: $complete = $t->completed;
402: if (empty($t->start)) {
403: $start = null;
404: } else {
405: $start = $t->start;
406: }
407:
408: if (($completed == Nag::VIEW_INCOMPLETE && ($complete || $start > time())) ||
409: ($completed == Nag::VIEW_COMPLETE && !$complete) ||
410: ($completed == Nag::VIEW_FUTURE &&
411: ($complete || $start == 0 || $start < time())) ||
412: ($completed == Nag::VIEW_FUTURE_INCOMPLETE && $complete)) {
413: continue;
414: }
415: if (empty($t->parent_id)) {
416: $this->tasks->add($t);
417: } else {
418: $dict[$tuid] = $t;
419: }
420: }
421:
422: /* Build a tree from the subtasks. */
423: foreach (array_keys($dict) as $key) {
424: $task = $this->tasks->get($dict[$key]->parent_id);
425: if ($task) {
426: $task->add($dict[$key]);
427: } elseif (isset($dict[$dict[$key]->parent_id])) {
428: $dict[$dict[$key]->parent_id]->add($dict[$key]);
429: } else {
430: $this->tasks->add($dict[$key]);
431: }
432: }
433:
434: return true;
435: }
436:
437: /**
438: * Lists all alarms near $date.
439: *
440: * @param integer $date The unix epoch time to check for alarms.
441: *
442: * @return array An array of tasks that have alarms that match.
443: */
444: public function listAlarms($date)
445: {
446: $task_list = $this->_getData()->getObjects();
447: if (empty($task_list)) {
448: return array();
449: }
450:
451: $tasks = array();
452: foreach ($task_list as $task) {
453: $tuid = $task['uid'];
454: $t = new Nag_Task($this->_buildTask($task));
455: if ($t->alarm && $t->due &&
456: $t->due - $t->alarm * 60 < $date) {
457: $tasks[] = $t;
458: }
459: }
460:
461: return $tasks;
462: }
463:
464: /**
465: * Retrieves sub-tasks from the database.
466: *
467: * @param string $parentId The parent id for the sub-tasks to retrieve.
468: *
469: * @return array List of sub-tasks.
470: */
471: public function getChildren($parentId)
472: {
473: $task_list = $this->_getData()->getObjects();
474: if (empty($task_list)) {
475: return array();
476: }
477:
478: $tasks = array();
479:
480: foreach ($task_list as $task) {
481: if ($task['parent'] != $parentId) {
482: continue;
483: }
484: $t = new Nag_Task($this->_buildTask($task));
485: $children = $this->getChildren($t->id);
486: $t->mergeChildren($children);
487: $tasks[] = $t;
488: }
489:
490: return $tasks;
491: }
492: }
493: