Overview

Packages

  • Nag
  • None

Classes

  • Horde_Core_Ui_VarRenderer_Nag
  • Nag
  • Nag_Ajax_Application
  • Nag_Api
  • Nag_Driver
  • Nag_Driver_Kolab
  • Nag_Driver_Sql
  • Nag_Exception
  • Nag_Factory_Tasklists
  • Nag_Form_CreateTaskList
  • Nag_Form_DeleteTaskList
  • Nag_Form_EditTaskList
  • Nag_Form_Renderer_Task
  • Nag_Form_Task
  • Nag_Form_Type_NagAlarm
  • Nag_Form_Type_NagDue
  • Nag_Form_Type_NagMethod
  • Nag_Form_Type_NagStart
  • Nag_Task
  • Nag_Tasklists_Base
  • Nag_Tasklists_Default
  • Nag_Tasklists_Kolab
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Nag_Task handles as single task as well as a list of tasks and implements a
   4:  * recursive iterator to handle a (hierarchical) list of tasks.
   5:  *
   6:  * See the enclosed file COPYING for license information (GPL). If you
   7:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   8:  *
   9:  * @author  Jan Schneider <jan@horde.org>
  10:  * @package Nag
  11:  */
  12: class Nag_Task
  13: {
  14:     /**
  15:      * The task id.
  16:      *
  17:      * @var string
  18:      */
  19:     public $id;
  20: 
  21:     /**
  22:      * This task's tasklist id.
  23:      *
  24:      * @var string
  25:      */
  26:     public $tasklist;
  27: 
  28:     /**
  29:      * The task uid.
  30:      *
  31:      * @var string
  32:      */
  33:     public $uid;
  34: 
  35:     /**
  36:      * The task owner.
  37:      *
  38:      * @var string
  39:      */
  40:     public $owner;
  41: 
  42:     /**
  43:      * The task assignee.
  44:      *
  45:      * @var string
  46:      */
  47:     public $assignee;
  48: 
  49:     /**
  50:      * The task title.
  51:      *
  52:      * @var string
  53:      */
  54:     public $name;
  55: 
  56:     /**
  57:      * The task decription.
  58:      *
  59:      * @var string
  60:      */
  61:     public $desc;
  62: 
  63:     /**
  64:      * The start date timestamp.
  65:      *
  66:      * @var integer
  67:      */
  68:     public $start;
  69: 
  70:     /**
  71:      * The due date timestamp.
  72:      *
  73:      * @var integer
  74:      */
  75:     public $due;
  76: 
  77:     /**
  78:      * The task priority.
  79:      *
  80:      * @var integer
  81:      */
  82:     public $priority;
  83: 
  84:     /**
  85:      * The estimated task length.
  86:      *
  87:      * @var float
  88:      */
  89:     public $estimate;
  90: 
  91:     /**
  92:      * Whether the task is completed.
  93:      *
  94:      * @var boolean
  95:      */
  96:     public $completed;
  97: 
  98:     /**
  99:      * The completion date timestamp.
 100:      *
 101:      * @var integer
 102:      */
 103:     public $completed_date;
 104: 
 105:     /**
 106:      * The task category
 107:      *
 108:      * @var string
 109:      */
 110:     public $category;
 111: 
 112:     /**
 113:      * The task alarm threshold.
 114:      *
 115:      * @var integer
 116:      */
 117:     public $alarm;
 118: 
 119:     /**
 120:      * The particular alarm methods overridden for this task.
 121:      *
 122:      * @var array
 123:      */
 124:     public $methods;
 125: 
 126:     /**
 127:      * Whether the task is private.
 128:      *
 129:      * @var boolean
 130:      */
 131:     public $private;
 132: 
 133:     /**
 134:      * URL to view the task.
 135:      *
 136:      * @var string
 137:      */
 138:     public $view_link;
 139: 
 140:     /**
 141:      * URL to complete the task.
 142:      *
 143:      * @var string
 144:      */
 145:     public $complete_link;
 146: 
 147:     /**
 148:      * URL to edit the task.
 149:      *
 150:      * @var string
 151:      */
 152:     public $edit_link;
 153: 
 154:     /**
 155:      * URL to delete the task.
 156:      *
 157:      * @var string
 158:      */
 159:     public $delete_link;
 160: 
 161:     /**
 162:      * The parent task's id.
 163:      *
 164:      * @var string
 165:      */
 166:     public $parent_id = '';
 167: 
 168:     /**
 169:      * The parent task.
 170:      *
 171:      * @var Nag_Task
 172:      */
 173:     public $parent;
 174: 
 175:     /**
 176:      * The sub-tasks.
 177:      *
 178:      * @var array
 179:      */
 180:     public $children = array();
 181: 
 182:     /**
 183:      * This task's idention (child) level.
 184:      *
 185:      * @var integer
 186:      */
 187:     public $indent = 0;
 188: 
 189:     /**
 190:      * Whether this is the last sub-task.
 191:      *
 192:      * @var boolean
 193:      */
 194:     public $lastChild;
 195: 
 196:     /**
 197:      * Internal flag.
 198:      *
 199:      * @var boolean
 200:      * @see each()
 201:      */
 202:     protected $_inlist = false;
 203: 
 204:     /**
 205:      * Internal pointer.
 206:      *
 207:      * @var integer
 208:      * @see each()
 209:      */
 210:     protected $_pointer;
 211: 
 212:     /**
 213:      * Task id => pointer dictionary.
 214:      *
 215:      * @var array
 216:      */
 217:     protected $_dict = array();
 218: 
 219:     /**
 220:      * Constructor.
 221:      *
 222:      * Takes a hash and returns a nice wrapper around it.
 223:      *
 224:      * @param array $task  A task hash.
 225:      */
 226:     public function __construct(array $task = null)
 227:     {
 228:         if ($task) {
 229:             $this->merge($task);
 230:         }
 231:     }
 232: 
 233:     /**
 234:      * Merges a task hash into this task object.
 235:      *
 236:      * @param array $task  A task hash.
 237:      */
 238:     public function merge(array $task)
 239:     {
 240:         foreach ($task as $key => $val) {
 241:             if ($key == 'tasklist_id') {
 242:                 $key = 'tasklist';
 243:             } elseif ($key == 'task_id') {
 244:                 $key = 'id';
 245:             } elseif ($key == 'parent') {
 246:                 $key = 'parent_id';
 247:             }
 248:             $this->$key = $val;
 249:         }
 250:     }
 251: 
 252:     /**
 253:      * Saves this task in the storage backend.
 254:      */
 255:     public function save()
 256:     {
 257:         $storage = Nag_Driver::singleton($this->tasklist);
 258:         return $storage->modify($this->id,
 259:                                 $this->name,
 260:                                 $this->desc,
 261:                                 $this->start,
 262:                                 $this->due,
 263:                                 $this->priority,
 264:                                 $this->estimate,
 265:                                 $this->completed,
 266:                                 $this->category,
 267:                                 $this->alarm,
 268:                                 $this->methods,
 269:                                 $this->parent_id,
 270:                                 $this->private,
 271:                                 $this->owner,
 272:                                 $this->assignee,
 273:                                 $this->completed_date);
 274:     }
 275: 
 276:     /**
 277:      * Returns the parent task of this task, if one exists.
 278:      *
 279:      * @return mixed  The parent task, null if none exists
 280:      */
 281:     public function getParent()
 282:     {
 283:         if (!$this->parent_id) {
 284:             return null;
 285:         }
 286:         return Nag::getTask($this->tasklist, $this->parent_id);
 287:     }
 288: 
 289:     /**
 290:      * Adds a sub task to this task.
 291:      *
 292:      * @param Nag_Task $task  A sub task.
 293:      */
 294:     public function add(Nag_Task $task)
 295:     {
 296:         $this->_dict[$task->id] = count($this->children);
 297:         $task->parent = $this;
 298:         $this->children[] = $task;
 299:     }
 300: 
 301:     /**
 302:      * Loads all sub-tasks.
 303:      */
 304:     public function loadChildren()
 305:     {
 306:         $storage = Nag_Driver::singleton($this->tasklist);
 307:         try {
 308:             $this->children = $storage->getChildren($this->id);
 309:         } catch (Nag_Exception $e) {}
 310:     }
 311: 
 312:     /**
 313:      * Merges an array of tasks into this task's children.
 314:      *
 315:      * @param array $children  A list of Nag_Tasks.
 316:      *
 317:      */
 318:     public function mergeChildren(array $children)
 319:     {
 320:         for ($i = 0, $c = count($children); $i < $c; ++$i) {
 321:             $this->add($children[$i]);
 322:         }
 323:     }
 324: 
 325:     /**
 326:      * Returns a sub task by its id.
 327:      *
 328:      * The methods goes recursively through all sub tasks until it finds the
 329:      * searched task.
 330:      *
 331:      * @param string $key  A task id.
 332:      *
 333:      * @return Nag_Task  The searched task or null.
 334:      */
 335:     public function get($key)
 336:     {
 337:         return isset($this->_dict[$key]) ?
 338:             $this->children[$this->_dict[$key]] :
 339:             null;
 340:     }
 341: 
 342:     /**
 343:      * Returns whether this is a task (not a container) or contains any sub
 344:      * tasks.
 345:      *
 346:      * @return boolean  True if this is a task or has sub tasks.
 347:      */
 348:     public function hasTasks()
 349:     {
 350:         return ($this->id) ? true : $this->hasSubTasks();
 351:     }
 352: 
 353:     /**
 354:      * Returns whether this task contains any sub tasks.
 355:      *
 356:      * @return boolean  True if this task has sub tasks.
 357:      */
 358:     public function hasSubTasks()
 359:     {
 360:         foreach ($this->children as $task) {
 361:             if ($task->hasTasks()) {
 362:                 return true;
 363:             }
 364:         }
 365:         return false;
 366:     }
 367: 
 368:     /**
 369:      * Returns whether all sub tasks are completed.
 370:      *
 371:      * @return boolean  True if all sub tasks are completed.
 372:      */
 373:     public function childrenCompleted()
 374:     {
 375:         foreach ($this->children as $task) {
 376:             if (!$task->completed || !$task->childrenCompleted()) {
 377:                 return false;
 378:             }
 379:         }
 380:         return true;
 381:     }
 382: 
 383:     /**
 384:      * Returns the number of tasks including this and any sub tasks.
 385:      *
 386:      * @return integer  The number of tasks and sub tasks.
 387:      */
 388:     public function count()
 389:     {
 390:         $count = $this->id ? 1 : 0;
 391:         foreach ($this->children as $task) {
 392:             $count += $task->count();
 393:         }
 394:         return $count;
 395:     }
 396: 
 397:     /**
 398:      * Returns the estimated length for this and any sub tasks.
 399:      *
 400:      * @return integer  The estimated length sum.
 401:      */
 402:     public function estimation()
 403:     {
 404:         $estimate = $this->estimate;
 405:         foreach ($this->children as $task) {
 406:             $estimate += $task->estimation();
 407:         }
 408:         return $estimate;
 409:     }
 410: 
 411:     /**
 412:      * Format the description - link URLs, etc.
 413:      *
 414:      * @return string
 415:      */
 416:     public function getFormattedDescription()
 417:     {
 418:         $desc = $GLOBALS['injector']
 419:             ->getInstance('Horde_Core_Factory_TextFilter')
 420:             ->filter($this->desc,
 421:                      'text2html',
 422:                      array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
 423:         try {
 424:             return Horde::callHook('format_description', array($desc), 'nag');
 425:         } catch (Horde_Exception_HookNotSet $e) {
 426:             return $desc;
 427:         }
 428:     }
 429: 
 430:     /**
 431:      * Resets the tasks iterator.
 432:      *
 433:      * Call this each time before looping through the tasks.
 434:      *
 435:      * @see each()
 436:      */
 437:     public function reset()
 438:     {
 439:         foreach (array_keys($this->children) as $key) {
 440:             $this->children[$key]->reset();
 441:         }
 442:         $this->_pointer = 0;
 443:         $this->_inlist = false;
 444:     }
 445: 
 446:     /**
 447:      * Returns the next task iterating through all tasks and sub tasks.
 448:      *
 449:      * Call reset() each time before looping through the tasks:
 450:      * <code>
 451:      * $tasks->reset();
 452:      * while ($task = $tasks->each() {
 453:      *     ...
 454:      * }
 455:      *
 456:      * @see reset()
 457:      */
 458:     public function each()
 459:     {
 460:         if ($this->id && !$this->_inlist) {
 461:             $this->_inlist = true;
 462:             return $this;
 463:         }
 464:         if ($this->_pointer >= count($this->children)) {
 465:             $task = false;
 466:             return $task;
 467:         }
 468:         $next = $this->children[$this->_pointer]->each();
 469:         if ($next) {
 470:             return $next;
 471:         }
 472:         $this->_pointer++;
 473:         return $this->each();
 474:     }
 475: 
 476:     /**
 477:      * Processes a list of tasks by adding action links, obscuring details of
 478:      * private tasks and calculating indentation.
 479:      *
 480:      * @param integer $indent  The indention level of the tasks.
 481:      */
 482:     public function process($indent = 0)
 483:     {
 484:         /* Link cache. */
 485:         static $view_url_list, $task_url_list;
 486: 
 487:         /* Set indention. */
 488:         $this->indent = $indent;
 489:         if ($this->id) {
 490:             ++$indent;
 491:         }
 492: 
 493:         /* Process children. */
 494:         for ($i = 0, $c = count($this->children); $i < $c; ++$i) {
 495:             $this->children[$i]->process($indent);
 496:         }
 497: 
 498:         /* Mark last child. */
 499:         if (count($this->children)) {
 500:             $this->children[count($this->children) - 1]->lastChild = true;
 501:         }
 502: 
 503:         /* Only process further if this is really a (parent) task, not only a
 504:          * task list container. */
 505:         if (!$this->id) {
 506:             return;
 507:         }
 508: 
 509:         if (!isset($view_url_list[$this->tasklist])) {
 510:             $view_url_list[$this->tasklist] = Horde_Util::addParameter(Horde::url('view.php'), 'tasklist', $this->tasklist);
 511:             $task_url_list[$this->tasklist] = Horde_Util::addParameter(Horde::url('task.php'), 'tasklist', $this->tasklist);
 512:         }
 513: 
 514:         /* Obscure private tasks. */
 515:         if ($this->private && $this->owner != $GLOBALS['registry']->getAuth()) {
 516:             $this->name = _("Private Task");
 517:             $this->desc = '';
 518:             $this->category = _("Private");
 519:         }
 520: 
 521:         /* Create task links. */
 522:         $this->view_link = Horde_Util::addParameter($view_url_list[$this->tasklist], 'task', $this->id);
 523: 
 524:         $task_url_task = Horde_Util::addParameter($task_url_list[$this->tasklist], 'task', $this->id);
 525:         $this->complete_link = Horde_Util::addParameter($task_url_task, 'actionID', 'complete_task');
 526:         $this->edit_link = Horde_Util::addParameter($task_url_task, 'actionID', 'modify_task');
 527:         $this->delete_link = Horde_Util::addParameter($task_url_task, 'actionID', 'delete_task');
 528:     }
 529: 
 530:     /**
 531:      * Returns the HTML code for any tree icons, when displaying this task in
 532:      * a tree view.
 533:      *
 534:      * @return string  The HTML code for necessary tree icons.
 535:      */
 536:     public function treeIcons()
 537:     {
 538:         $html = '';
 539: 
 540:         $parent = $this->parent;
 541:         for ($i = 1; $i < $this->indent; ++$i) {
 542:             if ($parent && $parent->lastChild) {
 543:                 $html = Horde::img('tree/blank.png') . $html;
 544:             } else {
 545:                 $html = Horde::img('tree/line.png', '|') . $html;
 546:             }
 547:             $parent = $parent->parent;
 548:         }
 549:         if ($this->indent) {
 550:             if ($this->lastChild) {
 551:                 $html .= Horde::img($GLOBALS['registry']->nlsconfig->curr_rtl ? 'tree/rev-joinbottom.png' : 'tree/joinbottom.png', '\\');
 552:             } else {
 553:                 $html .= Horde::img($GLOBALS['registry']->nlsconfig->curr_rtl ? 'tree/rev-join.png' : 'tree/join.png', '+');
 554:             }
 555:         }
 556: 
 557:         return $html;
 558:     }
 559: 
 560:     /**
 561:      * Sorts sub tasks by the given criteria.
 562:      *
 563:      * @param string $sortby     The field by which to sort
 564:      *                           (Nag::SORT_PRIORITY, Nag::SORT_NAME
 565:      *                           Nag::SORT_DUE, Nag::SORT_COMPLETION).
 566:      * @param integer $sortdir   The direction by which to sort
 567:      *                           (Nag::SORT_ASCEND, Nag::SORT_DESCEND).
 568:      * @param string $altsortby  The secondary sort field.
 569:      */
 570:     public function sort($sortby, $sortdir, $altsortby)
 571:     {
 572:         /* Sorting criteria for the task list. */
 573:         $sort_functions = array(
 574:             Nag::SORT_PRIORITY => 'ByPriority',
 575:             Nag::SORT_NAME => 'ByName',
 576:             Nag::SORT_CATEGORY => 'ByCategory',
 577:             Nag::SORT_DUE => 'ByDue',
 578:             Nag::SORT_START => 'ByStart',
 579:             Nag::SORT_COMPLETION => 'ByCompletion',
 580:             Nag::SORT_ASSIGNEE => 'ByAssignee',
 581:             Nag::SORT_ESTIMATE => 'ByEstimate',
 582:             Nag::SORT_OWNER => 'ByOwner'
 583:         );
 584: 
 585:         /* Sort the array if we have a sort function defined for this
 586:          * field. */
 587:         if (isset($sort_functions[$sortby])) {
 588:             $prefix = ($sortdir == Nag::SORT_DESCEND) ? '_rsort' : '_sort';
 589:             usort($this->children, array('Nag', $prefix . $sort_functions[$sortby]));
 590:             if (isset($sort_functions[$altsortby]) && $altsortby !== $sortby) {
 591:                 $task_buckets = array();
 592:                 for ($i = 0, $c = count($this->children); $i < $c; ++$i) {
 593:                     if (!isset($task_buckets[$this->children[$i]->$sortby])) {
 594:                         $task_buckets[$this->children[$i]->$sortby] = array();
 595:                     }
 596:                     $task_buckets[$this->children[$i]->$sortby][] = $this->children[$i];
 597:                 }
 598:                 $tasks = array();
 599:                 foreach ($task_buckets as $task_bucket) {
 600:                     usort($task_bucket, array('Nag', $prefix . $sort_functions[$altsortby]));
 601:                     $tasks = array_merge($tasks, $task_bucket);
 602:                 }
 603:                 $this->children = $tasks;
 604:             }
 605: 
 606:             /* Mark last child. */
 607:             for ($i = 0, $c = count($this->children); $i < $c; ++$i) {
 608:                 $this->children[$i]->lastChild = false;
 609:             }
 610:             if (count($this->children)) {
 611:                 $this->children[count($this->children) - 1]->lastChild = true;
 612:             }
 613: 
 614:             for ($i = 0, $c = count($this->children); $i < $c; ++$i) {
 615:                 $this->_dict[$this->children[$i]->id] = $i;
 616:                 $this->children[$i]->sort($sortby, $sortdir, $altsortby);
 617:             }
 618:         }
 619:     }
 620: 
 621:     /**
 622:      * Returns a hash representation for this task.
 623:      *
 624:      * @return array  A task hash.
 625:      */
 626:     public function toHash()
 627:     {
 628:         return array('tasklist_id' => $this->tasklist,
 629:                      'task_id' => $this->id,
 630:                      'uid' => $this->uid,
 631:                      'parent' => $this->parent_id,
 632:                      'owner' => $this->owner,
 633:                      'assignee' => $this->assignee,
 634:                      'name' => $this->name,
 635:                      'desc' => $this->desc,
 636:                      'category' => $this->category,
 637:                      'start' => $this->start,
 638:                      'due' => $this->due,
 639:                      'priority' => $this->priority,
 640:                      'estimate' => $this->estimate,
 641:                      'completed' => $this->completed,
 642:                      'completed_date' => $this->completed_date,
 643:                      'alarm' => $this->alarm,
 644:                      'methods' => $this->methods,
 645:                      'private' => $this->private);
 646:     }
 647: 
 648:     /**
 649:      * Returns a simple object suitable for json transport representing this
 650:      * task.
 651:      *
 652:      * @param boolean $full        Whether to return all event details.
 653:      * @param string $time_format  The date() format to use for time formatting.
 654:      *
 655:      * @return object  A simple object.
 656:      */
 657:     public function toJson($full = false, $time_format = 'H:i')
 658:     {
 659:         $json = new stdClass;
 660:         $json->l = $this->tasklist;
 661:         $json->n = $this->name;
 662:         if ($this->desc) {
 663:             //TODO: Get the proper amount of characters, and cut by last
 664:             //whitespace
 665:             $json->sd = Horde_String::substr($this->desc, 0, 80);
 666:         }
 667:         $json->cp = (boolean)$this->completed;
 668:         if ($this->due) {
 669:             $date = new Horde_Date($this->due);
 670:             $json->du = $date->toJson();
 671:         }
 672:         if ($this->start) {
 673:             $date = new Horde_Date($this->start);
 674:             $json->s = $date->toJson();
 675:         }
 676:         $json->pr = (int)$this->priority;
 677: 
 678:         if ($full) {
 679:             // @todo: do we really need all this?
 680:             $json->id = $this->id;
 681:             $json->de = $this->desc;
 682:             if ($this->due) {
 683:                 $date = new Horde_Date($this->due);
 684:                 $json->dd = $date->strftime('%x');
 685:                 $json->dt = $date->format($time_format);
 686:             }
 687:             /*
 688:             $json->p = $this->parent_id;
 689:             $json->o = $this->owner;
 690:             $json->as = $this->assignee;
 691:             if ($this->estimate) {
 692:                 $date = new Horde_Date($this->estimate);
 693:                 $json->e = $date->toJson();
 694:             }
 695:             if ($this->completed_date) {
 696:                 $date = new Horde_Date($this->completed_date);
 697:                 $json->cd = $date->toJson();
 698:             }
 699:             */
 700:             $json->a = (int)$this->alarm;
 701:             $json->m = $this->methods;
 702:             //$json->pv = (boolean)$this->private;
 703: 
 704:             try {
 705:                 $share = $GLOBALS['nag_shares']->getShare($this->tasklist);
 706:             } catch (Horde_Share_exception $e) {
 707:                 Horde::logMessage($e->getMessage(), 'ERR');
 708:                 throw new Nag_Exception($e);
 709:             }
 710:             $json->pe = $share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT);
 711:             $json->pd = $share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::DELETE);
 712:         }
 713: 
 714:         return $json;
 715:     }
 716: 
 717:     /**
 718:      * Returns an alarm hash of this task suitable for Horde_Alarm.
 719:      *
 720:      * @param string $user  The user to return alarms for.
 721:      * @param Prefs $prefs  A Prefs instance.
 722:      *
 723:      * @return array  Alarm hash or null.
 724:      */
 725:     public function toAlarm($user = null, $prefs = null)
 726:     {
 727:         if (empty($this->alarm) || $this->completed) {
 728:             return;
 729:         }
 730: 
 731:         if (empty($user)) {
 732:             $user = $GLOBALS['registry']->getAuth();
 733:         }
 734:         if (empty($prefs)) {
 735:             $prefs = $GLOBALS['prefs'];
 736:         }
 737: 
 738:         $methods = !empty($this->methods) ? $this->methods : @unserialize($prefs->getValue('task_alarms'));
 739:         if (!$methods) {
 740:             $methods = array();
 741:         }
 742: 
 743:         if (isset($methods['notify'])) {
 744:             $methods['notify']['show'] = array(
 745:                 '__app' => $GLOBALS['registry']->getApp(),
 746:                 'task' => $this->id,
 747:                 'tasklist' => $this->tasklist);
 748:             $methods['notify']['ajax'] = 'task:' . $this->tasklist . ':' . $this->id;
 749:             if (!empty($methods['notify']['sound'])) {
 750:                 if ($methods['notify']['sound'] == 'on') {
 751:                     // Handle boolean sound preferences;
 752:                     $methods['notify']['sound'] = (string)Horde_Themes::sound('theetone.wav');
 753:                 } else {
 754:                     // Else we know we have a sound name that can be
 755:                     // served from Horde.
 756:                     $methods['notify']['sound'] = (string)Horde_Themes::sound($methods['notify']['sound']);
 757:                 }
 758:             }
 759:         }
 760:         if (isset($methods['mail'])) {
 761:             $image = Nag::getImagePart('big_alarm.png');
 762: 
 763:             $view = new Horde_View(array('templatePath' => NAG_TEMPLATES . '/alarm', 'encoding' => 'UTF-8'));
 764:             new Horde_View_Helper_Text($view);
 765:             $view->task = $this;
 766:             $view->imageId = $image->getContentId();
 767:             $view->due = new Horde_Date($this->due);
 768:             $view->dateFormat = $prefs->getValue('date_format');
 769:             $view->timeFormat = $prefs->getValue('twentyFour') ? 'H:i' : 'h:ia';
 770:             if (!$prefs->isLocked('task_alarms')) {
 771:                 $view->prefsUrl = Horde::url(Horde::getServiceLink('prefs', 'nag'), true)->remove(session_name());
 772:             }
 773: 
 774:             $methods['mail']['mimepart'] = Nag::buildMimeMessage($view, 'mail', $image);
 775:         }
 776:         return array(
 777:             'id' => $this->uid,
 778:             'user' => $user,
 779:             'start' => new Horde_Date($this->due - $this->alarm * 60),
 780:             'methods' => array_keys($methods),
 781:             'params' => $methods,
 782:             'title' => $this->name,
 783:             'text' => $this->desc);
 784:     }
 785: 
 786:     /**
 787:      * Exports this task in iCalendar format.
 788:      *
 789:      * @param Horde_Icalendar $calendar  A Horde_Icalendar object that acts as
 790:      *                                   the container.
 791:      *
 792:      * @return Horde_Icalendar_Vtodo  A vtodo component of this task.
 793:      */
 794:     public function toiCalendar(Horde_Icalendar $calendar)
 795:     {
 796:         $vTodo = Horde_Icalendar::newComponent('vtodo', $calendar);
 797:         $v1 = $calendar->getAttribute('VERSION') == '1.0';
 798: 
 799:         $vTodo->setAttribute('UID', $this->uid);
 800: 
 801:         if (!empty($this->assignee)) {
 802:             $vTodo->setAttribute('ORGANIZER', $this->assignee);
 803:         }
 804: 
 805:         if (!empty($this->name)) {
 806:             $vTodo->setAttribute('SUMMARY', $this->name);
 807:         }
 808: 
 809:         if (!empty($this->desc)) {
 810:             $vTodo->setAttribute('DESCRIPTION', $this->desc);
 811:         }
 812: 
 813:         if (isset($this->priority)) {
 814:             $vTodo->setAttribute('PRIORITY', $this->priority);
 815:         }
 816: 
 817:         if (!empty($this->parent_id)) {
 818:             $vTodo->setAttribute('RELATED-TO', $this->parent->uid);
 819:         }
 820: 
 821:         if ($this->private) {
 822:             $vTodo->setAttribute('CLASS', 'PRIVATE');
 823:         }
 824: 
 825:         if (!empty($this->start)) {
 826:             $vTodo->setAttribute('DTSTART', $this->start);
 827:         }
 828: 
 829:         if ($this->due) {
 830:             $vTodo->setAttribute('DUE', $this->due);
 831: 
 832:             if ($this->alarm) {
 833:                 if ($v1) {
 834:                     $vTodo->setAttribute('AALARM', $this->due - $this->alarm * 60);
 835:                 } else {
 836:                     $vAlarm = Horde_Icalendar::newComponent('valarm', $vTodo);
 837:                     $vAlarm->setAttribute('ACTION', 'DISPLAY');
 838:                     $vAlarm->setAttribute('TRIGGER;VALUE=DURATION', '-PT' . $this->alarm . 'M');
 839:                     $vTodo->addComponent($vAlarm);
 840:                 }
 841:             }
 842:         }
 843: 
 844:         if ($this->completed) {
 845:             $vTodo->setAttribute('STATUS', 'COMPLETED');
 846:             $vTodo->setAttribute('COMPLETED', $this->completed_date ? $this->completed_date : time());
 847:         } else {
 848:             if ($v1) {
 849:                 $vTodo->setAttribute('STATUS', 'NEEDS ACTION');
 850:             } else {
 851:                 $vTodo->setAttribute('STATUS', 'NEEDS-ACTION');
 852:             }
 853:         }
 854: 
 855:         if (!empty($this->category)) {
 856:             $vTodo->setAttribute('CATEGORIES', $this->category);
 857:         }
 858: 
 859:         /* Get the task's history. */
 860:         $created = $modified = null;
 861:         try {
 862:             $log = $GLOBALS['injector']->getInstance('Horde_History')->getHistory('nag:' . $this->tasklist . ':' . $this->uid);
 863:             foreach ($log as $entry) {
 864:                 switch ($entry['action']) {
 865:                 case 'add':
 866:                     $created = $entry['ts'];
 867:                     break;
 868: 
 869:                 case 'modify':
 870:                     $modified = $entry['ts'];
 871:                     break;
 872:                 }
 873:             }
 874:         } catch (Exception $e) {}
 875:         if (!empty($created)) {
 876:             $vTodo->setAttribute($v1 ? 'DCREATED' : 'CREATED', $created);
 877:             if (empty($modified)) {
 878:                 $modified = $created;
 879:             }
 880:         }
 881:         if (!empty($modified)) {
 882:             $vTodo->setAttribute('LAST-MODIFIED', $modified);
 883:         }
 884: 
 885:         return $vTodo;
 886:     }
 887: 
 888:     /**
 889:      * Create an AS message from this task
 890:      *
 891:      * @return Horde_ActiveSync_Message_Task
 892:      */
 893:     function toASTask()
 894:     {
 895:         $message = new Horde_ActiveSync_Message_Task();
 896: 
 897:         /* Notes and Title */
 898:         $message->setBody($this->desc);
 899:         $message->setSubject($this->name);
 900: 
 901:         /* Completion */
 902:         if ($this->completed) {
 903:             $message->SetDateCompleted(new Horde_Date($this->completed_date));
 904:             $message->setComplete(Horde_ActiveSync_Message_Task::TASK_COMPLETE_TRUE);
 905:         } else {
 906:             $message->setComplete(Horde_ActiveSync_Message_Task::TASK_COMPLETE_FALSE);
 907:         }
 908: 
 909:         /* Due Date */
 910:         if (!empty($this->due)) {
 911:             $message->setDueDate(new Horde_Date($this->due));
 912:         }
 913: 
 914:         /* Start Date */
 915:         if (!empty($this->start)) {
 916:             $message->setStartDate(new Horde_Date($this->start));
 917:         }
 918: 
 919:         /* Priority */
 920:         switch ($this->priority) {
 921:         case 5:
 922:             $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_LOW;
 923:             break;
 924:         case 4:
 925:         case 3:
 926:         case 2:
 927:             $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL;
 928:             break;
 929:         case 1:
 930:             $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_HIGH;
 931:             break;
 932:         default:
 933:             $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL;
 934:         }
 935:         $message->setImportance($priority);
 936: 
 937:         /* Reminders */
 938:             if ($this->due && $this->alarm) {
 939:             $message->setReminder(new Horde_Date($this->due - $this->alarm));
 940:         }
 941: 
 942:         return $message;
 943:     }
 944: 
 945:     /**
 946:      * Creates a task from a Horde_Icalendar_Vtodo object.
 947:      *
 948:      * @param Horde_Icalendar_Vtodo $vTodo  The iCalendar data to update from.
 949:      */
 950:     function fromiCalendar(Horde_Icalendar_Vtodo $vTodo)
 951:     {
 952:         try {
 953:             $name = $vTodo->getAttribute('SUMMARY');
 954:             if (!is_array($name)) { $this->name = $name; }
 955:         } catch (Horde_Icalendar_Exception $e) {}
 956: 
 957:         try {
 958:             $assignee = $vTodo->getAttribute('ORGANIZER');
 959:             if (!is_array($assignee)) { $this->assignee = $assignee; }
 960:         } catch (Horde_Icalendar_Exception $e) {}
 961: 
 962:         try {
 963:             $uid = $vTodo->getAttribute('UID');
 964:             if (!is_array($uid)) { $this->uid = $uid; }
 965:         } catch (Horde_Icalendar_Exception $e) {}
 966: 
 967:         try {
 968:             $relations = $vTodo->getAttribute('RELATED-TO');
 969:             if (!is_array($relations)) {
 970:                 $relations = array($relations);
 971:             }
 972:             $params = $vTodo->getAttribute('RELATED-TO', true);
 973:             foreach ($relations as $id => $relation) {
 974:                 if (empty($params[$id]['RELTYPE']) ||
 975:                     Horde_String::upper($params[$id]['RELTYPE']) == 'PARENT') {
 976: 
 977:                     $storage = Nag_Driver::singleton($this->tasklist);
 978:                     $parent = $storage->getByUID($relation);
 979:                     $this->parent_id = $parent->id;
 980:                     break;
 981:                 }
 982:             }
 983:         } catch (Horde_Icalendar_Exception $e) {}
 984: 
 985:         try {
 986:             $start = $vTodo->getAttribute('DTSTART');
 987:             if (!is_array($start)) {
 988:                 // Date-Time field
 989:                 $this->start = $start;
 990:             } else {
 991:                 // Date field
 992:                 $this->start = mktime(0, 0, 0, (int)$start['month'], (int)$start['mday'], (int)$start['year']);
 993:             }
 994:         } catch (Horde_Icalendar_Exception $e) {}
 995: 
 996:         try {
 997:             $due = $vTodo->getAttribute('DUE');
 998:             if (is_array($due)) {
 999:                 $this->due = mktime(0, 0, 0, (int)$due['month'], (int)$due['mday'], (int)$due['year']);
1000:             } elseif (!empty($due)) {
1001:                 $this->due = $due;
1002:             }
1003:         } catch (Horde_Icalendar_Exception $e) {}
1004: 
1005:         // vCalendar 1.0 alarms
1006:         try {
1007:             $alarm = $vTodo->getAttribute('AALARM');
1008:             if (!is_array($alarm) && !empty($alarm) && !empty($this->due)) {
1009:                 $this->alarm = intval(($this->due - $alarm) / 60);
1010:                 if ($this->alarm === 0) {
1011:                     // We don't support alarms exactly at due date.
1012:                     $this->alarm = 1;
1013:                 }
1014:             }
1015:         } catch (Horde_Icalendar_Exception $e) {}
1016: 
1017:         // @TODO: vCalendar 2.0 alarms
1018: 
1019:         try {
1020:             $desc = $vTodo->getAttribute('DESCRIPTION');
1021:             if (!is_array($desc)) { $this->desc = $desc; }
1022:         } catch (Horde_Icalendar_Exception $e) {}
1023: 
1024:         try {
1025:             $priority = $vTodo->getAttribute('PRIORITY');
1026:             if (!is_array($priority)) { $this->priority = $priority; }
1027:         } catch (Horde_Icalendar_Exception $e) {}
1028: 
1029:         try {
1030:             $cat = $vTodo->getAttribute('CATEGORIES');
1031:             if (!is_array($cat)) { $this->category = $cat; }
1032:         } catch (Horde_Icalendar_Exception $e) {}
1033: 
1034:         try {
1035:             $status = $vTodo->getAttribute('STATUS');
1036:             if (!is_array($status)) { $this->completed = !strcasecmp($status, 'COMPLETED'); }
1037:         } catch (Horde_Icalendar_Exception $e) {}
1038: 
1039:         try {
1040:             $class = $vTodo->getAttribute('CLASS');
1041:             if (!is_array($class)) {
1042:                 $class = Horde_String::upper($class);
1043:                 $this->private = $class == 'PRIVATE' || $class == 'CONFIDENTIAL';
1044:             }
1045:         } catch (Horde_Icalendar_Exception $e) {}
1046:     }
1047: 
1048:     /**
1049:      * Create a nag Task object from an activesync message
1050:      *
1051:      * @param Horde_ActiveSync_Message_Task $message  The task object
1052:      */
1053:     function fromASTask(Horde_ActiveSync_Message_Task $message)
1054:     {
1055:         /* Notes and Title */
1056:         $this->desc = $message->getBody();
1057:         $this->name = $message->getSubject();
1058: 
1059:         /* Completion */
1060:         if ($this->completed = $message->getComplete()) {
1061:             $dateCompleted = $message->getDateCompleted();
1062:             $this->completed_date = empty($dateCompleted) ? null : $dateCompleted;
1063:         }
1064: 
1065:         /* Due Date */
1066:         if ($due = $message->getDueDate()) {
1067:             $this->due = $due->timestamp();
1068:         }
1069: 
1070:         /* Start Date */
1071:         if ($start = $message->getStartDate()) {
1072:             $this->start = $start->timestamp();
1073:         }
1074: 
1075:         /* Priority */
1076:         switch ($message->getImportance()) {
1077:         case Horde_ActiveSync_Message_Task::IMPORTANCE_LOW;
1078:             $this->priority = 5;
1079:             break;
1080:         case Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL;
1081:             $this->priority = 3;
1082:             break;
1083:         case Horde_ActiveSync_Message_Task::IMPORTANCE_HIGH;
1084:             $this->priority = 1;
1085:             break;
1086:         default:
1087:             $this->priority = 3;
1088:         }
1089: 
1090:         if (($alarm = $message->getReminder()) && $this->due) {
1091:             $this->alarm = $this->due - $alarm->timestamp();
1092:         }
1093: 
1094:         $this->tasklist = $GLOBALS['prefs']->getValue('default_tasklist');
1095:     }
1096: 
1097:     /**
1098:      * CSS formatting.
1099:      *
1100:      * @return string  CSS formatting.
1101:      */
1102:     public function getCssStyle()
1103:     {
1104:         $cManager = new Horde_Prefs_CategoryManager();
1105:         $colors = $cManager->colors();
1106:         if (!isset($colors[$this->category])) {
1107:             return '';
1108:         }
1109:         $fgColors = $cManager->fgColors();
1110: 
1111:         return 'color:' . (isset($fgColors[$this->category]) ? $fgColors[$this->category] : $fgColors['_default_']) . ';' .
1112:             'background:' . $colors[$this->category] . ';';
1113:     }
1114: 
1115: }
1116: 
API documentation generated by ApiGen