<?php

// Actions
/** @const NAG_ADD_TASK Add a new task.           */ define('NAG_ADD_TASK', 101);
/** @const NAG_DELETE_TASKS Delete tasks.         */ define('NAG_DELETE_TASKS', 102);
/** @const NAG_MODIFY_TASK Modify a task.         */ define('NAG_MODIFY_TASK', 103);
/** @const NAG_SAVE_TASK Save a task to storage.  */ define('NAG_SAVE_TASK', 104);
/** @const NAG_SEARCH_TASKS Search the task list. */ define('NAG_SEARCH_TASKS', 105);
/** @const NAG_COMPLETE_TASKS Complete tasks.     */ define('NAG_COMPLETE_TASKS', 106);
/** @const NAG_SET_PRIORITY Set task priorities.  */ define('NAG_SET_PRIORITY', 107);
/** @const NAG_DELETE_CATEGORY Delete a category. */ define('NAG_DELETE_CATEGORY', 108);
/** @const NAG_RENAME_CATEGORY Rename a category. */ define('NAG_RENAME_CATEGORY', 109);
/** @const NAG_ADD_CATEGORY Add a new category.   */ define('NAG_ADD_CATEGORY', 110);

// Sort types
/** @const NAG_SORT_NAME Sort by task name.           */ define('NAG_SORT_NAME', 0);
/** @const NAG_SORT_PRIORITY Sort by priority.        */ define('NAG_SORT_PRIORITY', 1);
/** @const NAG_SORT_DUE Sort by due date.             */ define('NAG_SORT_DUE', 2);
/** @const NAG_SORT_COMPLETION Sort by completion.    */ define('NAG_SORT_COMPLETION', 3);
/** @const NAG_SORT_CATEGORY Sort by category.        */ define('NAG_SORT_CATEGORY', 4);
/** @const NAG_SORT_OWNER Sort by owner.              */ define('NAG_SORT_OWNER', 5);
/** @const NAG_SORT_ASCEND Sort in ascending order.   */ define('NAG_SORT_ASCEND', 0);
/** @const NAG_SORT_DESCEND Sort in descending order. */ define('NAG_SORT_DESCEND', 1);

/**
 * Array for mapping the completed states to their string representations.
 * @var array $completion_map
 */
$GLOBALS['completion_map'] = array(
    0 => _("No"),
    1 => _("Yes"));

/**
 * Nag Base Class.
 *
 * $Horde: nag/lib/Nag.php,v 1.88 2003/08/21 11:43:44 jan Exp $
 *
 * @author Jon Parise <jon@horde.org>
 * @version $Revision: 1.88 $
 * @package nag
 */
class Nag {

    function secondsToString($seconds)
    {
        $hours = floor($seconds / 3600);
        $minutes = ($seconds / 60) % 60;

        if ($hours > 1) {
            if ($minutes == 0) {
                return sprintf(_("%d hours"), $hours);
            } else if ($minutes == 1) {
                return sprintf(_("%d hours, %d minute"), $hours, $minutes);
            } else {
                return sprintf(_("%d hours, %d minutes"), $hours, $minutes);
            }
        } else if ($hours == 1) {
            if ($minutes == 0) {
                return sprintf(_("%d hour"), $hours);
            } else if ($minutes == 1) {
                return sprintf(_("%d hour, %d minute"), $hours, $minutes);
            } else {
                return sprintf(_("%d hour, %d minutes"), $hours, $minutes);
            }
        } else {
            if ($minutes == 0) {
                return _("no time");
            } else if ($minutes == 1) {
                return sprintf(_("%d minute"), $minutes);
            } else {
                return sprintf(_("%d minutes"), $minutes);
            }
        }
    }

    /**
     * Retrieves the current user's task list from storage.
     * This function will also sort the resulting list, if requested.
     *
     * @param object Nag_Driver  $storage   The current storage object.
     * @param constant $sortby   (optional) The field by which to sort.
     *                           (NAG_SORT_PRIORITY, NAG_SORT_NAME
     *                            NAG_SORT_DUE, NAG_SORT_COMPLETION)
     * @param constant $sortdir  (optional) The direction by which to sort.
     *                           (NAG_SORT_ASC, NAG_SORT_DESC)
     *
     * @return array        Returns a list of the requested tasks.
     *
     * @see Nag_Driver::listTasks()
     */
    function listTasks($sortby = NAG_SORT_PRIORITY,
                       $sortdir = NAG_SORT_ASCEND)
    {
        global $prefs, $conf, $registry;
        $tasks = array();

        /* Sorting criteria for the task list. */
        $sort_functions = array(
            NAG_SORT_PRIORITY => 'ByPriority',
            NAG_SORT_NAME => 'ByName',
            NAG_SORT_CATEGORY => 'ByCategory',
            NAG_SORT_DUE => 'ByDue',
            NAG_SORT_COMPLETION => 'ByCompletion',
            NAG_SORT_OWNER => 'ByOwner'
        );

        foreach ($GLOBALS['display_tasklists'] as $tasklist) {
            /* Create a Nag storage instance. */
            $storage = &Nag_Driver::singleton($conf['storage']['driver'], $tasklist,
                                              $conf['storage']['params']);
            $storage->retrieve();

            /* Retrieve the task list for storage. */
            $newtasks = $storage->listTasks();
            foreach ($newtasks as $taskID => $task) {
                $url = Horde::addParameter('view.php', 'task', $task['task_id']);
                $url = Horde::addParameter($url, 'tasklist', $task['tasklist_id']);
                $newtasks[$taskID]['view_link'] = Horde::applicationUrl($url);

                $url = Horde::addParameter('task.php', 'task', $task['task_id']);
                $url = Horde::addParameter($url, 'tasklist', $task['tasklist_id']);
                $newtasks[$taskID]['complete_link'] = Horde::applicationUrl(Horde::addParameter($url, 'actionID', NAG_COMPLETE_TASKS));
                $newtasks[$taskID]['edit_link'] = Horde::applicationUrl(Horde::addParameter($url, 'actionID', NAG_MODIFY_TASK));
                $newtasks[$taskID]['delete_link'] = Horde::applicationUrl(Horde::addParameter($url, 'actionID', NAG_DELETE_TASKS));
            }

            $tasks = array_merge($tasks, $newtasks);
        }

        // We look for registered apis that support listAs(taskHash).
        $apps = @unserialize($prefs->getValue('show_external'));
        if (is_array($apps)) {
            foreach ($apps as $app) {
                if ($app != 'nag' && $registry->hasMethod('getListTypes', $app)) {
                    $types = $registry->callByPackage($app, 'getListTypes');
                    if (!empty($types['taskHash'])) {
                        $newtasks = $registry->callByPackage($app, 'listAs', array('taskHash'));
                        $tasks = array_merge($tasks, $newtasks);
                    }
                }
            }
        }

        /* Filter complete/incomplete tasks if the user doesn't want
         * them shown. */
        switch ($prefs->getValue('show_completed')) {
        case 0:
            foreach ($tasks as $taskID => $task) {
                if (!empty($task['completed'])) {
                    unset($tasks[$taskID]);
                }
            }
            break;

        case 2:
            foreach ($tasks as $taskID => $task) {
                if (empty($task['completed'])) {
                    unset($tasks[$taskID]);
                }
            }
            break;
        }

        // Sort the array if we have a sort function defined for this
        // field.
        if (array_key_exists($sortby, $sort_functions)) {
            $prefix = ($sortdir == NAG_SORT_DESCEND) ? '_rsort' : '_sort';
            uasort($tasks, array('Nag', $prefix . $sort_functions[$sortby]));
        }

        return $tasks;
    }

    function getTask($tasklist, $task)
    {
        global $conf;

        $storage = &Nag_Driver::singleton($conf['storage']['driver'], $tasklist,
                                          $conf['storage']['params']);
        $storage->retrieve();
        $tasks = $storage->listTasks();

        if (isset($tasks[$task])) {
            return $tasks[$task];
        }

        return false;
    }

    /**
     * Returns all the alarms active right on $date.
     *
     * @param object $date         The start of the time range.
     *
     * @return array  The alarms (alarmId) active on $date.
     */
    function listAlarms($date)
    {
        global $conf;
        $tasks = array();

        foreach ($GLOBALS['display_tasklists'] as $tasklist) {
            /* Create a Nag storage instance. */
            $storage = &Nag_Driver::singleton($conf['storage']['driver'], $tasklist,
                                              $conf['storage']['params']);
            $storage->retrieve();

            /* Retrieve the alarms for the task list. */
            $newtasks = $storage->listAlarms($date);
            if (is_a($newtasks, 'PEAR_Error')) {
                return $newtasks;
            }

            /* Don't show an alarm for complete tasks. */
            foreach ($newtasks as $taskID => $task) {
                if (!empty($task['completed'])) {
                    unset($newtasks[$taskID]);
                }
            }

            $tasks = array_merge($tasks, $newtasks);
        }

        return $tasks;
    }

    /**
     * List a user's categories
     *
     * @return array A list of categories.
     */
    function listCategories($tasklist = null)
    {
        global $prefs;

        static $catString, $categories;

        $cur = $GLOBALS['nag_shares']->getPrefByShare('task_categories', $tasklist);
        if (is_null($catString) || $catString != $cur) {
            $categories = array(0 => _("Unfiled"));

            $catString = $cur;
            if (empty($catString)) {
                return $categories;
            }

            $cats = explode('|', $catString);
            foreach ($cats as $cat) {
                list($key, $val) = explode(':', $cat);
                $categories[$key] = $val;
            }
        }

        asort($categories);
        return $categories;
    }

    /**
     * List categories for all active tasklists.
     *
     * @param optional array $tasklists  Restrict to this list of tasklists.
     *
     * @return array  Multi-level hash of categories.
     */
    function listAllCategories($tasklists = null)
    {
        if (!is_array($tasklists)) {
            $tasklists = $GLOBALS['display_tasklists'];
        }
        if (!count($tasklists)) {
            return array();
        }

        $categories = array();
        foreach (array_keys(Nag::listTasklists()) as $id) {
            if (in_array($id, $tasklists)) {
                $categories[$id] = Nag::listCategories($id);
            }
        }

        return $categories;
    }

    /**
     * List all task lists a user has access to.
     *
     * @param optional boolean $owneronly  Only return tasklists that this
     *                                     user owns? Defaults to false.
     * @param optional integer $permission The permission to filter notepads by.
     *
     * @return array  The task lists.
     */
    function listTasklists($owneronly = false, $permission = _PERMS_SHOW)
    {
        $tasklists = $GLOBALS['nag_shares']->listShares(Auth::getAuth(), $permission, $owneronly);
        if (is_a($tasklists, 'PEAR_Error')) {
            Horde::logMessage($tasklists, __FILE__, __LINE__, PEAR_LOG_ERR);
            return array();
        }

        return $tasklists;
    }

    /**
     * Add a new category
     *
     * @param string  $name     The name of the category to add.
     *
     * @return integer          A valid category id, 0 on failure or
     *                          the new category's id on success.
     */
    function addCategory($name)
    {
        global $prefs;

        if ($prefs->isLocked('task_categories') || empty($name)) {
            return 0;
        }

        $categories = Nag::listCategories();
        if (in_array($name, $categories)) {
            return 0;
        }

        $categories[] = $name;
        unset($categories[0]);

        $cats = array();
        $key = 0;
        foreach ($categories as $key => $cat) {
            $cat = array($key, $cat);
            $cats[] = implode(':', $cat);
        }

        $catString = implode('|', $cats);
        $prefs->setValue('task_categories', $catString);

        return $key;
    }

    /**
     * Delete a category
     *
     * @param integer   $categoryID The id of the category to remove.
     *
     * @return boolean              True on success, false on failure.
     */
    function deleteCategory($categoryID)
    {
        global $prefs;
        $categories = Nag::listCategories();

        if ($prefs->isLocked('task_categories') || !array_key_exists($categoryID, $categories)) {
            return false;
        }

        /* The first category ("Unfiled") cannot be deleted. */
        unset($categories[0]);

        /* Delete the specified category. */
        unset($categories[$categoryID]);

        $cats = array();
        foreach ($categories as $key => $cat) {
            $cat = array($key, $cat);
            $cats[] = implode(':', $cat);
        }

        $catString = implode('|', $cats);
        $prefs->setValue('task_categories', $catString);

        return true;
    }


    /**
     * Rename a category
     *
     * @param integer   $categoryID The id of the category to remove.
     * @param string    $name       The new name of the category.
     *
     * @return boolean              True on success, false on failure.
     */
    function renameCategory($categoryID, $name)
    {
        global $prefs;
        $categories = Nag::listCategories();

        if ($prefs->isLocked('task_categories') || empty($name) ||
                        !array_key_exists($categoryID, $categories) ||
                        in_array($name, $categories)) {
            return false;
        }

        /* The first category ("Unfiled") cannot be renamed. */
        unset($categories[0]);

        /* Assign the new name to the specified category. */
        $categories[$categoryID] = $name;

        $cats = array();
        foreach ($categories as $key => $cat) {
            $cat = array($key, $cat);
            $cats[] = implode(':', $cat);
        }

        $catString = implode('|', $cats);
        $prefs->setValue('task_categories', $catString);

        return true;
    }

    /**
     * Builds the HTML for a priority selection widget.
     *
     * @param string  $name         The name of the widget.
     * @param integer $selected     (optional) The default selected priority.
     *
     * @return string       The HTML <select> widget.
     */
    function buildPriorityWidget($name, $selected = -1)
    {
        $descs = array(1 => _("(highest)"), 5 => _("(lowest)"));

        $html = "<select id=\"$name\" name=\"$name\">";
        for ($priority = 1; $priority <= 5; $priority++) {
            $html .= "<option value=\"$priority\"";
            $html .= ($priority == $selected) ? ' selected="selected">' : '>';
            $html .= $priority . ' ' . @$descs[$priority] . '</option>';
        }
        $html .= "</select>\n";

        return $html;
    }

    /**
     * Builds the HTML for a task completion state widget.
     *
     * @param string  $name         The name of the widget.
     * @param integer $selected     (optional) The default completion state.
     *
     * @return string       The HTML <select> widget.
     */
    function buildCompletionWidget($name, $selected = 0)
    {
        global $completion_map;

        $html = "<select id=\"$name\" name=\"$name\">";

        foreach ($completion_map as $completed => $name) {
            $html .= "<option value=\"$completed\"";
            $html .= ($completed == $selected) ? ' selected="selected">' : '>';
            $html .= $name . '</option>';
        }
        $html .= "</select>\n";

        return $html;
    }

    /**
     * Builds the HTML for a task category widget.
     *
     * @param string  $name       The name of the widget.
     * @param integer $selected   (optional) The default category.
     * @param boolean $newheader  (optional) Include a new new category option.
     *
     * @return string       The HTML <select> widget.
     */
    function buildCategoryWidget($name, $selected = false, $newheader = false)
    {
        $html = "<select id=\"$name\" name=\"$name\">";

        if ($newheader) {
            $html .= '<option value="*new*">' . _("New Category") . "</option>\n";
            $html .= '<option value="">----</option>' . "\n";
        }

        foreach (Nag::listCategories() as $id => $name) {
            $html .= "<option value=\"$id\"";
            $html .= ($id == $selected && $selected !== false) ? ' selected="selected">' : '>';
            $html .= $name . '</option>';
        }
        $html .= '</select>';

        return $html;
    }

    /**
     * Formats the given Unix-style date string.
     *
     * @param string $unixdate     The Unix-style date value to format.
     *
     * @return string       The formatted due date string.
     */
    function formatDate($unixdate = '')
    {
        global $conf;

        if (empty($unixdate)) {
            return '';
        }

        return sprintf(_("%s at %s"),
                       strftime($conf['view']['date_format'], $unixdate),
                       strftime($conf['view']['time_format'], $unixdate));
    }

    /**
     * Returns the string matching the given category ID.
     *
     * @param integer $categoryID     The category ID to look up.
     * @param string  $tasklist       The tasklist the category belongs to.
     *
     * @return string       The formatted category string.
     */
    function formatCategory($categoryID = 0, $tasklist = null)
    {
        $categories = Nag::listCategories($tasklist);
        return isset($categories[$categoryID]) ?
            $categories[$categoryID] :
            $categories[0];
    }

    /**
     * Returns the string representation of the given completion status.
     *
     * @param int $completed    The completion value.
     *
     * @return string       The HTML representation of $completed.
     */
    function formatCompletion($completed)
    {
        return $completed ?
            Horde::img('checked.gif', _("Completed")) :
            Horde::img('unchecked.gif', _("Not Completed"));
    }

    /**
     * Returns a colored representation of a priority.
     *
     * @param int $priority    The priority level.
     *
     * @return string       The HTML representation of $priority.
     */
    function formatPriority($priority)
    {
        return '<span class="pri-' . (int)$priority . '">' . (int)$priority . '</span>';
    }

    /**
     * Returns the string matching the given alarm value.
     *
     * @param int  $value       The alarm value in minutes.
     *
     * @return string       The formatted alarm string.
     */
    function formatAlarm($value)
    {
        if ($value) {
            if ($value % 10080 == 0) {
                $alarm_value = $value / 10080;
                $alarm_unit = _("Week(s)");
            } elseif ($value % 1440 == 0) {
                $alarm_value = $value / 1440;
                $alarm_unit = _("Day(s)");
            } elseif ($value % 60 == 0) {
                $alarm_value = $value / 60;
                $alarm_unit = _("Hour(s)");
            } else {
                $alarm_value = $value;
                $alarm_unit = _("Minute(s)");
            }
            $alarm_text = "$alarm_value $alarm_unit";
        } else {
            $alarm_text = _("None");
        }
        return $alarm_text;
    }

    /**
     * Comparison function for sorting tasks by priority.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByPriority($a, $b)
    {
        if ($a['priority'] == $b['priority']) return 0;
        return ($a['priority'] > $b['priority']) ? 1 : -1;
    }

    /**
     * Comparison function for reverse sorting tasks by priority.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByPriority($a, $b)
    {
        if ($a['priority'] == $b['priority']) return 0;
        return ($a['priority'] > $b['priority']) ? -1 : 1;
    }

    /**
     * Comparison function for sorting tasks by name.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByName($a, $b)
    {
        return strcasecmp($a['name'], $b['name']);
    }

    /**
     * Comparison function for reverse sorting tasks by name.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByName($a, $b)
    {
        return strcasecmp($b['name'], $a['name']);
    }

    /**
     * Comparison function for sorting tasks by category.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByCategory($a, $b)
    {
        return strcasecmp(Nag::formatCategory($b['category'], $b['tasklist_id']),
                          Nag::formatCategory($a['category'], $a['tasklist_id']));
    }

    /**
     * Comparison function for reverse sorting tasks by category.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByCategory($a, $b)
    {
        return strcasecmp(Nag::formatCategory($b['category'], $b['tasklist_id']),
                          Nag::formatCategory($a['category'], $a['tasklist_id']));
    }

    /**
     * Comparison function for sorting tasks by due date.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByDue($a, $b)
    {
        if ($a['due'] == $b['due']) return 0;
        return ($a['due'] > $b['due']) ? -1 : 1;
    }

    /**
     * Comparison function for reverse sorting tasks by due date.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByDue($a, $b)
    {
        if ($a['due'] == $b['due']) return 0;
        return ($a['due'] < $b['due']) ? -1 : 1;
    }

    /**
     * Comparison function for sorting tasks by completion status.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByCompletion($a, $b)
    {
        if ($a['completed'] == $b['completed']) return 0;
        return ($a['completed'] > $b['completed']) ? -1 : 1;
    }

    /**
     * Comparison function for reverse sorting tasks by completion status.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByCompletion($a, $b)
    {
        if ($a['completed'] == $b['completed']) return 0;
        return ($a['completed'] < $b['completed']) ? -1 : 1;
    }

    /**
     * Comparison function for sorting tasks by owner.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  1 if task one is greater, -1 if task two is greater; 0 if they are equal.
     */
    function _sortByOwner($a, $b)
    {
        $ashare = $GLOBALS['nag_shares']->getShare($a['tasklist_id']);
        $bshare = $GLOBALS['nag_shares']->getShare($b['tasklist_id']);

        $aowner = $a['tasklist_id'];
        $bowner = $b['tasklist_id'];

        if (!is_a($ashare, 'PEAR_Error') && $aowner != $ashare->getOwner()) {
            $aowner = $ashare->getShareName();
        }
        if (!is_a($bshare, 'PEAR_Error') && $bowner != $bshare->getOwner()) {
            $bowner = $bshare->getShareName();
        }

        return strcasecmp($aowner, $bowner);
    }

    /**
     * Comparison function for reverse sorting tasks by owner.
     *
     * @param array $a  Task one.
     * @param array $b  Task two.
     *
     * @return integer  -1 if task one is greater, 1 if task two is greater; 0 if they are equal.
     */
    function _rsortByOwner($a, $b)
    {
        $ashare = $GLOBALS['nag_shares']->getShare($a['tasklist_id']);
        $bshare = $GLOBALS['nag_shares']->getShare($b['tasklist_id']);

        $aowner = $a['tasklist_id'];
        $bowner = $b['tasklist_id'];

        if (!is_a($ashare, 'PEAR_Error') && $aowner != $ashare->getOwner()) {
            $aowner = $ashare->getShareName();
        }
        if (!is_a($bshare, 'PEAR_Error') && $bowner != $bshare->getOwner()) {
            $bowner = $bshare->getShareName();
        }

        return strcasecmp($bowner, $aowner);
    }

    function menu()
    {
        global $notification, $conf, $registry, $prefs, $print_link, $browser;
        $tasklists = Nag::listTasklists();

        require_once HORDE_BASE . '/lib/Menu.php';
        require NAG_TEMPLATES . '/menu/menu.inc';

        /* Include the JavaScript for the help system (if enabled). */
        Help::javascript();

        // Get any alarms in the next hour.
        $_now = time();
        $_alarmList = Nag::listAlarms($_now);
        if (is_a($_alarmList, 'PEAR_Error')) {
            Horde::logMessage($_alarmList, __FILE__, __LINE__, PEAR_LOG_ERR);
            $notification->push($_alarmList, 'horde.error');
        } else {
            $_messages = array();
            foreach ($_alarmList as $_task) {
                $differential = $_task['due'] - $_now;
                $_key = $differential;
                while (isset($_messages[$_key])) {
                    $_key++;
                }
                if ($differential >= -60 && $differential < 60) {
                    $_messages[$_key] = array(sprintf(_("%s is due now."), $_task['name']), 'nag.alarm');
                } elseif ($differential >= 60) {
                    $_messages[$_key] = array(sprintf(_("%s is due in %s"), $_task['name'],
                                                      Nag::secondsToString($differential)), 'nag.alarm');
                }
            }

            ksort($_messages);
            foreach ($_messages as $message) {
                $notification->push($message[0], $message[1]);
            }
        }

        // Check here for guest task lists so that we don't get
        // multiple messages after redirects, etc.
        if (!Auth::getAuth() && !count(Nag::listTasklists())) {
            $notification->push(_("No task lists are available to guests."));
        }

        // Display all notifications.
        $notification->notify();
    }

}
