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 Base Class.
   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  Chuck Hagenbuch <chuck@horde.org>
  10:  * @author  Jan Schneider <jan@horde.org>
  11:  * @package Nag
  12:  */
  13: class Nag
  14: {
  15:     /**
  16:      * Sort by task name.
  17:      */
  18:     const SORT_NAME = 'name';
  19: 
  20:     /**
  21:      * Sort by priority.
  22:      */
  23:     const SORT_PRIORITY = 'priority';
  24: 
  25:     /**
  26:      * Sort by due date.
  27:      */
  28:     const SORT_DUE = 'due';
  29: 
  30:     /**
  31:      * Sort by start date.
  32:      */
  33:     const SORT_START = 'start';
  34: 
  35:     /**
  36:      * Sort by completion.
  37:      */
  38:     const SORT_COMPLETION = 'completed';
  39: 
  40:     /**
  41:      * Sort by category.
  42:      */
  43:     const SORT_CATEGORY = 'category';
  44: 
  45:     /**
  46:      * Sort by owner.
  47:      */
  48:     const SORT_OWNER = 'tasklist';
  49: 
  50:     /**
  51:      * Sort by estimate.
  52:      */
  53:     const SORT_ESTIMATE = 'estimate';
  54: 
  55:     /**
  56:      * Sort by assignee.
  57:      */
  58:     const SORT_ASSIGNEE = 'assignee';
  59: 
  60:     /**
  61:      * Sort in ascending order.
  62:      */
  63:     const SORT_ASCEND = 0;
  64: 
  65:     /**
  66:      * Sort in descending order.
  67:      */
  68:     const SORT_DESCEND = 1;
  69: 
  70:     /**
  71:      * Incomplete tasks
  72:      */
  73:     const VIEW_INCOMPLETE = 0;
  74: 
  75:     /**
  76:      * All tasks
  77:      */
  78:     const VIEW_ALL = 1;
  79: 
  80:     /**
  81:      * Complete tasks
  82:      */
  83:     const VIEW_COMPLETE = 2;
  84: 
  85:     /**
  86:      * Future tasks
  87:      */
  88:     const VIEW_FUTURE = 3;
  89: 
  90:     /**
  91:      * Future and incompleted tasks
  92:      */
  93:     const VIEW_FUTURE_INCOMPLETE = 4;
  94: 
  95:     /**
  96:      *
  97:      * @param integer $seconds
  98:      *
  99:      * @return string
 100:      */
 101:     static public function secondsToString($seconds)
 102:     {
 103:         $hours = floor($seconds / 3600);
 104:         $minutes = ($seconds / 60) % 60;
 105: 
 106:         if ($hours > 1) {
 107:             if ($minutes == 0) {
 108:                 return sprintf(_("%d hours"), $hours);
 109:             } elseif ($minutes == 1) {
 110:                 return sprintf(_("%d hours, %d minute"), $hours, $minutes);
 111:             } else {
 112:                 return sprintf(_("%d hours, %d minutes"), $hours, $minutes);
 113:             }
 114:         } elseif ($hours == 1) {
 115:             if ($minutes == 0) {
 116:                 return sprintf(_("%d hour"), $hours);
 117:             } elseif ($minutes == 1) {
 118:                 return sprintf(_("%d hour, %d minute"), $hours, $minutes);
 119:             } else {
 120:                 return sprintf(_("%d hour, %d minutes"), $hours, $minutes);
 121:             }
 122:         } else {
 123:             if ($minutes == 0) {
 124:                 return _("no time");
 125:             } elseif ($minutes == 1) {
 126:                 return sprintf(_("%d minute"), $minutes);
 127:             } else {
 128:                 return sprintf(_("%d minutes"), $minutes);
 129:             }
 130:         }
 131:     }
 132: 
 133:     /**
 134:      * Parses a complete date-time string into a Horde_Date object.
 135:      *
 136:      * @param string $date       The date-time string to parse.
 137:      * @param boolean $withtime  Whether time is included in the string.
 138:      *
 139:      * @return Horde_Date  The parsed date.
 140:      * @throws Horde_Date_Exception
 141:      */
 142:     static public function parseDate($date, $withtime = true)
 143:     {
 144:         // strptime() is not available on Windows.
 145:         if (!function_exists('strptime')) {
 146:             return new Horde_Date($date);
 147:         }
 148: 
 149:         // strptime() is locale dependent, i.e. %p is not always matching
 150:         // AM/PM. Set the locale to C to workaround this, but grab the
 151:         // locale's D_FMT before that.
 152:         $format = Horde_Nls::getLangInfo(D_FMT);
 153:         if ($withtime) {
 154:             $format .= ' '
 155:                 . ($GLOBALS['prefs']->getValue('twentyFour') ? '%H:%M' : '%I:%M %p');
 156:         }
 157:         $old_locale = setlocale(LC_TIME, 0);
 158:         setlocale(LC_TIME, 'C');
 159: 
 160:         // Try exact format match first.
 161:         $date_arr = strptime($date, $format);
 162:         setlocale(LC_TIME, $old_locale);
 163: 
 164:         if (!$date_arr) {
 165:             // Try with locale dependent parsing next.
 166:             $date_arr = strptime($date, $format);
 167:             if (!$date_arr) {
 168:                 // Try throwing at Horde_Date finally.
 169:                 return new Horde_Date($date);
 170:             }
 171:         }
 172: 
 173:         return new Horde_Date(
 174:             array('year'  => $date_arr['tm_year'] + 1900,
 175:                   'month' => $date_arr['tm_mon'] + 1,
 176:                   'mday'  => $date_arr['tm_mday'],
 177:                   'hour'  => $date_arr['tm_hour'],
 178:                   'min'   => $date_arr['tm_min'],
 179:                   'sec'   => $date_arr['tm_sec']));
 180:     }
 181: 
 182:     /**
 183:      * Retrieves the current user's task list from storage.
 184:      *
 185:      * This function will also sort the resulting list, if requested.
 186:      *
 187:      * @param string $sortby      The field by which to sort (Nag::SORT_*).
 188:      * @param integer $sortdir    The direction by which to sort
 189:      *                            (Nag::SORT_ASCEND, Nag::SORT_DESCEND).
 190:      * @param string $altsortby   The secondary sort field.
 191:      * @param array $tasklists    An array of tasklist to display or
 192:      *                            null/empty to display taskslists
 193:      *                            $GLOBALS['display_tasklists'].
 194:      * @param integer $completed  Which tasks to retrieve (1 = all tasks,
 195:      *                            0 = incomplete tasks, 2 = complete tasks,
 196:      *                            3 = future tasks, 4 = future and incomplete
 197:      *                            tasks).
 198:      *
 199:      * @return Nag_Task  A list of the requested tasks.
 200:      */
 201:     public static function listTasks($sortby = null,
 202:                                      $sortdir = null,
 203:                                      $altsortby = null,
 204:                                      array $tasklists = null,
 205:                                      $completed = null)
 206:     {
 207:         global $prefs, $registry;
 208: 
 209:         if (is_null($sortby)) {
 210:             $sortby = $prefs->getValue('sortby');
 211:         }
 212:         if (is_null($sortdir)) {
 213:             $sortdir = $prefs->getValue('sortdir');
 214:         }
 215:         if (is_null($altsortby)) {
 216:             $altsortby = $prefs->getValue('altsortby');
 217:         }
 218: 
 219:         if (is_null($tasklists)) {
 220:             $tasklists = $GLOBALS['display_tasklists'];
 221:         }
 222:         if (!is_array($tasklists)) {
 223:             $tasklists = array($tasklists);
 224:         }
 225:         if (is_null($completed)) {
 226:             $completed = $prefs->getValue('show_completed');
 227:         }
 228: 
 229:         $tasks = new Nag_Task();
 230:         foreach ($tasklists as $tasklist) {
 231:             /* Create a Nag storage instance. */
 232:             $storage = Nag_Driver::singleton($tasklist);
 233: 
 234:             /* Retrieve the tasklist from storage. */
 235:             $result = $storage->retrieve($completed);
 236:             $tasks->mergeChildren($storage->tasks->children);
 237:         }
 238: 
 239:         /* Process all tasks. */
 240:         $tasks->process();
 241: 
 242:         /* We look for registered apis that support listAs(taskHash). */
 243:         $apps = @unserialize($prefs->getValue('show_external'));
 244:         if (is_array($apps)) {
 245:             foreach ($apps as $app) {
 246:                 if ($app != 'nag' &&
 247:                     $registry->hasMethod('getListTypes', $app)) {
 248:                     try {
 249:                         $types = $registry->callByPackage($app, 'getListTypes');
 250:                     } catch (Horde_Exception $e) {
 251:                         continue;
 252:                     }
 253:                     if (!empty($types['taskHash'])) {
 254:                         try {
 255:                             $newtasks = $registry->callByPackage($app, 'listAs', array('taskHash'));
 256:                              foreach ($newtasks as $task) {
 257:                                 $task['tasklist_id'] = '**EXTERNAL**';
 258:                                 $task['tasklist_name'] = $registry->get('name', $app);
 259:                                 $tasks->add(new Nag_Task($task));
 260:                             }
 261:                         } catch (Horde_Exception $e) {
 262:                             Horde::logMessage($newtasks, 'ERR');
 263:                         }
 264:                     }
 265:                 }
 266:             }
 267:         }
 268: 
 269:         /* Sort the array. */
 270:         $tasks->sort($sortby, $sortdir, $altsortby);
 271: 
 272:         return $tasks;
 273:     }
 274: 
 275:     /**
 276:      * Returns a single task.
 277:      *
 278:      * @param string $tasklist  A tasklist.
 279:      * @param string $task      A task id.
 280:      *
 281:      * @return array  The task hash.
 282:      */
 283:     public static function getTask($tasklist, $task)
 284:     {
 285:         $storage = Nag_Driver::singleton($tasklist);
 286:         $task = $storage->get($task);
 287:         $task->process();
 288:         return $task;
 289:     }
 290: 
 291:     /**
 292:      * Returns the number of taks in task lists that the current user owns.
 293:      *
 294:      * @return integer  The number of tasks that the user owns.
 295:      */
 296:     public static function countTasks()
 297:     {
 298:         static $count;
 299:         if (isset($count)) {
 300:             return $count;
 301:         }
 302: 
 303:         $tasklists = self::listTasklists(true, Horde_Perms::ALL);
 304: 
 305:         $count = 0;
 306:         foreach (array_keys($tasklists) as $tasklist) {
 307:             /* Create a Nag storage instance. */
 308:             $storage = Nag_Driver::singleton($tasklist);
 309:             $storage->retrieve();
 310: 
 311:             /* Retrieve the task list from storage. */
 312:             $count += $storage->tasks->count();
 313:         }
 314: 
 315:         return $count;
 316:     }
 317: 
 318:     /**
 319:      * Imports one or more tasks parsed from a string.
 320:      *
 321:      * @param string $text      The text to parse into
 322:      * @param string $tasklist  The tasklist into which the task will be
 323:      *                          imported.  If 'null', the user's default
 324:      *                          tasklist will be used.
 325:      *
 326:      * @return array  The UIDs of all tasks that were added.
 327:      */
 328:     public static function createTasksFromText($text, $tasklist = null)
 329:     {
 330:         if ($tasklist === null) {
 331:             $tasklist = self::getDefaultTasklist(Horde_Perms::EDIT);
 332:         } elseif (!self::hasPermission($tasklist, Horde_Perms::EDIT)) {
 333:             return PEAR::raiseError(_("Permission Denied"));
 334:         }
 335: 
 336:         $storage = Nag_Driver::singleton($tasklist);
 337:         $dateParser = Horde_Date_Parser::factory(
 338:             array('locale' => $GLOBALS['prefs']->getValue('language')) );
 339: 
 340:         $quickParser = new Nag_QuickParser();
 341:         $tasks = $quickParser->parse($text);
 342: 
 343:         $uids = array();
 344:         foreach ($tasks as &$task) {
 345:             if (!is_array($task)) {
 346:                 $name = $task;
 347:                 $task = array($name);
 348:             }
 349: 
 350:             $r = $dateParser->parse($task[0], array('return' => 'result'));
 351:             if ($d = $r->guess()) {
 352:                 $name = $r->untaggedText();
 353:                 $due = $d->timestamp();
 354:             } else {
 355:                 $name = $task[0];
 356:                 $due = 0;
 357:             }
 358: 
 359:             if (isset($task['parent'])) {
 360:                 $newTask = $storage->add($name, '', 0, $due, 3, 0.0, 0, '', 0, null, null, $tasks[$task['parent']]['id']);
 361:             } else {
 362:                 $newTask = $storage->add($name, '', 0, $due, 3);
 363:             }
 364:             $uids[] = $newTask[1];
 365:             $task['id'] = $newTask[0];
 366:         }
 367: 
 368:         return $uids;
 369:     }
 370: 
 371:     /**
 372:      * Returns all the alarms active right on $date.
 373:      *
 374:      * @param integer $date     The unix epoch time to check for alarms.
 375:      * @param array $tasklists  An array of tasklists
 376:      *
 377:      * @return array  The alarms (taskId) active on $date.
 378:      */
 379:     public static function listAlarms($date, array $tasklists = null)
 380:     {
 381:         if (is_null($tasklists)) {
 382:             $tasklists = $GLOBALS['display_tasklists'];
 383:         }
 384: 
 385:         $tasks = array();
 386:         foreach ($tasklists as $tasklist) {
 387:             /* Create a Nag storage instance. */
 388:             $storage = Nag_Driver::singleton($tasklist);
 389: 
 390:             /* Retrieve the alarms for the task list. */
 391:             $newtasks = $storage->listAlarms($date);
 392: 
 393:             /* Don't show an alarm for complete tasks. */
 394:             foreach ($newtasks as $taskID => $task) {
 395:                 if (!empty($task->completed)) {
 396:                     unset($newtasks[$taskID]);
 397:                 }
 398:             }
 399: 
 400:             $tasks = array_merge($tasks, $newtasks);
 401:         }
 402: 
 403:         return $tasks;
 404:     }
 405: 
 406:     /**
 407:      * Lists all task lists a user has access to.
 408:      *
 409:      * This method takes the $conf['share']['hidden'] setting into account. If
 410:      * this setting is enabled, even if requesting permissions different than
 411:      * SHOW, it will only return calendars that the user owns or has SHOW
 412:      * permissions for. For checking individual calendar's permissions, use
 413:      * hasPermission() instead.
 414:      *
 415:      * @param boolean $owneronly  Only return tasklists that this user owns?
 416:      *                            Defaults to false.
 417:      * @param integer $permission The permission to filter tasklists by.
 418:      *
 419:      * @return array  The task lists.
 420:      */
 421:     public static function listTasklists($owneronly = false,
 422:                                          $permission = Horde_Perms::SHOW)
 423:     {
 424:         if ($owneronly && !$GLOBALS['registry']->getAuth()) {
 425:             return array();
 426:         }
 427: 
 428:         if ($owneronly || empty($GLOBALS['conf']['share']['hidden'])) {
 429:             try {
 430:                 $tasklists = $GLOBALS['nag_shares']->listShares(
 431:                     $GLOBALS['registry']->getAuth(),
 432:                     array('perm' => $permission,
 433:                           'attributes' => $owneronly ? $GLOBALS['registry']->getAuth() : null,
 434:                           'sort_by' => 'name'));
 435:             } catch (Horde_Share_Exception $e) {
 436:                 Horde::logMessage($e->getMessage(), 'ERR');
 437:                 return array();
 438:             }
 439:         } else {
 440:             try {
 441:                 $tasklists = $GLOBALS['nag_shares']->listShares(
 442:                     $GLOBALS['registry']->getAuth(),
 443:                     array('perm' => $permission,
 444:                           'attributes' => $GLOBALS['registry']->getAuth(),
 445:                           'sort_by' => 'name'));
 446:             } catch (Horde_Share_Exception $e) {
 447:                 Horde::logMessage($e);
 448:                 return array();
 449:             }
 450:             $display_tasklists = @unserialize($GLOBALS['prefs']->getValue('display_tasklists'));
 451:             if (is_array($display_tasklists)) {
 452:                 foreach ($display_tasklists as $id) {
 453:                     try {
 454:                         $tasklist = $GLOBALS['nag_shares']->getShare($id);
 455:                         if ($tasklist->hasPermission($GLOBALS['registry']->getAuth(), $permission)) {
 456:                             $tasklists[$id] = $tasklist;
 457:                         }
 458:                     } catch (Horde_Exception_NotFound $e) {
 459:                     } catch (Horde_Share_Exception $e) {
 460:                         Horde::logMessage($e);
 461:                         return array();
 462:                     }
 463:                 }
 464:             }
 465:         }
 466: 
 467:         return $tasklists;
 468:     }
 469: 
 470:     /**
 471:      * Filters data based on permissions.
 472:      *
 473:      * @param array $in            The data we want filtered.
 474:      * @param integer $permission  The Horde_Perms::* constant we will filter
 475:      *                             on.
 476:      *
 477:      * @return array  The filtered data.
 478:      */
 479:     public static function permissionsFilter(array $in, $permission = Horde_Perms::READ)
 480:     {
 481:         // FIXME: Must find a way to check individual tasklists for
 482:         // permission.  Can't specify attributes as it does not check for the
 483:         // 'key' attribute, only 'name' and 'value'.
 484:         return $in;
 485: 
 486:         // Broken code:
 487:         $out = array();
 488: 
 489:         foreach ($in as $sourceId => $source) {
 490:             if ($in->hasPermission($permission)) {
 491:                 $out[$sourceId] = $source;
 492:             }
 493:         }
 494: 
 495:         return $out;
 496:     }
 497: 
 498:     /**
 499:      * Returns whether the current user has certain permissions on a tasklist.
 500:      *
 501:      * @since Nag 3.0.3
 502:      *
 503:      * @param string $tasklist  A tasklist id.
 504:      * @param integer $perm     A Horde_Perms permission mask.
 505:      *
 506:      * @return boolean  True if the current user has the requested permissions.
 507:      */
 508:     static public function hasPermission($tasklist, $perm)
 509:     {
 510:         try {
 511:             $share = $GLOBALS['nag_shares']->getShare($tasklist);
 512:             if (!$share->hasPermission($GLOBALS['registry']->getAuth(), $perm)) {
 513:                 throw new Horde_Exception_NotFound();
 514:             }
 515:         } catch (Horde_Exception_NotFound $e) {
 516:             return false;
 517:         }
 518:         return true;
 519:     }
 520: 
 521:     /**
 522:      * Returns the default tasklist for the current user at the specified
 523:      * permissions level.
 524:      *
 525:      * @param integer $permission  The permission to require.
 526:      *
 527:      * @return mixed The default tasklist or false if none.
 528:      */
 529:     public static function getDefaultTasklist($permission = Horde_Perms::SHOW)
 530:     {
 531:         global $prefs;
 532: 
 533:         $default_tasklist = $prefs->getValue('default_tasklist');
 534:         $tasklists = self::listTasklists(false, $permission);
 535: 
 536:         if (isset($tasklists[$default_tasklist])) {
 537:             return $default_tasklist;
 538:         } elseif ($prefs->isLocked('default_tasklist')) {
 539:             return $GLOBALS['registry']->getAuth();
 540:         } elseif (count($tasklists)) {
 541:             reset($tasklists);
 542:             return key($tasklists);
 543:         }
 544: 
 545:         return false;
 546:     }
 547: 
 548:     /**
 549:      * Creates a new share.
 550:      *
 551:      * @param array $info  Hash with tasklist information.
 552:      *
 553:      * @return Horde_Share  The new share.
 554:      */
 555:     public static function addTasklist(array $info)
 556:     {
 557:         try {
 558:             $tasklist = $GLOBALS['nag_shares']->newShare($GLOBALS['registry']->getAuth(), strval(new Horde_Support_Randomid()), $info['name']);
 559:             $tasklist->set('color', $info['color']);
 560:             $tasklist->set('desc', $info['description']);
 561:             if (!empty($info['system'])) {
 562:                 $tasklist->set('owner', null);
 563:             }
 564: 
 565:             $GLOBALS['nag_shares']->addShare($tasklist);
 566:         } catch (Horde_Share_Exception $e) {
 567:             throw new Nag_Exception($e);
 568:         }
 569: 
 570:         $GLOBALS['display_tasklists'][] = $tasklist->getName();
 571:         $GLOBALS['prefs']->setValue('display_tasklists', serialize($GLOBALS['display_tasklists']));
 572: 
 573:         return $tasklist;
 574:     }
 575: 
 576:     /**
 577:      * Updates an existing share.
 578:      *
 579:      * @param Horde_Share_Object $tasklist  The share to update.
 580:      * @param array $info                   Hash with task list information.
 581:      *
 582:      * @throws Horde_Exception_PermissionDenied
 583:      * @throws Nag_Exception
 584:      */
 585:     public static function updateTasklist(Horde_Share_Object $tasklist, array $info)
 586:     {
 587:         if (!$GLOBALS['registry']->getAuth() ||
 588:             ($tasklist->get('owner') != $GLOBALS['registry']->getAuth() &&
 589:              (!is_null($tasklist->get('owner')) || !$GLOBALS['registry']->isAdmin()))) {
 590: 
 591:             throw new Horde_Exception_PermissionDenied(_("You are not allowed to change this task list."));
 592:         }
 593: 
 594:         $tasklist->set('name', $info['name']);
 595:         $tasklist->set('color', $info['color']);
 596:         $tasklist->set('desc', $info['description']);
 597:         $tasklist->set('owner', empty($info['system']) ? $GLOBALS['registry']->getAuth() : null);
 598: 
 599:         try {
 600:             $tasklist->save();
 601:         } catch (Horde_Share_Exception $e) {
 602:             throw new Nag_Exception(sprintf(_("Unable to save task list \"%s\": %s"), $info['name'], $e->getMessage()));
 603:         }
 604:     }
 605: 
 606:     /**
 607:      * Deletes a task list.
 608:      *
 609:      * @param Horde_Share_Object $tasklist  The task list to delete.
 610:      * @throws Nag_Exception
 611:      * @throws Horde_Exception_PermissionDenied
 612:      */
 613:     public static function deleteTasklist(Horde_Share_Object $tasklist)
 614:     {
 615:         if (!$GLOBALS['registry']->getAuth() ||
 616:             ($tasklist->get('owner') != $GLOBALS['registry']->getAuth() &&
 617:              (!is_null($tasklist->get('owner')) || !$GLOBALS['registry']->isAdmin()))) {
 618:             throw new Horde_Exception_PermissionDenied(_("You are not allowed to delete this task list."));
 619:         }
 620: 
 621:         // Delete the task list.
 622:         $storage = &Nag_Driver::singleton($tasklist->getName());
 623:         $result = $storage->deleteAll();
 624: 
 625:         // Remove share and all groups/permissions.
 626:         try {
 627:             return $GLOBALS['nag_shares']->removeShare($tasklist);
 628:         } catch (Horde_Share_Exception $e) {
 629:             throw new Nag_Exception($e);
 630:         }
 631:     }
 632: 
 633:     /**
 634:      * Builds the HTML for a priority selection widget.
 635:      *
 636:      * @param string $name       The name of the widget.
 637:      * @param integer $selected  The default selected priority.
 638:      *
 639:      * @return string  The HTML <select> widget.
 640:      */
 641:     public static function buildPriorityWidget($name, $selected = -1)
 642:     {
 643:         $descs = array(1 => _("(highest)"), 5 => _("(lowest)"));
 644: 
 645:         $html = "<select id=\"$name\" name=\"$name\">";
 646:         for ($priority = 1; $priority <= 5; $priority++) {
 647:             $html .= "<option value=\"$priority\"";
 648:             $html .= ($priority == $selected) ? ' selected="selected">' : '>';
 649:             $html .= $priority . ' ' . @$descs[$priority] . '</option>';
 650:         }
 651:         $html .= "</select>\n";
 652: 
 653:         return $html;
 654:     }
 655: 
 656:     /**
 657:      * Builds the HTML for a checkbox widget.
 658:      *
 659:      * @param string $name      The name of the widget.
 660:      * @param integer $checked  The default checkbox state.
 661:      *
 662:      * @return string  HTML for a checkbox representing the completion state.
 663:      */
 664:     public static function buildCheckboxWidget($name, $checked = 0)
 665:     {
 666:         $name = htmlspecialchars($name);
 667:         return "<input type=\"checkbox\" id=\"$name\" name=\"$name\"" .
 668:             ($checked ? ' checked="checked"' : '') . ' />';
 669:     }
 670: 
 671:     /**
 672:      * Formats the given Unix-style date string.
 673:      *
 674:      * @param string $unixdate  The Unix-style date value to format.
 675:      * @param boolean $hours    Whether to add hours.
 676:      *
 677:      * @return string  The formatted due date string.
 678:      */
 679:     public static function formatDate($unixdate = '', $hours = true)
 680:     {
 681:         global $prefs;
 682: 
 683:         if (empty($unixdate)) {
 684:             return '';
 685:         }
 686: 
 687:         $date = strftime($prefs->getValue('date_format'), $unixdate);
 688:         if (!$hours) {
 689:             return $date;
 690:         }
 691: 
 692:         return sprintf(_("%s at %s"),
 693:                        $date,
 694:                        strftime($prefs->getValue('twentyFour') ? '%H:%M' : '%I:%M %p', $unixdate));
 695:     }
 696: 
 697:     /**
 698:      * Returns the string representation of the given completion status.
 699:      *
 700:      * @param integer $completed  The completion value.
 701:      *
 702:      * @return string  The HTML representation of $completed.
 703:      */
 704:     public static function formatCompletion($completed)
 705:     {
 706:         return $completed ?
 707:             Horde::img('checked.png', _("Completed")) :
 708:             Horde::img('unchecked.png', _("Not Completed"));
 709:     }
 710: 
 711:     /**
 712:      * Returns a colored representation of a priority.
 713:      *
 714:      * @param integer $priority  The priority level.
 715:      *
 716:      * @return string  The HTML representation of $priority.
 717:      */
 718:     public static function formatPriority($priority)
 719:     {
 720:         return '<span class="pri-' . (int)$priority . '">' . (int)$priority .
 721:             '</span>';
 722:     }
 723: 
 724:     /**
 725:      * Returns the string matching the given alarm value.
 726:      *
 727:      * @param integer $value  The alarm value in minutes.
 728:      *
 729:      * @return string  The formatted alarm string.
 730:      */
 731:     public static function formatAlarm($value)
 732:     {
 733:         if ($value) {
 734:             if ($value % 10080 == 0) {
 735:                 $alarm_value = $value / 10080;
 736:                 $alarm_unit = _("Week(s)");
 737:             } elseif ($value % 1440 == 0) {
 738:                 $alarm_value = $value / 1440;
 739:                 $alarm_unit = _("Day(s)");
 740:             } elseif ($value % 60 == 0) {
 741:                 $alarm_value = $value / 60;
 742:                 $alarm_unit = _("Hour(s)");
 743:             } else {
 744:                 $alarm_value = $value;
 745:                 $alarm_unit = _("Minute(s)");
 746:             }
 747:             $alarm_text = "$alarm_value $alarm_unit";
 748:         } else {
 749:             $alarm_text = _("None");
 750:         }
 751:         return $alarm_text;
 752:     }
 753: 
 754:     /**
 755:      * Returns the full name and a compose to message an assignee.
 756:      *
 757:      * @param string $assignee  The assignee's user name.
 758:      * @param boolean $link     Whether to link to an email compose screen.
 759:      *
 760:      * @return string  The formatted assignee name.
 761:      */
 762:     public static function formatAssignee($assignee, $link = false)
 763:     {
 764:         if (!strlen($assignee)) {
 765:             return '';
 766:         }
 767: 
 768:         $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($assignee);
 769:         $fullname = $identity->getValue('fullname');
 770:         if (!strlen($fullname)) {
 771:             $fullname = $assignee;
 772:         }
 773:         $email = $identity->getValue('from_addr');
 774:         if ($link && !empty($email) &&
 775:             $GLOBALS['registry']->hasMethod('mail/compose')) {
 776:             return Horde::link($GLOBALS['registry']->call(
 777:                                    'mail/compose',
 778:                                    array(array('to' => $email))))
 779:                 . htmlspecialchars($fullname . ' <' . $email . '>')
 780:                 . '</a>';
 781:         }
 782: 
 783:         return htmlspecialchars($fullname);
 784:     }
 785: 
 786:     /**
 787:      * Initial app setup code.
 788:      */
 789:     public static function initialize()
 790:     {
 791:         /* Store the request timestamp if it's not already present. */
 792:         if (!isset($_SERVER['REQUEST_TIME'])) {
 793:             $_SERVER['REQUEST_TIME'] = time();
 794:         }
 795: 
 796:         // Update the preference for what task lists to display. If the user
 797:         // doesn't have any selected task lists for view then fall back to
 798:         // some available list.
 799:         $GLOBALS['display_tasklists'] = @unserialize($GLOBALS['prefs']->getValue('display_tasklists'));
 800:         if (!$GLOBALS['display_tasklists']) {
 801:             $GLOBALS['display_tasklists'] = array();
 802:         }
 803:         if (($tasklistId = Horde_Util::getFormData('display_tasklist')) !== null) {
 804:             if (is_array($tasklistId)) {
 805:                 $GLOBALS['display_tasklists'] = $tasklistId;
 806:             } else {
 807:                 if (in_array($tasklistId, $GLOBALS['display_tasklists'])) {
 808:                     $key = array_search($tasklistId, $GLOBALS['display_tasklists']);
 809:                     unset($GLOBALS['display_tasklists'][$key]);
 810:                 } else {
 811:                     $GLOBALS['display_tasklists'][] = $tasklistId;
 812:                 }
 813:             }
 814:         }
 815: 
 816:         // Make sure all task lists exist now, to save on checking later.
 817:         $_temp = $GLOBALS['display_tasklists'];
 818:         $GLOBALS['all_tasklists'] = self::listTasklists();
 819:         $GLOBALS['display_tasklists'] = array();
 820:         foreach ($_temp as $id) {
 821:             if (isset($GLOBALS['all_tasklists'][$id])) {
 822:                 $GLOBALS['display_tasklists'][] = $id;
 823:             }
 824:         }
 825: 
 826:         /* All tasklists for guests. */
 827:         if (!count($GLOBALS['display_tasklists']) &&
 828:             !$GLOBALS['registry']->getAuth()) {
 829:             $GLOBALS['display_tasklists'] = array_keys($GLOBALS['all_tasklists']);
 830:         }
 831: 
 832:         $tasklists = $GLOBALS['injector']->getInstance('Nag_Factory_Tasklists')
 833:             ->create();
 834:         if (($new_default = $tasklists->ensureDefaultShare()) !== null) {
 835:             $GLOBALS['display_tasklists'][] = $new_default;
 836:         }
 837: 
 838:         $GLOBALS['prefs']->setValue('display_tasklists', serialize($GLOBALS['display_tasklists']));
 839:     }
 840: 
 841:     public static function menu()
 842:     {
 843:         Horde::startBuffer();
 844:         include NAG_TEMPLATES . '/quick.inc';
 845:         return Horde::menu() . Horde::endBuffer();
 846:     }
 847: 
 848:     /**
 849:      * Trigger notifications.
 850:      */
 851:     public static function status()
 852:     {
 853:         global $notification;
 854: 
 855:         if (empty($GLOBALS['conf']['alarms']['driver'])) {
 856:             // Get any alarms in the next hour.
 857:             $now = time();
 858:             try {
 859:                 $alarmList = self::listAlarms($now);
 860:                 $messages = array();
 861:                 foreach ($alarmList as $task) {
 862:                     $differential = $task->due - $now;
 863:                     $key = $differential;
 864:                     while (isset($messages[$key])) {
 865:                         $key++;
 866:                     }
 867:                     if ($differential >= -60 && $differential < 60) {
 868:                         $messages[$key] = array(sprintf(_("%s is due now."), $task->name), 'horde.alarm');
 869:                     } elseif ($differential >= 60) {
 870:                         $messages[$key] = array(sprintf(_("%s is due in %s"), $task->name,
 871:                                                         self::secondsToString($differential)), 'horde.alarm');
 872:                     }
 873:                 }
 874: 
 875:                 ksort($messages);
 876:                 foreach ($messages as $message) {
 877:                     $notification->push($message[0], $message[1]);
 878:                 }
 879:             } catch (Nag_Exception $e) {
 880:                 Horde::logMessage($e, 'ERR');
 881:                 $notification->push($e->getMessage(), 'horde.error');
 882:             }
 883:         }
 884: 
 885:         // Check here for guest task lists so that we don't get multiple
 886:         // messages after redirects, etc.
 887:         if (!$GLOBALS['registry']->getAuth() && !count(self::listTasklists())) {
 888:             $notification->push(_("No task lists are available to guests."));
 889:         }
 890: 
 891:         // Display all notifications.
 892:         $notification->notify(array('listeners' => 'status'));
 893:     }
 894: 
 895:     /**
 896:      * Sends email notifications that a task has been added, edited, or
 897:      * deleted to users that want such notifications.
 898:      *
 899:      * @param string $action      The event action. One of "add", "edit", or
 900:      *                            "delete".
 901:      * @param Nag_Task $task      The changed task.
 902:      * @param Nag_Task $old_task  The original task if $action is "edit".
 903:      *
 904:      * @throws Nag_Exception
 905:      */
 906:     public static function sendNotification($action, $task, $old_task = null)
 907:     {
 908:         if (!in_array($action, array('add', 'edit', 'delete'))) {
 909:             throw new Nag_Exception('Unknown event action: ' . $action);
 910:         }
 911: 
 912:         try {
 913:             $share = $GLOBALS['nag_shares']->getShare($task->tasklist);
 914:         } catch (Horde_Share_Exception $e) {
 915:             Horde::logMessage($e->getMessage(), 'ERR');
 916:             throw new Nag_Exception($e);
 917:         }
 918: 
 919:         $groups = $GLOBALS['injector']->getInstance('Horde_Group');
 920:         $recipients = array();
 921:         $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create();
 922:         $from = $identity->getDefaultFromAddress(true);
 923: 
 924:         $owner = $share->get('owner');
 925:         if (strlen($owner)) {
 926:             $recipients[$owner] = self::_notificationPref($owner, 'owner');
 927:         }
 928: 
 929:         foreach ($share->listUsers(Horde_Perms::READ) as $user) {
 930:             if (empty($recipients[$user])) {
 931:                 $recipients[$user] = self::_notificationPref($user, 'read', $task->tasklist);
 932:             }
 933:         }
 934:         foreach ($share->listGroups(Horde_Perms::READ) as $group) {
 935:             try {
 936:                 $group_users = $groups->listUsers($group);
 937:             } catch (Horde_Group_Exception $e) {
 938:                 Horde::logMessage($e, 'ERR');
 939:                 continue;
 940:             }
 941: 
 942:             foreach ($group_users as $user) {
 943:                 if (empty($recipients[$user])) {
 944:                     $recipients[$user] = self::_notificationPref($user, 'read', $task->tasklist);
 945:                 }
 946:             }
 947:         }
 948: 
 949:         $addresses = array();
 950:         foreach ($recipients as $user => $vals) {
 951:             if (!$vals) {
 952:                 continue;
 953:             }
 954:             $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($user);
 955:             $email = $identity->getValue('from_addr');
 956:             if (strpos($email, '@') === false) {
 957:                 continue;
 958:             }
 959:             list($mailbox, $host) = explode('@', $email);
 960:             if (!isset($addresses[$vals['lang']][$vals['tf']][$vals['df']])) {
 961:                 $addresses[$vals['lang']][$vals['tf']][$vals['df']] = array();
 962:             }
 963:             $addresses[$vals['lang']][$vals['tf']][$vals['df']][] = Horde_Mime_Address::writeAddress($mailbox, $host, $identity->getValue('fullname'));
 964:         }
 965: 
 966:         if (!$addresses) {
 967:             return;
 968:         }
 969: 
 970:         $mail = new Horde_Mime_Mail(array(
 971:             'User-Agent' => 'Nag ' . $GLOBALS['registry']->getVersion(),
 972:             'Precedence' => 'bulk',
 973:             'Auto-Submitted' => 'auto-generated',
 974:             'From' => $from));
 975: 
 976:         foreach ($addresses as $lang => $twentyFour) {
 977:             $GLOBALS['registry']->setLanguageEnvironment($lang);
 978: 
 979:             $view_link = Horde_Util::addParameter(Horde::url('view.php', true),
 980:                                             array('tasklist' => $task->tasklist,
 981:                                                   'task' => $task->id),
 982:                                             null, false);
 983: 
 984:             switch ($action) {
 985:             case 'add':
 986:                 $subject = _("Task added:");
 987:                 $notification_message = _("You requested to be notified when tasks are added to your task lists.")
 988:                     . "\n\n"
 989:                     . ($task->due
 990:                        ? _("The task \"%s\" has been added to task list \"%s\", with a due date of: %s.")
 991:                        : _("The task \"%s\" has been added to task list \"%s\"."))
 992:                     . "\n"
 993:                     . str_replace('%', '%%', $view_link);
 994:                 break;
 995: 
 996:             case 'edit':
 997:                 $subject = _("Task modified:");
 998:                 $notification_message = _("You requested to be notified when tasks are edited on your task lists.")
 999:                     . "\n\n"
1000:                     . _("The task \"%s\" has been edited on task list \"%s\".")
1001:                     . "\n"
1002:                     . str_replace('%', '%%', $view_link)
1003:                     . "\n\n"
1004:                     . _("Changes made for this task:");
1005:                 if ($old_task->name != $task->name) {
1006:                     $notification_message .= "\n - "
1007:                         . sprintf(_("Changed name from \"%s\" to \"%s\""),
1008:                                   $old_task->name, $task->name);
1009:                 }
1010:                 if ($old_task->tasklist != $task->tasklist) {
1011:                     $old_share = $GLOBALS['nag_shares']->getShare($old_task->tasklist);
1012:                     $notification_message .= "\n - "
1013:                         . sprintf(_("Changed task list from \"%s\" to \"%s\""),
1014:                                   $old_share->get('name'), $share->get('name'));
1015:                 }
1016:                 if ($old_task->parent_id != $task->parent_id) {
1017:                     $old_parent = $old_task->getParent();
1018:                     try {
1019:                         $parent = $task->getParent();
1020:                         $notification_message .= "\n - "
1021:                             . sprintf(_("Changed parent task from \"%s\" to \"%s\""),
1022:                                       $old_parent ? $old_parent->name : _("no parent"),
1023:                                       $parent ? $parent->name : _("no parent"));
1024:                     } catch (Nag_Exception $e) {
1025:                     }
1026:                 }
1027:                 if ($old_task->category != $task->category) {
1028:                     $notification_message .= "\n - "
1029:                         . sprintf(_("Changed category from \"%s\" to \"%s\""),
1030:                                   $old_task->category, $task->category);
1031:                 }
1032:                 if ($old_task->assignee != $task->assignee) {
1033:                     $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($old_task->assignee);
1034:                     $old_name = $identity->getValue('fullname');
1035:                     if (!strlen($old_name)) {
1036:                         $old_name = $old_task->assignee;
1037:                     }
1038:                     $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($task->assignee);
1039:                     $new_name = $identity->getValue('fullname');
1040:                     if (!strlen($new_name)) {
1041:                         $new_name = $new_task->assignee;
1042:                     }
1043:                     $notification_message .= "\n - "
1044:                         . sprintf(_("Changed assignee from \"%s\" to \"%s\""),
1045:                                   $old_name, $new_name);
1046:                 }
1047:                 if ($old_task->private != $task->private) {
1048:                     $notification_message .= "\n - "
1049:                         . ($task->private ? _("Turned privacy on") : _("Turned privacy off"));
1050:                 }
1051:                 if ($old_task->due != $task->due) {
1052:                     $notification_message .= "\n - "
1053:                         . sprintf(_("Changed due date from %s to %s"),
1054:                                   $old_task->due ? self::formatDate($old_task->due) : _("no due date"),
1055:                                   $task->due ? self::formatDate($task->due) : _("no due date"));
1056:                 }
1057:                 if ($old_task->start != $task->start) {
1058:                     $notification_message .= "\n - "
1059:                         . sprintf(_("Changed start date from %s to %s"),
1060:                                   $old_task->start ? self::formatDate($old_task->start) : _("no start date"),
1061:                                   $task->start ? self::formatDate($task->start) : _("no start date"));
1062:                 }
1063:                 if ($old_task->alarm != $task->alarm) {
1064:                     $notification_message .= "\n - "
1065:                         . sprintf(_("Changed alarm from %s to %s"),
1066:                                   self::formatAlarm($old_task->alarm), self::formatAlarm($task->alarm));
1067:                 }
1068:                 if ($old_task->priority != $task->priority) {
1069:                     $notification_message .= "\n - "
1070:                         . sprintf(_("Changed priority from %s to %s"),
1071:                                   $old_task->priority, $task->priority);
1072:                 }
1073:                 if ($old_task->estimate != $task->estimate) {
1074:                     $notification_message .= "\n - "
1075:                         . sprintf(_("Changed estimate from %s to %s"),
1076:                                   $old_task->estimate, $task->estimate);
1077:                 }
1078:                 if ($old_task->completed != $task->completed) {
1079:                     $notification_message .= "\n - "
1080:                         . sprintf(_("Changed completion from %s to %s"),
1081:                                   $old_task->completed ? _("completed") : _("not completed"),
1082:                                   $task->completed ? _("completed") : _("not completed"));
1083:                 }
1084:                 if ($old_task->desc != $task->desc) {
1085:                     $notification_message .= "\n - " . _("Changed description");
1086:                 }
1087:                 break;
1088: 
1089:             case 'delete':
1090:                 $subject = _("Task deleted:");
1091:                 $notification_message =
1092:                     _("You requested to be notified when tasks are deleted from your task lists.")
1093:                     . "\n\n"
1094:                     . _("The task \"%s\" has been deleted from task list \"%s\".");
1095:                 break;
1096:             }
1097: 
1098:             $mail->addHeader('Subject', $subject . ' ' . $task->name);
1099: 
1100:             foreach ($twentyFour as $tf => $dateFormat) {
1101:                 foreach ($dateFormat as $df => $df_recipients) {
1102:                     $message = sprintf($notification_message,
1103:                                        $task->name,
1104:                                        $share->get('name'),
1105:                                        $task->due ? strftime($df, $task->due) . ' ' . date($tf ? 'H:i' : 'h:ia', $task->due) : '');
1106:                     if (strlen(trim($task->desc))) {
1107:                         $message .= "\n\n" . _("Task description:") . "\n\n" . $task->desc;
1108:                     }
1109: 
1110:                     $mail->setBody($message);
1111:                     $mail->clearRecipients();
1112:                     $mail->addRecipients($df_recipients);
1113: 
1114:                     Horde::logMessage(sprintf('Sending event notifications for %s to %s',
1115:                                               $task->name, implode(', ', $df_recipients)), 'INFO');
1116:                     $mail->send($GLOBALS['injector']->getInstance('Horde_Mail'));
1117:                 }
1118:             }
1119:         }
1120:     }
1121: 
1122:     /**
1123:      * Builds the body MIME part of a multipart message.
1124:      *
1125:      * @param Horde_View $view        A view to render the HTML and plain text
1126:      *                                templates for the messate.
1127:      * @param string $template        The template base name for the view.
1128:      * @param Horde_Mime_Part $image  The MIME part of a related image.
1129:      *
1130:      * @return Horde_Mime_Part  A multipart/alternative MIME part.
1131:      */
1132:     public static function buildMimeMessage(Horde_View $view, $template,
1133:                                             Horde_Mime_Part $image)
1134:     {
1135:         $multipart = new Horde_Mime_Part();
1136:         $multipart->setType('multipart/alternative');
1137:         $bodyText = new Horde_Mime_Part();
1138:         $bodyText->setType('text/plain');
1139:         $bodyText->setCharset('UTF-8');
1140:         $bodyText->setContents($view->render($template . '.plain.php'));
1141:         $bodyText->setDisposition('inline');
1142:         $multipart->addPart($bodyText);
1143:         $bodyHtml = new Horde_Mime_Part();
1144:         $bodyHtml->setType('text/html');
1145:         $bodyHtml->setCharset('UTF-8');
1146:         $bodyHtml->setContents($view->render($template . '.html.php'));
1147:         $bodyHtml->setDisposition('inline');
1148:         $related = new Horde_Mime_Part();
1149:         $related->setType('multipart/related');
1150:         $related->setContentTypeParameter('start', $bodyHtml->setContentId());
1151:         $related->addPart($bodyHtml);
1152:         $related->addPart($image);
1153:         $multipart->addPart($related);
1154:         return $multipart;
1155:     }
1156: 
1157:     /**
1158:      * Returns a MIME part for an image to be embedded into a HTML document.
1159:      *
1160:      * @param string $file  An image file name.
1161:      *
1162:      * @return Horde_Mime_Part  A MIME part representing the image.
1163:      */
1164:     public static function getImagePart($file)
1165:     {
1166:         $background = Horde_Themes::img($file);
1167:         $image = new Horde_Mime_Part();
1168:         $image->setType('image/png');
1169:         $image->setContents(file_get_contents($background->fs));
1170:         $image->setContentId();
1171:         $image->setDisposition('attachment');
1172:         return $image;
1173:     }
1174: 
1175:     /**
1176:      * Returns the real name, if available, of a user.
1177:      *
1178:      * @param string $uid  The userid of the user to retrieve
1179:      *
1180:      * @return string  The fullname of the user.
1181:      */
1182:     public static function getUserName($uid)
1183:     {
1184:         static $names = array();
1185: 
1186:         if (!isset($names[$uid])) {
1187:             $ident = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($uid);
1188:             $ident->setDefault($ident->getDefault());
1189:             $names[$uid] = $ident->getValue('fullname');
1190:             if (empty($names[$uid])) {
1191:                 $names[$uid] = $uid;
1192:             }
1193:         }
1194: 
1195:         return $names[$uid];
1196:     }
1197: 
1198:     /**
1199:      * Returns whether a user wants email notifications for a tasklist.
1200:      *
1201:      * @access private
1202:      *
1203:      * @todo This method is causing a memory leak somewhere, noticeable if
1204:      *       importing a large amount of events.
1205:      *
1206:      * @param string $user      A user name.
1207:      * @param string $mode      The check "mode". If "owner", the method checks
1208:      *                          if the user wants notifications only for
1209:      *                          tasklists he owns. If "read", the method checks
1210:      *                          if the user wants notifications for all
1211:      *                          tasklists he has read access to, or only for
1212:      *                          shown tasklists and the specified tasklist is
1213:      *                          currently shown.
1214:      * @param string $tasklist  The name of the tasklist if mode is "read".
1215:      *
1216:      * @return boolean  True if the user wants notifications for the tasklist.
1217:      */
1218:     public static function _notificationPref($user, $mode, $tasklist = null)
1219:     {
1220:         $prefs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Prefs')->create('nag', array(
1221:             'cache' => false,
1222:             'user' => $user
1223:         ));
1224:         $vals = array('lang' => $prefs->getValue('language'),
1225:                       'tf' => $prefs->getValue('twentyFour'),
1226:                       'df' => $prefs->getValue('date_format'));
1227: 
1228:         if ($prefs->getValue('task_notification_exclude_self') &&
1229:             $user == $GLOBALS['registry']->getAuth()) {
1230:             return false;
1231:         }
1232: 
1233:         $notification = $prefs->getValue('task_notification');
1234:         switch ($notification) {
1235:         case 'owner':
1236:             return $mode == 'owner' ? $vals : false;
1237:         case 'read':
1238:             return $mode == 'read' ? $vals : false;
1239:         case 'show':
1240:             if ($mode == 'read') {
1241:                 $display_tasklists = unserialize($prefs->getValue('display_tasklists'));
1242:                 return in_array($tasklist, $display_tasklists) ? $vals : false;;
1243:             }
1244:         }
1245: 
1246:         return false;
1247:     }
1248: 
1249:     /**
1250:      * Comparison function for sorting tasks by create date (not currently used
1251:      * as it would require accessing Horde_History for each task) and id.
1252:      *
1253:      * @param array $a  Task one.
1254:      * @param array $b  Task two.
1255:      *
1256:      * @return integer  1 if task one is greater, -1 if task two is greater;
1257:      *                  0 if they are equal (though no tasks should ever be
1258:      *                  equal in this comparison).
1259:      */
1260:     public static function _sortByIdentity($a, $b)
1261:     {
1262:         return strcmp($a->id, $b->id);
1263:     }
1264: 
1265:     /**
1266:      * Comparison function for sorting tasks by priority.
1267:      *
1268:      * @param array $a  Task one.
1269:      * @param array $b  Task two.
1270:      *
1271:      * @return integer  1 if task one is greater, -1 if task two is greater;
1272:      *                  0 if they are equal.
1273:      */
1274:     public static function _sortByPriority($a, $b)
1275:     {
1276:         if ($a->priority == $b->priority) {
1277:             return self::_sortByIdentity($a, $b);
1278:         }
1279:         return ($a->priority > $b->priority) ? 1 : -1;
1280:     }
1281: 
1282:     /**
1283:      * Comparison function for reverse sorting tasks by priority.
1284:      *
1285:      * @param array $a  Task one.
1286:      * @param array $b  Task two.
1287:      *
1288:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1289:      *                  0 if they are equal.
1290:      */
1291:     public static function _rsortByPriority($a, $b)
1292:     {
1293:         return self::_sortByPriority($b, $a);
1294:     }
1295: 
1296:     /**
1297:      * Comparison function for sorting tasks by name.
1298:      *
1299:      * @param array $a  Task one.
1300:      * @param array $b  Task two.
1301:      *
1302:      * @return integer  1 if task one is greater, -1 if task two is greater;
1303:      *                  0 if they are equal.
1304:      */
1305:     public static function _sortByName($a, $b)
1306:     {
1307:         return strcasecmp($a->name, $b->name);
1308:     }
1309: 
1310:     /**
1311:      * Comparison function for reverse sorting tasks by name.
1312:      *
1313:      * @param array $a  Task one.
1314:      * @param array $b  Task two.
1315:      *
1316:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1317:      *                  0 if they are equal.
1318:      */
1319:     public static function _rsortByName($a, $b)
1320:     {
1321:         return strcasecmp($b->name, $a->name);
1322:     }
1323: 
1324:     /**
1325:      * Comparison function for sorting tasks by assignee.
1326:      *
1327:      * @param array $a  Task one.
1328:      * @param array $b  Task two.
1329:      *
1330:      * @return integer  1 if task one is greater, -1 if task two is greater;
1331:      *                  0 if they are equal.
1332:      */
1333:     public static function _sortByAssignee($a, $b)
1334:     {
1335:         return strcasecmp($a->assignee, $b->assignee);
1336:     }
1337: 
1338:     /**
1339:      * Comparison function for reverse sorting tasks by assignee.
1340:      *
1341:      * @param array $a  Task one.
1342:      * @param array $b  Task two.
1343:      *
1344:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1345:      *                  0 if they are equal.
1346:      */
1347:     public static function _rsortByAssignee($a, $b)
1348:     {
1349:         return strcasecmp($b->assignee, $a->assignee);
1350:     }
1351: 
1352:     /**
1353:      * Comparison function for sorting tasks by assignee.
1354:      *
1355:      * @param array $a  Task one.
1356:      * @param array $b  Task two.
1357:      *
1358:      * @return integer  1 if task one is greater, -1 if task two is greater;
1359:      *                  0 if they are equal.
1360:      */
1361:     public static function _sortByEstimate($a, $b)
1362:     {
1363:         $a_est = $a->estimation();
1364:         $b_est = $b->estimation();
1365:         if ($a_est == $b_est) {
1366:             return self::_sortByIdentity($a, $b);
1367:         }
1368:         return ($a_est > $b_est) ? 1 : -1;
1369:     }
1370: 
1371:     /**
1372:      * Comparison function for reverse sorting tasks by name.
1373:      *
1374:      * @param array $a  Task one.
1375:      * @param array $b  Task two.
1376:      *
1377:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1378:      *                  0 if they are equal.
1379:      */
1380:     public static function _rsortByEstimate($a, $b)
1381:     {
1382:         return self::_sortByEstimate($b, $a);
1383:     }
1384: 
1385:     /**
1386:      * Comparison function for sorting tasks by category.
1387:      *
1388:      * @param array $a  Task one.
1389:      * @param array $b  Task two.
1390:      *
1391:      * @return integer  1 if task one is greater, -1 if task two is greater;
1392:      *                  0 if they are equal.
1393:      */
1394:     public static function _sortByCategory($a, $b)
1395:     {
1396:         return strcasecmp($a->category ? $a->category : _("Unfiled"),
1397:                           $b->category ? $b->category : _("Unfiled"));
1398:     }
1399: 
1400:     /**
1401:      * Comparison function for reverse sorting tasks by category.
1402:      *
1403:      * @param array $a  Task one.
1404:      * @param array $b  Task two.
1405:      *
1406:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1407:      *                  0 if they are equal.
1408:      */
1409:     public static function _rsortByCategory($a, $b)
1410:     {
1411:         return self::_sortByCategory($b, $a);
1412:     }
1413: 
1414:     /**
1415:      * Comparison function for sorting tasks by due date.
1416:      *
1417:      * @param array $a  Task one.
1418:      * @param array $b  Task two.
1419:      *
1420:      * @return integer  1 if task one is greater, -1 if task two is greater;
1421:      *                  0 if they are equal.
1422:      */
1423:     public static function _sortByDue($a, $b)
1424:     {
1425:         if ($a->due == $b->due) {
1426:             return self::_sortByIdentity($a, $b);
1427:         }
1428: 
1429:         // Treat empty due dates as farthest into the future.
1430:         if ($a->due == 0) {
1431:             return 1;
1432:         }
1433:         if ($b->due == 0) {
1434:             return -1;
1435:         }
1436: 
1437:         return ($a->due > $b->due) ? 1 : -1;
1438:     }
1439: 
1440:     /**
1441:      * Comparison function for reverse sorting tasks by due date.
1442:      *
1443:      * @param array $a  Task one.
1444:      * @param array $b  Task two.
1445:      *
1446:      * @return integer  -1 if task one is greater, 1 if task two is greater,
1447:      *                  0 if they are equal.
1448:      */
1449:     public static function _rsortByDue($a, $b)
1450:     {
1451:         return self::_sortByDue($b, $a);
1452:     }
1453: 
1454:     /**
1455:      * Comparison function for sorting tasks by start date.
1456:      *
1457:      * @param array $a  Task one.
1458:      * @param array $b  Task two.
1459:      *
1460:      * @return integer  1 if task one is greater, -1 if task two is greater;
1461:      *                  0 if they are equal.
1462:      */
1463:     public static function _sortByStart($a, $b)
1464:     {
1465:         if ($a->start == $b->start) {
1466:             return self::_sortByIdentity($a, $b);
1467:         }
1468: 
1469:         // Treat empty start dates as farthest into the future.
1470:         if ($a->start == 0) {
1471:             return 1;
1472:         }
1473:         if ($b->start == 0) {
1474:             return -1;
1475:         }
1476: 
1477:         return ($a->start > $b->start) ? 1 : -1;
1478:     }
1479: 
1480:     /**
1481:      * Comparison function for reverse sorting tasks by start date.
1482:      *
1483:      * @param array $a  Task one.
1484:      * @param array $b  Task two.
1485:      *
1486:      * @return integer  -1 if task one is greater, 1 if task two is greater,
1487:      *                  0 if they are equal.
1488:      */
1489:     public static function _rsortByStart($a, $b)
1490:     {
1491:         return self::_sortByStart($b, $a);
1492:     }
1493: 
1494:     /**
1495:      * Comparison function for sorting tasks by completion status.
1496:      *
1497:      * @param array $a  Task one.
1498:      * @param array $b  Task two.
1499:      *
1500:      * @return integer  1 if task one is greater, -1 if task two is greater;
1501:      *                  0 if they are equal.
1502:      */
1503:     public static function _sortByCompletion($a, $b)
1504:     {
1505:         if ($a->completed == $b->completed) {
1506:             return self::_sortByIdentity($a, $b);
1507:         }
1508:         return ($a->completed > $b->completed) ? -1 : 1;
1509:     }
1510: 
1511:     /**
1512:      * Comparison function for reverse sorting tasks by completion status.
1513:      *
1514:      * @param array $a  Task one.
1515:      * @param array $b  Task two.
1516:      *
1517:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1518:      *                  0 if they are equal.
1519:      */
1520:     public static function _rsortByCompletion($a, $b)
1521:     {
1522:         return self::_sortByCompletion($b, $a);
1523:     }
1524: 
1525:     /**
1526:      * Comparison function for sorting tasks by owner.
1527:      *
1528:      * @param array $a  Task one.
1529:      * @param array $b  Task two.
1530:      *
1531:      * @return integer  1 if task one is greater, -1 if task two is greater;
1532:      *                  0 if they are equal.
1533:      */
1534:     public static function _sortByOwner($a, $b)
1535:     {
1536:         $diff = strcasecmp(self::_getOwner($a), self::_getOwner($b));
1537:         if ($diff == 0) {
1538:             return self::_sortByIdentity($a, $b);
1539:         } else {
1540:             return $diff;
1541:         }
1542:     }
1543: 
1544:     /**
1545:      * Comparison function for reverse sorting tasks by owner.
1546:      *
1547:      * @param array $a  Task one.
1548:      * @param array $b  Task two.
1549:      *
1550:      * @return integer  -1 if task one is greater, 1 if task two is greater;
1551:      *                  0 if they are equal.
1552:      */
1553:     public static function _rsortByOwner($a, $b)
1554:     {
1555:         return self::_sortByOwner($b, $a);
1556:     }
1557: 
1558:     /**
1559:      * Returns the owner of a taksk.
1560:      *
1561:      * @param Nag_Task $task  A task.
1562:      *
1563:      * @return string  The task's owner.
1564:      */
1565:     static protected function _getOwner($task)
1566:     {
1567:         if ($task->tasklist == '**EXTERNAL**') {
1568:             return $GLOBALS['registry']->getAuth();
1569:         }
1570:         $share = $GLOBALS['nag_shares']->getShare($task->tasklist);
1571:         $owner = $task->tasklist;
1572:         if ($owner != $share->get('owner')) {
1573:             $owner = $share->get('name');
1574:         }
1575:         return $owner;
1576:     }
1577: 
1578:     /**
1579:      * Returns the calendars that should be used for syncing.
1580:      *
1581:      * @return array  An array of calendar ids
1582:      */
1583:     static public function getSyncLists()
1584:     {
1585:         $cs = unserialize($GLOBALS['prefs']->getValue('sync_lists'));
1586:         if (!empty($cs)) {
1587:             // Have a pref, make sure it's still available
1588:             $lists = self::listTasklists(false, Horde_Perms::EDIT);
1589:             $cscopy = array_flip($cs);
1590:             foreach ($cs as $c) {
1591:                 if (empty($lists[$c])) {
1592:                     unset($cscopy[$c]);
1593:                 }
1594:             }
1595: 
1596:             // Have at least one
1597:             if (count($cscopy)) {
1598:                 return array_flip($cscopy);
1599:             }
1600:         }
1601: 
1602:         if ($cs = self::getDefaultTasklist(Horde_Perms::EDIT)) {
1603:             return array($cs);
1604:         }
1605: 
1606:         return array();
1607:     }
1608: 
1609: }
1610: 
API documentation generated by ApiGen