1: <?php
2: /**
3: * Nag_Driver:: defines an API for implementing storage backends for Nag.
4: *
5: * See the enclosed file COPYING for license information (GPL). If you
6: * did not receive this file, see http://www.horde.org/licenses/gpl.
7: *
8: * @author Jon Parise <jon@horde.org>
9: * @author Jan Schneider <jan@horde.org>
10: * @package Nag
11: */
12: class Nag_Driver
13: {
14: /**
15: * A Nag_Task instance holding the current task list.
16: *
17: * @var Nag_Task
18: */
19: public $tasks;
20:
21: /**
22: * String containing the current tasklist.
23: *
24: * @var string
25: */
26: protected $_tasklist = '';
27:
28: /**
29: * Hash containing connection parameters.
30: *
31: * @var array
32: */
33: protected $_params = array();
34:
35: /**
36: * An error message to throw when something is wrong.
37: *
38: * @var string
39: */
40: protected $_errormsg;
41:
42: /**
43: * Constructor - just store the $params in our newly-created
44: * object. All other work is done by initialize().
45: *
46: * @param array $params Any parameters needed for this driver.
47: * @param string $errormsg Custom error message
48: *
49: * @return Nag_Driver
50: */
51: public function __construct(array $params = array(), $errormsg = null)
52: {
53: $this->tasks = new Nag_Task();
54: $this->_params = $params;
55: if (is_null($errormsg)) {
56: $this->_errormsg = _("The Tasks backend is not currently available.");
57: } else {
58: $this->_errormsg = $errormsg;
59: }
60: }
61:
62: /**
63: * List all alarms near $date.
64: *
65: * @param integer $date The unix epoch time to check for alarms.
66: *
67: * @return array An array of tasks that have alarms that match.
68: */
69: public function listAlarms($date)
70: {
71: if (!$this->tasks->count()) {
72: $result = $this->retrieve(0);
73: }
74: $alarms = array();
75: $this->tasks->reset();
76: while ($task = $this->tasks->each()) {
77: if ($task->alarm &&
78: ($task->due - ($task->alarm * 60)) <= $date) {
79: $alarms[$task_id] = $task;
80: }
81: }
82: return $alarms;
83: }
84:
85: /**
86: * Attempts to return a concrete Nag_Driver instance based on $driver.
87: *
88: * @param string $tasklist The name of the tasklist to load.
89: *
90: * @param string $driver The type of concrete Nag_Driver subclass
91: * to return. The is based on the storage
92: * driver ($driver). The code is dynamically
93: * included.
94: *
95: * @param array $params (optional) A hash containing any additional
96: * configuration or connection parameters a
97: * subclass might need.
98: *
99: * @return mixed The newly created concrete Nag_Driver instance, or
100: * false on an error.
101: */
102: static public function factory($tasklist = '', $driver = null, $params = null)
103: {
104: if (is_null($driver)) {
105: $driver = $GLOBALS['conf']['storage']['driver'];
106: }
107: $driver = ucfirst(basename($driver));
108: if (is_null($params)) {
109: $params = Horde::getDriverConfig('storage', $driver);
110: }
111: $class = 'Nag_Driver_' . $driver;
112: if (class_exists($class)) {
113: try {
114: $nag = new $class($tasklist, $params);
115: } catch (Nag_Exception $e) {
116: $nag = new Nag_Driver($params, sprintf(_("The Tasks backend is not currently available: %s"), $e->getMessage()));
117: }
118: } else {
119: $nag = new Nag_Driver($params, sprintf(_("Unable to load the definition of %s."), $class));
120: }
121:
122: return $nag;
123: }
124:
125: /**
126: * Attempts to return a reference to a concrete Nag_Driver
127: * instance based on $driver. It will only create a new instance
128: * if no Nag_Driver instance with the same parameters currently
129: * exists.
130: *
131: * This should be used if multiple storage sources are required.
132: *
133: * This method must be invoked as: $var =& Nag_Driver::singleton()
134: *
135: * @param string $tasklist The name of the tasklist to load.
136: *
137: * @param string $driver The type of concrete Nag_Driver subclass
138: * to return. The is based on the storage
139: * driver ($driver). The code is dynamically
140: * included.
141: *
142: * @param array $params (optional) A hash containing any additional
143: * configuration or connection parameters a
144: * subclass might need.
145: *
146: * @return mixed The created concrete Nag_Driver instance, or false
147: * on error.
148: */
149: static public function &singleton($tasklist = '', $driver = null, array $params = null)
150: {
151: static $instances = array();
152:
153: if (is_null($driver)) {
154: $driver = $GLOBALS['conf']['storage']['driver'];
155: }
156:
157: if (is_null($params)) {
158: $params = Horde::getDriverConfig('storage', $driver);
159: }
160:
161: $signature = serialize(array($tasklist, $driver, $params));
162: if (!isset($instances[$signature])) {
163: $instances[$signature] = Nag_Driver::factory($tasklist, $driver, $params);
164: }
165:
166: return $instances[$signature];
167: }
168:
169: /**
170: * Adds a task and handles notification.
171: *
172: * @param string $name The name (short) of the task.
173: * @param string $desc The description (long) of the task.
174: * @param integer $start The start date of the task.
175: * @param integer $due The due date of the task.
176: * @param integer $priority The priority of the task.
177: * @param float $estimate The estimated time to complete the task.
178: * @param integer $completed The completion state of the task.
179: * @param string $category The category of the task.
180: * @param integer $alarm The alarm associated with the task.
181: * @param array $methods The overridden alarm notification methods.
182: * @param string $uid A Unique Identifier for the task.
183: * @param string $parent The parent task.
184: * @param boolean $private Whether the task is private.
185: * @param string $owner The owner of the event.
186: * @param string $assignee The assignee of the event.
187: *
188: * @return array array(ID,UID) of new task
189: */
190: public function add($name, $desc, $start = 0, $due = 0, $priority = 0,
191: $estimate = 0.0, $completed = 0, $category = '',
192: $alarm = 0, array $methods = null, $uid = null,
193: $parent = '', $private = false, $owner = null,
194: $assignee = null)
195: {
196: if (is_null($uid)) {
197: $uid = strval(new Horde_Support_Guid());
198: }
199: if (is_null($owner)) {
200: $owner = $GLOBALS['registry']->getAuth();
201: }
202:
203: $taskId = $this->_add($name, $desc, $start, $due, $priority, $estimate,
204: $completed, $category, $alarm, $methods, $uid,
205: $parent, $private, $owner, $assignee);
206:
207: $task = $this->get($taskId);
208: $task->process();
209:
210: /* Log the creation of this item in the history log. */
211: $history = $GLOBALS['injector']->getInstance('Horde_History');
212: try {
213: $history->log('nag:' . $this->_tasklist . ':' . $uid, array('action' => 'add'), true);
214: } catch (Exception $e) {
215: Horde::logMessage($e, 'ERR');
216: }
217:
218: /* Log completion status changes. */
219: if ($completed) {
220: try {
221: $history->log('nag:' . $this->_tasklist . ':' . $uid, array('action' => 'complete'), true);
222: } catch (Exception $e) {
223: Horde::logMessage($e, 'ERR');
224: }
225: }
226:
227: /* Notify users about the new event. */
228: $result = Nag::sendNotification('add', $task);
229:
230: /* Add an alarm if necessary. */
231: if (!empty($alarm) &&
232: ($alarm = $task->toAlarm())) {
233: $alarm['start'] = new Horde_Date($alarm['start']);
234: $GLOBALS['injector']->getInstance('Horde_Alarm')->set($alarm);
235: }
236:
237: return array($taskId, $uid);
238: }
239:
240: /**
241: * Modifies an existing task and handles notification.
242: *
243: * @param string $taskId The task to modify.
244: * @param string $name The name (short) of the task.
245: * @param string $desc The description (long) of the task.
246: * @param integer $start The start date of the task.
247: * @param integer $due The due date of the task.
248: * @param integer $priority The priority of the task.
249: * @param float $estimate The estimated time to complete the task.
250: * @param integer $completed The completion state of the task.
251: * @param string $category The category of the task.
252: * @param integer $alarm The alarm associated with the task.
253: * @param array $methods The overridden alarm notification
254: * methods.
255: * @param string $parent The parent task.
256: * @param boolean $private Whether the task is private.
257: * @param string $owner The owner of the event.
258: * @param string $assignee The assignee of the event.
259: * @param integer $completed_date The task's completion date.
260: * @param string $tasklist The new tasklist.
261: *
262: * @throws Nag_Exception
263: */
264: public function modify($taskId, $name, $desc, $start = 0, $due = 0,
265: $priority = 0, $estimate = 0.0, $completed = 0,
266: $category = '', $alarm = 0, array $methods = null,
267: $parent = '', $private = false, $owner = null,
268: $assignee = null, $completed_date = null,
269: $tasklist = null)
270: {
271: /* Retrieve unmodified task. */
272: $task = $this->get($taskId);
273:
274: /* Avoid circular reference. */
275: if ($parent == $taskId) {
276: $parent = '';
277: }
278: $modify = $this->_modify($taskId, $name, $desc, $start, $due,
279: $priority, $estimate, $completed, $category,
280: $alarm, $methods, $parent, $private, $owner,
281: $assignee, $completed_date);
282:
283: $new_task = $this->get($task->id);
284: $log_tasklist = $this->_tasklist;
285: if (!is_null($tasklist) && $task->tasklist != $tasklist) {
286: /* Moving the task to another tasklist. */
287: try {
288: $share = $GLOBALS['nag_shares']->getShare($task->tasklist);
289: } catch (Horde_Share_Exception $e) {
290: Horde::logMessage($e->getMessage(), 'ERR');
291: throw new Nag_Exception($e);
292: }
293:
294: if (!$share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::DELETE)) {
295: $GLOBALS['notification']->push(sprintf(_("Access denied removing task from %s."), $share->get('name')), 'horde.error');
296: return false;
297: }
298:
299: try {
300: $share = $GLOBALS['nag_shares']->getShare($tasklist);
301: } catch (Horde_Share_Exception $e) {
302: Horde::logMessage($e->getMessage(), 'ERR');
303: throw new Nag_Exception($e);
304: }
305:
306: if (!$share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
307: $GLOBALS['notification']->push(sprintf(_("Access denied moving the task to %s."), $share->get('name')), 'horde.error');
308: }
309:
310: $moved = $this->_move($task->id, $tasklist);
311: $new_storage = Nag_Driver::singleton($tasklist);
312: $new_task = $new_storage->get($task->id);
313:
314: /* Log the moving of this item in the history log. */
315: if (!empty($task->uid)) {
316: $history = $GLOBALS['injector']->getInstance('Horde_History');
317: try {
318: $history->log('nag:' . $task->tasklist . ':' . $task->uid, array('action' => 'delete'), true);
319: } catch (Exception $e) {
320: Horde::logMessage($e, 'ERR');
321: }
322: try {
323: $history->log('nag:' . $tasklist . ':' . $task->uid, array('action' => 'add'), true);
324: } catch (Exception $e) {
325: Horde::logMessage($e, 'ERR');
326: }
327: $log_tasklist = $tasklist;
328: }
329: }
330:
331: /* Update alarm if necessary. */
332: $horde_alarm = $GLOBALS['injector']->getInstance('Horde_Alarm');
333: if (empty($alarm) || $completed) {
334: $horde_alarm->delete($task->uid);
335: } else {
336: $task = $this->get($taskId);
337: $task->process();
338: $alarm = $task->toAlarm();
339: if ($alarm) {
340: $alarm['start'] = new Horde_Date($alarm['start']);
341: $horde_alarm->set($alarm);
342: }
343: }
344:
345: /* Log the modification of this item in the history log. */
346: if (!empty($task->uid)) {
347: try {
348: $GLOBALS['injector']->getInstance('Horde_History')->log('nag:' . $log_tasklist . ':' . $task->uid, array('action' => 'modify'), true);
349: } catch (Exception $e) {
350: Horde::logMessage($e, 'ERR');
351: }
352: }
353:
354: /* Log completion status changes. */
355: if ($task->completed != $completed) {
356: $attributes = array('action' => 'complete');
357: if (!$completed) {
358: $attributes['ts'] = 0;
359: }
360: try {
361: $GLOBALS['injector']->getInstance('Horde_History')->log('nag:' . $log_tasklist . ':' . $task->uid, $attributes, true);
362: } catch (Exception $e) {
363: Horde::logMessage($e, 'ERR');
364: }
365: }
366:
367: /* Notify users about the changed event. */
368: try {
369: $result = Nag::sendNotification('edit', $new_task, $task);
370: } catch (Nag_Exception $e) {
371: Horde::logMessage($e, 'ERR');
372: }
373:
374: return true;
375: }
376:
377: /**
378: * Deletes a task and handles notification.
379: *
380: * @param string $taskId The task to delete.
381: */
382: public function delete($taskId)
383: {
384: /* Get the task's details for use later. */
385: $task = $this->get($taskId);
386: $delete = $this->_delete($taskId);
387:
388: /* Log the deletion of this item in the history log. */
389: if (!empty($task->uid)) {
390: try {
391: $GLOBALS['injector']->getInstance('Horde_History')->log('nag:' . $this->_tasklist . ':' . $task->uid, array('action' => 'delete'), true);
392: } catch (Exception $e) {
393: Horde::logMessage($e, 'ERR');
394: }
395: }
396:
397: /* Notify users about the deleted event. */
398: try {
399: $result = Nag::sendNotification('delete', $task);
400: } catch (Nag_Exception $e) {
401: Horde::logMessage($e, 'ERR');
402: }
403:
404: /* Delete alarm if necessary. */
405: if (!empty($task->alarm)) {
406: $GLOBALS['injector']->getInstance('Horde_Alarm')->delete($task->uid);
407: }
408: }
409:
410: /**
411: * Retrieves tasks from the database.
412: *
413: * @throws Nag_Exception
414: */
415: public function retrieve()
416: {
417: throw new Nag_Exception($this->_errormsg);
418: }
419:
420: /**
421: * Retrieves sub-tasks from the database.
422: *
423: * @param string $parentId The parent id for the sub-tasks to retrieve.
424: *
425: * @return array List of sub-tasks.
426: * @throws Nag_Exception
427: */
428: public function getChildren($parentId)
429: {
430: throw new Nag_Exception($this->_errormsg);
431: }
432:
433: /**
434: * Retrieves one task from the database.
435: *
436: * @param string $taskId The id of the task to retrieve.
437: *
438: * @return Nag_Task A Nag_Task object.
439: * @throws Nag_Exception
440: */
441: public function get($taskId)
442: {
443: throw new Nag_Exception($this->_errormsg);
444: }
445:
446: /**
447: * Retrieves one task from the database by UID.
448: *
449: * @param string $uid The UID of the task to retrieve.
450: *
451: * @return Nag_Task A Nag_Task object.
452: * @throws Nag_Exception
453: */
454: public function getByUID($uid)
455: {
456: throw new Nag_Exception($this->_errormsg);
457: }
458:
459: }
460: