1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10: class Nag_Api extends Horde_Registry_Api
11: {
12: 13: 14: 15: 16:
17: public $links = array(
18: 'show' => '%application%/view.php?tasklist=|tasklist|&task=|task|&uid=|uid|'
19: );
20:
21: 22: 23: 24: 25:
26: public function ajaxDefaults()
27: {
28: return array(
29: 'URI_TASKLIST_EXPORT' => (string)Horde::url('data.php', true)->add(array('actionID' => 'export', 'exportTasks' => 1, 'exportID' => Horde_Data::EXPORT_ICALENDAR, 'exportList' => '')),
30: 'default_tasklist' => Nag::getDefaultTasklist(Horde_Perms::EDIT),
31: 'default_due' => (bool)$GLOBALS['prefs']->getValue('default_due'),
32: 'default_due_days' => (int)$GLOBALS['prefs']->getValue('default_due_days'),
33: 'default_due_time' => $GLOBALS['prefs']->getValue('default_due_time'),
34: );
35: }
36:
37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56:
57: public function listTasks($sortby = null, $sortdir = null,
58: $altsortby = null, $tasklists = null,
59: $completed = null, $json = false)
60: {
61: $completedArray = array('incomplete' => Nag::VIEW_INCOMPLETE,
62: 'all' => Nag::VIEW_ALL,
63: 'complete' => Nag::VIEW_COMPLETE,
64: 'future' => Nag::VIEW_FUTURE,
65: 'future_incomplete' => Nag::VIEW_FUTURE_INCOMPLETE);
66:
67: if (!isset($sortby)) {
68: $sortby = $GLOBALS['prefs']->getValue('sortby');
69: }
70: if (!isset($sortdir)) {
71: $sortdir = $GLOBALS['prefs']->getValue('sortdir');
72: }
73: if (is_null($altsortby)) {
74: $altsortby = $GLOBALS['prefs']->getValue('altsortby');
75: }
76: if (is_null($tasklists)) {
77: $tasklists = $GLOBALS['display_tasklists'];
78: }
79: if (is_null($completed) || !isset($completedArray[$completed])) {
80: $completed = $GLOBALS['prefs']->getValue('show_completed');
81: } else {
82: $completed = $completedArray[$completed];
83: }
84:
85: $tasks = Nag::listTasks($sortby, $sortdir, $altsortby, $tasklists, $completed);
86: $tasks->reset();
87: $list = array();
88: while ($task = $tasks->each()) {
89: $list[$task->id] = $json ? $task->toJson() : $task->toHash();
90: }
91:
92: return $list;
93: }
94:
95: 96: 97: 98: 99: 100: 101: 102: 103:
104: public function listTasklists($owneronly = false, $permission = Horde_Perms::SHOW)
105: {
106: return Nag::listTasklists($owneronly, $permission);
107: }
108:
109: 110: 111: 112: 113: 114: 115: 116: 117:
118: public function getTasklist($name)
119: {
120: try {
121: $tasklist = $GLOBALS['nag_shares']->getShare($name);
122: } catch (Horde_Share_Exception $e) {
123: Horde::logMessage($e->getMessage(), 'ERR');
124: throw new Nag_Exception($e);
125: }
126: if (!$tasklist->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ)) {
127: throw new Horde_Exception_PermissionDenied(_("You are not allowed to retrieve this task list."));
128: }
129: return $tasklist;
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139: 140:
141: public function addTasklist($name, $description = '', $color = '')
142: {
143: $tasklist = Nag::addTasklist(array('name' => $name, 'description' => $description, 'color' => $color));
144: return $tasklist->getName();
145: }
146:
147: 148: 149: 150: 151: 152:
153: public static function updateTasklist($name, $info)
154: {
155: try {
156: $tasklist = $GLOBALS['nag_shares']->getShare($name);
157: } catch (Horde_Share_Exception $e) {
158: Horde::logMessage($e->getMessage(), 'ERR');
159: throw new Nag_Exception($e);
160: }
161:
162: return Nag::updateTasklist($tasklist, $info);
163: }
164:
165: 166: 167: 168: 169:
170: public function deleteTasklist($id)
171: {
172: $tasklist = $GLOBALS['nag_shares']->getShare($id);
173: return Nag::deleteTasklist($tasklist);
174: }
175:
176: 177: 178: 179: 180: 181: 182:
183: public function getDisplayedTasklists()
184: {
185: return $GLOBALS['display_tasklists'];
186: }
187:
188: 189: 190: 191: 192: 193: 194:
195: public function setDisplayedTasklists($list)
196: {
197: $GLOBALS['display_tasklists'] = $list;
198: $GLOBALS['prefs']->setValue('display_tasklists', serialize($list));
199: }
200:
201: 202: 203: 204: 205: 206: 207: 208:
209: public function modified($uid, $tasklist = null)
210: {
211: $modified = $this->getActionTimestamp($uid, 'modify', $tasklist);
212: if (empty($modified)) {
213: $modified = $this->getActionTimestamp($uid, 'add', $tasklist);
214: }
215: return $modified;
216: }
217:
218: 219: 220: 221: 222: 223: 224: 225: 226:
227: public function browse($path = '', $properties = array())
228: {
229: global $registry;
230:
231:
232: if (!$properties) {
233: $properties = array('name', 'icon', 'browseable');
234: }
235:
236: if (substr($path, 0, 3) == 'nag') {
237: $path = substr($path, 3);
238: }
239: $path = trim($path, '/');
240: $parts = explode('/', $path);
241:
242: if (empty($path)) {
243:
244:
245: $tasklists = Nag::listTasklists(false, Horde_Perms::READ);
246: $owners = array();
247: foreach ($tasklists as $tasklist) {
248: $owners[$tasklist->get('owner') ? $tasklist->get('owner') : '-system-'] = true;
249: }
250:
251: $results = array();
252: foreach (array_keys($owners) as $owner) {
253: if (in_array('name', $properties)) {
254: $results['nag/' . $owner]['name'] = $owner;
255: }
256: if (in_array('icon', $properties)) {
257: $results['nag/' . $owner]['icon'] = Horde_Themes::img('user.png');
258: }
259: if (in_array('browseable', $properties)) {
260: $results['nag/' . $owner]['browseable'] = true;
261: }
262: if (in_array('contenttype', $properties)) {
263: $results['nag/' . $owner]['contenttype'] =
264: 'httpd/unix-directory';
265: }
266: if (in_array('contentlength', $properties)) {
267: $results['nag/' . $owner]['contentlength'] = 0;
268: }
269: if (in_array('modified', $properties)) {
270: $results['nag/' . $owner]['modified'] =
271: $_SERVER['REQUEST_TIME'];
272: }
273: if (in_array('created', $properties)) {
274: $results['nag/' . $owner]['created'] = 0;
275: }
276: }
277: return $results;
278:
279: } elseif (count($parts) == 1) {
280:
281:
282:
283: $tasklists = $GLOBALS['nag_shares']->listShares(
284: $GLOBALS['registry']->getAuth(),
285: array('perm' => Horde_Perms::SHOW,
286: 'attributes' => $parts[0]));
287:
288:
289:
290:
291:
292: $tasklists = Nag::permissionsFilter($tasklists);
293:
294: $results = array();
295: foreach ($tasklists as $tasklistId => $tasklist) {
296: $retpath = 'nag/' . $parts[0] . '/' . $tasklistId;
297: if (in_array('name', $properties)) {
298: $results[$retpath]['name'] = sprintf(_("Tasks from %s"), $tasklist->get('name'));
299: $results[$retpath . '.ics']['name'] = $tasklist->get('name');
300: }
301: if (in_array('icon', $properties)) {
302: $results[$retpath]['icon'] = Horde_Themes::img('nag.png');
303: $results[$retpath . '.ics']['icon'] = Horde_Themes::img('mime/icalendar.png');
304: }
305: if (in_array('browseable', $properties)) {
306: $results[$retpath]['browseable'] = $tasklist->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ);
307: $results[$retpath . '.ics']['browseable'] = false;
308: }
309: if (in_array('contenttype', $properties)) {
310: $results[$retpath]['contenttype'] = 'httpd/unix-directory';
311: $results[$retpath . '.ics']['contenttype'] = 'text/calendar';
312: }
313: if (in_array('contentlength', $properties)) {
314: $results[$retpath]['contentlength'] = 0;
315: $results[$retpath . '.ics']['contentlength'] = strlen($this->exportTasklist($tasklistId, 'text/calendar'));
316: }
317: if (in_array('modified', $properties)) {
318:
319: $results[$retpath]['modified'] = $_SERVER['REQUEST_TIME'];
320: $results[$retpath . '.ics']['modified'] = $_SERVER['REQUEST_TIME'];
321: }
322: if (in_array('created', $properties)) {
323:
324: $results[$retpath]['created'] = 0;
325: $results[$retpath . '.ics']['created'] = 0;
326: }
327: }
328: return $results;
329:
330: } elseif (count($parts) == 2 && substr($parts[1], -4) == '.ics') {
331:
332:
333:
334: $tasklist = substr($parts[1], 0, -4);
335: if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
336: return PEAR::raiseError(_("Invalid tasklist file requested."), 404);
337: }
338: $ical_data = $this->exportTasklist($tasklist, 'text/calendar');
339: $result = array('data' => $ical_data,
340: 'mimetype' => 'text/calendar',
341: 'contentlength' => strlen($ical_data),
342: 'mtime' => $_SERVER['REQUEST_TIME']);
343:
344: return $result;
345:
346: } elseif (count($parts) == 2) {
347:
348:
349:
350:
351: if (!Nag::hasPermission($parts[1], Horde_Perms::READ)) {
352: return PEAR::raiseError(_("Invalid tasklist requested."), 404);
353: }
354: $storage = Nag_Driver::singleton($parts[1]);
355: try {
356: $storage->retrieve();
357: } catch (Nag_Exception $e) {
358: throw new Nag_Exception($e->getMessage, 500);
359: }
360: $icon = Horde_Themes::img('nag.png');
361: $results = array();
362: $storage->tasks->reset();
363: while ($task = $storage->tasks->each()) {
364: $key = 'nag/' . $parts[0] . '/' . $parts[1] . '/' . $task->id;
365: if (in_array('name', $properties)) {
366: $results[$key]['name'] = $task->name;
367: }
368: if (in_array('icon', $properties)) {
369: $results[$key]['icon'] = $icon;
370: }
371: if (in_array('browseable', $properties)) {
372: $results[$key]['browseable'] = false;
373: }
374: if (in_array('contenttype', $properties)) {
375: $results[$key]['contenttype'] = 'text/calendar';
376: }
377: if (in_array('contentlength', $properties)) {
378:
379:
380:
381:
382:
383:
384: $results[$key]['contentlength'] = 1;
385: }
386: if (in_array('modified', $properties)) {
387: $results[$key]['modified'] = $this->modified($task->uid, $path);
388: }
389: if (in_array('created', $properties)) {
390: $results[$key]['created'] = $this->getActionTimestamp($task->uid, 'add', $path);
391: }
392: }
393: return $results;
394: } else {
395:
396:
397:
398: if (count($parts) == 3 &&
399: Nag::hasPermission($parts[1], Horde_Perms::READ)) {
400:
401:
402:
403:
404: $storage = Nag_Driver::singleton($parts[1]);
405: $storage->retrieve();
406: try {
407: $storage->get($parts[2]);
408: } catch (Nag_Exception $e) {
409: throw new Nag_Exception($e->getMessage(), 500);
410: }
411: $result = array(
412: 'data' => $this->export($task->uid, 'text/calendar'),
413: 'mimetype' => 'text/calendar');
414: $modified = $this->modified($task->uid, $parts[1]);
415: if (!empty($modified)) {
416: $result['mtime'] = $modified;
417: }
418: return $result;
419: } elseif (count($parts) == 2 &&
420: substr($parts[1], -4) == '.ics' &&
421: Nag::hasPermission(substr($parts[1], 0, -4), Horde_Perms::READ)) {
422:
423:
424:
425: } else {
426:
427:
428:
429: return false;
430: }
431: }
432: }
433:
434: 435: 436: 437: 438: 439: 440: 441: 442:
443: public function put($path, $content, $content_type)
444: {
445: if (substr($path, 0, 3) == 'nag') {
446: $path = substr($path, 3);
447: }
448: $path = trim($path, '/');
449: $parts = explode('/', $path);
450:
451: if (count($parts) == 2 &&
452: substr($parts[1], -4) == '.ics') {
453:
454:
455:
456: if ($content_type == 'application/octet-stream') {
457: $content_type = 'text/calendar';
458: }
459: $tasklist = substr($parts[1], 0, -4);
460: } elseif (count($parts) == 3) {
461: $tasklist = $parts[1];
462:
463:
464:
465:
466: if ($content_type == 'application/octet-stream') {
467: $content_type = 'text/calendar';
468: }
469: } else {
470: throw new Nag_Exception(_("Invalid tasklist name supplied."), 403);
471: }
472:
473: if (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
474:
475:
476: throw new Nag_Exception(_("Tasklist does not exist or no permission to edit"), 403);
477: }
478:
479:
480:
481: $ids = array();
482: $uids_remove = array_flip($this->listUids($tasklist));
483:
484: $storage = Nag_Driver::singleton($tasklist);
485:
486: switch ($content_type) {
487: case 'text/calendar':
488: case 'text/x-vcalendar':
489: $iCal = new Horde_Icalendar();
490: if (!($content instanceof Horde_Icalendar_Vtodo)) {
491: if (!$iCal->parsevCalendar($content)) {
492: throw new Nag_Exception(_("There was an error importing the iCalendar data."), 400);
493: }
494: } else {
495: $iCal->addComponent($content);
496: }
497:
498: foreach ($iCal->getComponents() as $content) {
499: if (!($content instanceof Horde_Icalendar_Vtodo)) {
500: continue;
501: }
502: $task = new Nag_Task();
503: $task->fromiCalendar($content);
504: $task->tasklist = $tasklist;
505: $create = true;
506: if (isset($task->uid)) {
507: try {
508: $existing = $storage->getByUID($task->uid);
509: $create = false;
510: } catch (Horde_Exception_NotFound $e) {
511: }
512: }
513: if (!$create) {
514:
515:
516: if (isset($uids_remove[$task->uid])) {
517: unset($uids_remove[$task->uid]);
518: }
519: if ($existing->private &&
520: $existing->owner != $GLOBALS['registry']->getAuth()) {
521: continue;
522: }
523:
524:
525: $history = $GLOBALS['injector']->getInstance('Horde_History');
526: $created = $modified = null;
527: try {
528: $log = $history->getHistory('nag:' . $tasklist . ':' . $task->uid);
529: foreach ($log as $entry) {
530: switch ($entry['action']) {
531: case 'add':
532: $created = $entry['ts'];
533: break;
534:
535: case 'modify':
536: $modified = $entry['ts'];
537: break;
538: }
539: }
540: } catch (Exception $e) {}
541: if (empty($modified) && !empty($add)) {
542: $modified = $add;
543: }
544: if (!empty($modified) &&
545: $modified >= $content->getAttribute('LAST-MODIFIED')) {
546:
547:
548: continue;
549: }
550:
551:
552: $owner = $existing->owner;
553: $taskId = $existing->id;
554: try {
555: $storage->modify(
556: $taskId,
557: isset($task->name) ? $task->name : $existing->name,
558: isset($task->desc) ? $task->desc : $existing->desc,
559: isset($task->start) ? $task->start : $existing->start,
560: isset($task->due) ? $task->due : $existing->due,
561: isset($task->priority) ? $task->priority : $existing->priority,
562: isset($task->estimate) ? $task->estimate : 0,
563: isset($task->completed) ? (int)$task->completed : $existing->completed,
564: isset($task->category) ? $task->category : $existing->category,
565: isset($task->alarm) ? $task->alarm : $existing->alarm,
566: isset($task->methods) ? $task->methods : $existing->methods,
567: isset($task->parent_id) ? $task->parent_id : $existing->parent_id,
568: isset($task->private) ? $task->private : $existing->private,
569: $owner,
570: isset($task->assignee) ? $task->assignee : $existing->assignee,
571: isset($task->completed_date) ? $task->completed_date : $existing->completed_date
572: );
573: } catch (Nag_Exception $e) {
574: throw new Nag_Exception($e->getMessage(), 500);
575: }
576: $ids[] = $task->uid;
577: } else {
578: try {
579: $newTask = $storage->add(
580: isset($task->name) ? $task->name : '',
581: isset($task->desc) ? $task->desc : '',
582: isset($task->start) ? $task->start : 0,
583: isset($task->due) ? $task->due : 0,
584: isset($task->priority) ? $task->priority : 3,
585: isset($task->estimate) ? $task->estimate : 0,
586: !empty($task->completed),
587: isset($task->category) ? $task->category : '',
588: isset($task->alarm) ? $task->alarm : 0,
589: isset($task->methods) ? $task->methods : null,
590: isset($task->uid) ? $task->uid : null,
591: isset($task->parent_id) ? $task->parent_id : '',
592: !empty($task->private),
593: $GLOBALS['registry']->getAuth(),
594: isset($task->assignee) ? $task->assignee : null);
595: } catch (Nag_Exception $e) {
596: throw new Nag_Exception($e->getMessage(), 500);
597: }
598:
599: $ids[] = $newTask[1];
600: }
601: }
602: break;
603:
604: default:
605: throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $content_type), 400);
606: }
607:
608: if (Nag::hasPermission($tasklist, Horde_Perms::DELETE)) {
609: foreach (array_keys($uids_remove) as $uid) {
610: $this->delete($uid);
611: }
612: }
613:
614: return $ids;
615: }
616:
617: 618: 619: 620: 621: 622: 623: 624:
625: public function path_delete($path)
626: {
627: if (substr($path, 0, 3) == 'nag') {
628: $path = substr($path, 3);
629: }
630: $path = trim($path, '/');
631: $parts = explode('/', $path);
632:
633: if (count($parts) == 2) {
634:
635:
636:
637:
638: throw Nag_Exception(_("Deleting entire tasklists is not supported."), 403);
639:
640: }
641:
642: if (substr($parts[1], -4) == '.ics') {
643: $tasklistID = substr($parts[1], 0, -4);
644: } else {
645: $tasklistID = $parts[1];
646: }
647:
648: if (!(count($parts) == 2 || count($parts) == 3) ||
649: !Nag::hasPermission($tasklistID, Horde_Perms::DELETE)) {
650:
651: throw new Nag_Exception(_("Tasklist does not exist or no permission to delete"), 403);
652: }
653:
654:
655: try {
656: $storage = Nag_Driver::singleton($tasklistID);
657: $storage->retrieve();
658: } catch (Nag_Exception $e) {
659: throw new Nag_Exception(sprintf(_("Connection failed: %s"), $e->getMessage()), 500);
660: }
661: if (count($parts) == 3) {
662:
663: return $storage->delete($parts[2]);
664: } else {
665:
666: try {
667: $storage->deleteAll();
668: } catch (Nag_Exception $e) {
669: throw new Nag_Exception(sprintf(_("Unable to delete tasklist \"%s\": %s"), $tasklistID, $e->getMessage()), 500);
670: }
671:
672:
673: $share = $GLOBALS['nag_shares']->getShare($tasklistID);
674: try {
675: $GLOBALS['nag_shares']->removeShare($share);
676: } catch (Horde_Share_Exception $e) {
677: throw new Nag_Exception($e->getMessage());
678: }
679: }
680: }
681:
682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693:
694: public function listUids($tasklists = null)
695: {
696: if (!isset($GLOBALS['conf']['storage']['driver'])) {
697: throw new Nag_Exception(_("Not configured"));
698: }
699:
700: if (empty($tasklists)) {
701: $tasklists = Nag::getSyncLists();
702: } else {
703: if (!is_array($tasklists)) {
704: $tasklists = array($tasklists);
705: }
706: foreach ($tasklists as $list) {
707: if (!Nag::hasPermission($list, Horde_Perms::READ)) {
708: throw new Horde_Exception_PermissionDenied();
709: }
710: }
711: }
712:
713: $tasks = Nag::listTasks(null, null, null, $tasklists, 1);
714: $uids = array();
715: $tasks->reset();
716: while ($task = $tasks->each()) {
717: $uids[] = $task->uid;
718: }
719:
720: return $uids;
721: }
722:
723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737:
738: public function listBy($action, $timestamp, $tasklist = null, $end = null)
739: {
740: if (empty($tasklist)) {
741: $tasklist = Nag::getSyncLists();
742: $results = array();
743: foreach ($tasklist as $list) {
744: $results = array_merge($results, $this->listBy($action, $timestamp, $list, $end));
745: }
746: return $results;
747: }
748:
749: $filter = array(array('op' => '=', 'field' => 'action', 'value' => $action));
750: if (!empty($end)) {
751: $filter[] = array('op' => '<', 'field' => 'ts', 'value' => $end);
752: }
753: $histories = $GLOBALS['injector']
754: ->getInstance('Horde_History')
755: ->getByTimestamp('>', $timestamp, $filter, 'nag:' . $tasklist);
756:
757:
758: return preg_replace('/^([^:]*:){2}/', '', array_keys($histories));
759: }
760:
761: 762: 763: 764: 765: 766: 767: 768: 769: 770:
771: public function getChanges($start, $end)
772: {
773: return array(
774: 'add' => $this->listBy('add', $start, null, $end),
775: 'modify' => $this->listBy('modify', $start, null, $end),
776: 'delete' => $this->listBy('delete', $start, null, $end));
777: }
778:
779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792:
793: public function getActionTimestamp($uid, $action, $tasklist = null)
794: {
795: if ($tasklist === null) {
796: $tasklist = Nag::getDefaultTasklist(Horde_Perms::READ);
797: } elseif (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
798: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
799: }
800:
801: return $GLOBALS['injector']
802: ->getInstance('Horde_History')
803: ->getActionTimestamp('nag:' . $tasklist . ':' . $uid, $action);
804: }
805:
806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821:
822: public function import($content, $contentType, $tasklist = null)
823: {
824: if ($tasklist === null) {
825: $tasklist = Nag::getDefaultTasklist(Horde_Perms::EDIT);
826: } elseif (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
827: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
828: }
829:
830:
831: $storage = Nag_Driver::singleton($tasklist);
832:
833: switch ($contentType) {
834: case 'text/x-vcalendar':
835: case 'text/calendar':
836: case 'text/x-vtodo':
837: $iCal = new Horde_Icalendar();
838: if (!($content instanceof Horde_Icalendar_Vtodo)) {
839: if (!$iCal->parsevCalendar($content)) {
840: throw new Nag_Exception(_("There was an error importing the iCalendar data."));
841: }
842: } else {
843: $iCal->addComponent($content);
844: }
845:
846: $components = $iCal->getComponents();
847: if (count($components) == 0) {
848: throw new Nag_Exception(_("No iCalendar data was found."));
849: }
850:
851: $ids = array();
852: foreach ($components as $content) {
853: if ($content instanceof Horde_Icalendar_Vtodo) {
854: $task = new Nag_Task();
855: $task->fromiCalendar($content);
856: if (isset($task->uid)) {
857: $existing = $storage->getByUID($task->uid);
858: $taskId = $existing->id;
859: $result = $storage->modify(
860: $taskId,
861: isset($task->name) ? $task->name : $existing->name,
862: isset($task->desc) ? $task->desc : $existing->desc,
863: isset($task->start) ? $task->start : $existing->start,
864: isset($task->due) ? $task->due : $existing->due,
865: isset($task->priority) ? $task->priority : $existing->priority,
866: isset($task->estimate) ? $task->estimate : 0,
867: isset($task->completed) ? (int)$task->completed : $existing->completed,
868: isset($task->category) ? $task->category : $existing->category,
869: isset($task->alarm) ? $task->alarm : $existing->alarm,
870: isset($task->parent_id) ? $task->parent_id : $existing->parent_id,
871: isset($task->private) ? $task->private : $existing->private,
872: isset($task->owner) ? $task->owner : $existing->owner,
873: isset($task->assignee) ? $task->assignee : $existing->assignee
874: );
875: $ids[] = $task->uid;
876: } else {
877: $newTask = $storage->add(
878: isset($task->name) ? $task->name : '',
879: isset($task->desc) ? $task->desc : '',
880: isset($task->start) ? $task->start : 0,
881: isset($task->due) ? $task->due : 0,
882: isset($task->priority) ? $task->priority : 3,
883: isset($task->estimate) ? $task->estimate : 0,
884: !empty($task->completed),
885: isset($task->category) ? $task->category : '',
886: isset($task->alarm) ? $task->alarm : 0,
887: isset($task->methods) ? $task->methods : null,
888: isset($task->uid) ? $task->uid : null,
889: isset($task->parent_id) ? $task->parent_id : '',
890: !empty($task->private),
891: $GLOBALS['registry']->getAuth(),
892: isset($task->assignee) ? $task->assignee : null
893: );
894:
895: $ids[] = $newTask[1];
896: }
897: }
898: }
899: if (count($ids) == 0) {
900: throw Nag_Exception(_("No iCalendar data was found."));
901: } else if (count($ids) == 1) {
902: return $ids[0];
903: }
904: return $ids;
905:
906: case 'activesync':
907: $task = new Nag_Task();
908: $task->fromASTask($content);
909: $results = $storage->add(
910: isset($task->name) ? $task->name : '',
911: isset($task->desc) ? $task->desc : '',
912: isset($task->start) ? $task->start : 0,
913: isset($task->due) ? $task->due : 0,
914: isset($task->priority) ? $task->priority : 3,
915: isset($task->estimate) ? $task->estimate : 0,
916: !empty($task->completed),
917: isset($task->category) ? $task->category : '',
918: isset($task->alarm) ? $task->alarm : 0,
919: isset($task->methods) ? $task->methods : null,
920: isset($task->uid) ? $task->uid : null,
921: isset($task->parent_id) ? $task->parent_id : '',
922: !empty($task->private),
923: $GLOBALS['registry']->getAuth(),
924: isset($task->assignee) ? $task->assignee : null
925: );
926:
927:
928: return $results[1];
929: }
930:
931: throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
932: }
933:
934: 935: 936: 937: 938: 939: 940:
941: public function addTask(array $task)
942: {
943: if (!$GLOBALS['registry']->isAdmin() &&
944: !Nag::hasPermission($task['tasklist'], Horde_Perms::EDIT)) {
945: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
946: }
947:
948: $storage = Nag_Driver::singleton($task['tasklist']);
949: return $storage->add(
950: isset($task['name']) ? $task['name'] : '',
951: isset($task['desc']) ? $task['desc'] : '',
952: isset($task['start']) ? $task['start'] : 0,
953: isset($task['due']) ? $task['due'] : 0,
954: isset($task['priority']) ? $task['priority'] : 3,
955: isset($task['estimate']) ? $task['estimate'] : 0,
956: !empty($task['completed']),
957: isset($task['category']) ? $task['category'] : '',
958: isset($task['alarm']) ? $task['alarm'] : 0,
959: isset($task['methods']) ? $task['methods'] : null,
960: isset($task['uid']) ? $task['uid'] : null,
961: isset($task['parent_id']) ? $task['parent_id'] : '',
962: !empty($task['private']),
963: $GLOBALS['registry']->getAuth(),
964: isset($task['assignee']) ? $task['assignee'] : null
965: );
966: }
967:
968: 969: 970: 971: 972: 973: 974: 975: 976: 977: 978:
979: public function quickAdd($text, $tasklist = null)
980: {
981: if ($tasklist === null) {
982: $tasklist = Nag::getDefaultTasklist(Horde_Perms::EDIT);
983: } elseif (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
984: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
985: }
986:
987: return Nag::createTasksFromText($text, $tasklist);
988: }
989:
990: 991: 992: 993: 994: 995:
996: public function toggleCompletion($task_id, $tasklist_id)
997: {
998: if (!Nag::hasPermission($tasklist_id, Horde_Perms::EDIT)) {
999: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1000: }
1001:
1002: try {
1003: $share = $GLOBALS['nag_shares']->getShare($tasklist_id);
1004: } catch (Horde_Share_Exception $e) {
1005: Horde::logMessage($e->getMessage(), 'ERR');
1006: throw new Nag_Exception($e);
1007: }
1008: $task = Nag::getTask($tasklist_id, $task_id);
1009: $task->completed = !$task->completed;
1010: if ($task->completed) {
1011: $task->completed_date = time();
1012: } else {
1013: $task->completed_date = null;
1014: }
1015:
1016: return $task->save();
1017: }
1018:
1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032:
1033: public function export($uid, $contentType)
1034: {
1035: $storage = Nag_Driver::singleton();
1036: $task = $storage->getByUID($uid);
1037: if (!Nag::hasPermission($task->tasklist, Horde_Perms::READ)) {
1038: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1039: }
1040:
1041: $version = '2.0';
1042: switch ($contentType) {
1043: case 'text/x-vcalendar':
1044: $version = '1.0';
1045: case 'text/calendar':
1046:
1047: $iCal = new Horde_Icalendar($version);
1048: $iCal->setAttribute('PRODID', '-//The Horde Project//Nag ' . $GLOBALS['registry']->getVersion() . '//EN');
1049: $iCal->setAttribute('METHOD', 'PUBLISH');
1050:
1051:
1052: $vTodo = $task->toiCalendar($iCal);
1053: $vTodo->setAttribute('VERSION', $version);
1054:
1055: $iCal->addComponent($vTodo);
1056:
1057: return $iCal->exportvCalendar();
1058: case 'activesync':
1059: return $task->toASTask();
1060: default:
1061: throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
1062: }
1063: }
1064:
1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072:
1073: public function getTask($tasklist, $id)
1074: {
1075: if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
1076: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1077: }
1078:
1079: return Nag::getTask($tasklist, $id);
1080: }
1081:
1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096:
1097: public function exportTasklist($tasklist, $contentType)
1098: {
1099: if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
1100: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1101: }
1102:
1103: $tasks = Nag::listTasks(null, null, null, array($tasklist), 1);
1104:
1105: $version = '2.0';
1106: switch ($contentType) {
1107: case 'text/x-vcalendar':
1108: $version = '1.0';
1109: case 'text/calendar':
1110: $share = $GLOBALS['nag_shares']->getShare($tasklist);
1111:
1112: $iCal = new Horde_Icalendar($version);
1113: $iCal->setAttribute('X-WR-CALNAME', $share->get('name'));
1114:
1115: $tasks->reset();
1116: while ($task = $tasks->each()) {
1117: $iCal->addComponent($task->toiCalendar($iCal));
1118: }
1119:
1120: return $iCal->exportvCalendar();
1121: }
1122:
1123: throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
1124:
1125: }
1126:
1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134:
1135: public function delete($uid)
1136: {
1137:
1138: if (is_array($uid)) {
1139: foreach ($uid as $g) {
1140: $result = $this->delete($g);
1141: }
1142:
1143: return true;
1144: }
1145:
1146: $storage = Nag_Driver::singleton();
1147: $task = $storage->getByUID($uid);
1148:
1149: if (!$GLOBALS['registry']->isAdmin() &&
1150: !Nag::hasPermission($task->tasklist, Horde_Perms::DELETE)) {
1151:
1152: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1153: }
1154:
1155: return $storage->delete($task->id);
1156: }
1157:
1158: 1159: 1160: 1161: 1162: 1163:
1164: public function deleteTask($tasklist, $id)
1165: {
1166: if (!$GLOBALS['registry']->isAdmin() &&
1167: !Nag::hasPermission($tasklist, Horde_Perms::DELETE)) {
1168:
1169: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1170: }
1171:
1172: $storage = Nag_Driver::singleton($tasklist);
1173: return $storage->delete($id);
1174: }
1175:
1176: 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: 1185: 1186: 1187: 1188: 1189: 1190: 1191: 1192:
1193: public function replace($uid, $content, $contentType)
1194: {
1195: $storage = Nag_Driver::singleton();
1196: $existing = $storage->getByUID($uid);
1197: $taskId = $existing->id;
1198: if (!Nag::hasPermission($existing->tasklist, Horde_Perms::EDIT)) {
1199: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1200: }
1201:
1202: switch ($contentType) {
1203: case 'text/calendar':
1204: case 'text/x-vcalendar':
1205: if (!($content instanceof Horde_Icalendar_Vtodo)) {
1206: $iCal = new Horde_Icalendar();
1207: if (!$iCal->parsevCalendar($content)) {
1208: throw new Nag_Exception(_("There was an error importing the iCalendar data."));
1209: }
1210:
1211: $components = $iCal->getComponents();
1212: $component = null;
1213: foreach ($components as $content) {
1214: if ($content instanceof Horde_Icalendar_Vtodo) {
1215: if ($component !== null) {
1216: throw new Nag_Exception(_("Multiple iCalendar components found; only one vTodo is supported."));
1217: }
1218: $component = $content;
1219: }
1220:
1221: }
1222: if ($component === null) {
1223: throw new Nag_Exception(_("No iCalendar data was found."));
1224: }
1225: }
1226:
1227: $task = new Nag_Task();
1228: $task->fromiCalendar($content);
1229: $result = $storage->modify(
1230: $taskId,
1231: isset($task->name) ? $task->name : $existing->name,
1232: isset($task->desc) ? $task->desc : $existing->desc,
1233: isset($task->start) ? $task->start : $existing->start,
1234: isset($task->due) ? $task->due : $existing->due,
1235: isset($task->priority) ? $task->priority : $existing->priority,
1236: isset($task->estimate) ? $task->estimate : 0,
1237: isset($task->completed) ? (int)$task->completed : $existing->completed,
1238: isset($task->category) ? $task->category : $existing->category,
1239: isset($task->alarm) ? $task->alarm : $existing->alarm,
1240: isset($task->parent_id) ? $task->parent_id : $existing->parent_id,
1241: isset($task->private) ? $task->private : $existing->private,
1242: isset($task->owner) ? $task->owner : $existing->owner,
1243: isset($task->assignee) ? $task->assignee : $existing->assignee
1244: );
1245: break;
1246:
1247: case 'activesync':
1248: $task = new Nag_Task();
1249: $task->fromASTask($content);
1250: $result = $storage->modify(
1251: $taskId,
1252: isset($task->name) ? $task->name : $existing->name,
1253: isset($task->desc) ? $task->desc : $existing->desc,
1254: isset($task->start) ? $task->start : $existing->start,
1255: isset($task->due) ? $task->due : $existing->due,
1256: isset($task->priority) ? $task->priority : $existing->priority,
1257: isset($task->estimate) ? $task->estimate : 0,
1258: isset($task->completed) ? (int)$task->completed : $existing->completed,
1259: isset($task->category) ? $task->category : $existing->category,
1260: isset($task->alarm) ? $task->alarm : $existing->alarm,
1261: isset($task->parent_id) ? $task->parent_id : $existing->parent_id,
1262: isset($task->private) ? $task->private : $existing->private,
1263: isset($task->owner) ? $task->owner : $existing->owner,
1264: isset($task->assignee) ? $task->assignee : $existing->assignee
1265: );
1266: break;
1267: default:
1268: throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
1269: }
1270:
1271: return $result;
1272: }
1273:
1274: 1275: 1276: 1277: 1278: 1279: 1280:
1281: public function updateTask($tasklist, $id, $task)
1282: {
1283: if (!$GLOBALS['registry']->isAdmin() &&
1284: !Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
1285: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1286: }
1287:
1288: $storage = Nag_Driver::singleton($tasklist);
1289: $existing = $storage->get($id);
1290:
1291: return $storage->modify(
1292: $id,
1293: isset($task['name']) ? $task['name'] : $existing->name,
1294: isset($task['desc']) ? $task['desc'] : $existing->desc,
1295: isset($task['start']) ? $task['start'] : $existing->start,
1296: isset($task['due']) ? $task['due'] : $existing->due,
1297: isset($task['priority']) ? $task['priority'] : $existing->priority,
1298: isset($task['estimate']) ? $task['estimate'] : $existing->estimate,
1299: isset($task['completed']) ? (int)$task['completed'] : $existing->completed,
1300: isset($task['category']) ? $task['category'] : $existing->category,
1301: isset($task['alarm']) ? $task['alarm'] : $existing->alarm,
1302: isset($task['methods']) ? $task['methods'] : $existing->methods,
1303: isset($task['parent_id']) ? $task['parent_id'] : $existing->parent_id,
1304: isset($task['private']) ? $task['private'] : $existing->private,
1305: isset($task['owner']) ? $task['owner'] : $existing->owner,
1306: isset($task['assignee']) ? $task['assignee'] : $existing->assignee,
1307: $task['completed'] && !$existing->completed ? date() : $existing->completed_date,
1308: isset($task['tasklist']) ? $task['tasklist'] : $existing->tasklist
1309: );
1310: }
1311:
1312: 1313: 1314: 1315: 1316: 1317: 1318:
1319: public function listCostObjects($criteria)
1320: {
1321: $tasks = Nag::listTasks(null, null, null, null, 1);
1322: $result = array();
1323: $tasks->reset();
1324: while ($task = $tasks->each()) {
1325: $result[$task->id] = array('id' => $task->id,
1326: 'active' => !$task->completed,
1327: 'name' => $task->name);
1328: if (!empty($task->estimate)) {
1329: $result[$task->id]['estimate'] = $task->estimate;
1330: }
1331: }
1332:
1333: if (count($result) == 0) {
1334: return array();
1335: } else {
1336: return array(array('category' => _("Tasks"),
1337: 'objects' => array_values($result)));
1338: }
1339: }
1340:
1341: public function listTimeObjectCategories()
1342: {
1343: $categories = array();
1344: $tasklists = Nag::listTasklists(false, Horde_Perms::SHOW | Horde_Perms::READ);
1345: foreach ($tasklists as $tasklistId => $tasklist) {
1346: $categories[$tasklistId] = $tasklist->get('name');
1347: }
1348: return $categories;
1349: }
1350:
1351: 1352: 1353: 1354: 1355: 1356: 1357: 1358:
1359: public function listTimeObjects($categories, $start, $end)
1360: {
1361: $allowed_tasklists = Nag::listTasklists(false, Horde_Perms::READ);
1362: foreach ($categories as $tasklist) {
1363: if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
1364: return PEAR::raiseError(_("Permission Denied"));
1365: }
1366: }
1367:
1368: $timeobjects = array();
1369: $start = new Horde_Date($start);
1370: $start_ts = $start->timestamp();
1371: $end = new Horde_Date($end);
1372: $end_ts = $end->timestamp();
1373:
1374:
1375: $tasks = Nag::listTasks(null, null, null, $categories, 0);
1376: $tasks->reset();
1377: while ($task = $tasks->each()) {
1378:
1379: if (!$task->due || $task->due + 1 < $start_ts || $task->due > $end_ts) {
1380: continue;
1381: }
1382: $due_date = date('Y-m-d\TH:i:s', $task->due);
1383: $timeobjects[$task->id] = array(
1384: 'id' => $task->id,
1385: 'title' => $task->name,
1386: 'description' => $task->desc,
1387: 'start' => $due_date,
1388: 'end' => $due_date,
1389: 'category' => $task->category,
1390: 'color' => $allowed_tasklists[$task->tasklist]->get('color'),
1391: 'owner' => $allowed_tasklists[$task->tasklist]->get('owner'),
1392: 'permissions' => $GLOBALS['nag_shares']->getPermissions($task->tasklist, $GLOBALS['registry']->getAuth()),
1393: 'variable_length' => false,
1394: 'params' => array(
1395: 'task' => $task->id,
1396: 'tasklist' => $task->tasklist,
1397: ),
1398: 'link' => Horde::url('view.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id)),
1399: 'edit_link' => Horde::url('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'modify_task')),
1400: 'delete_link' => Horde::url('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'delete_task')),
1401: 'ajax_link' => 'task:' . $task->tasklist . ':' . $task->id,
1402: );
1403: }
1404:
1405: return $timeobjects;
1406: }
1407:
1408: 1409: 1410: 1411: 1412: 1413: 1414: 1415:
1416: public function saveTimeObject(array $timeobject)
1417: {
1418: $storage = Nag_Driver::singleton();
1419: $existing = $storage->get($timeobject['id']);
1420: if (!Nag::hasPermission($existing->tasklist, Horde_Perms::EDIT)) {
1421: throw new Horde_Exception_PermissionDenied(_("Permission Denied"));
1422: }
1423: $storage = Nag_Driver::singleton($existing->tasklist);
1424: if (isset($timeobject['start'])) {
1425: $due = new Horde_Date($timeobject['start']);
1426: $due = $due->timestamp();
1427: } else {
1428: $due = $existing->due;
1429: }
1430:
1431: return $storage->modify(
1432: $timeobject['id'],
1433: isset($timeobject['title']) ? $timeobject['title'] : $existing->name,
1434: isset($timeobject['description']) ? $timeobject['description'] : $existing->desc,
1435: $existing->start,
1436: $due,
1437: $existing->priority,
1438: $existing->estimate,
1439: $existing->completed,
1440: $existing->category,
1441: $existing->alarm,
1442: $existing->methods,
1443: $existing->parent_id,
1444: $existing->private,
1445: $existing->owner,
1446: $existing->assignee,
1447: $existing->completed_date,
1448: $existing->tasklist
1449: );
1450: }
1451:
1452: }
1453: