package.xml 0000664 0001750 0001750 00000235161 12654116100 011303 0 ustar jan jan
nagpear.horde.orgA web based task list managerNag is a web-based application built upon the Horde Application Framework which provides a simple, clean interface for managing online task lists (i.e., todo lists). It also includes strong integration with the other Horde applications and allows users to share task lists or enable light-weight project management.Jan Schneiderjanjan@horde.orgyesMichael J Rubinskymrubinskmrubinsk@horde.orgyesChuck Hagenbuchchuckchuck@horde.orgfalse2016-02-024.2.74.2.0stablestableGPL-2.0
* [mjr] Enforce child tasks must be completed before parent tasks in edit form (Bug #12308).
* [jan] Use access rules compatible with both Apache 2.2 and 2.4.
* [mjr] Fix possible fatal error due to tasks created with an alarm but no due date (Bug #14214).
* [mjr] Fix storage of parent field when using Kolab driver (Bug #14197, mike.gabriel@das-netzwerkteam.de).
* [mjr] Fix switching tab/filters when using Kolab driver (Bug #14196, mike.gabriel@das-netzwerkteam.de).
* [mjr] Fix moving subtasks to another tasklist when parent task is moved (Bug #14209).
* [mjr] Fix fatal error when saving tasks when system shares are present in certain cases.
* [mjr] Return recurring tasks via listTimeObjects API (Bug #14137).
* [mjr] Prevent orphaning tasks via the API.
5.3.06.0.0alpha16.0.0alpha11.7.0contentpear.horde.org2.0.53.0.0alpha13.0.0alpha1hordepear.horde.org5.0.06.0.0alpha16.0.0alpha1Horde_Authpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Corepear.horde.org2.6.13.0.0alpha13.0.0alpha1Horde_Datapear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Datepear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Date_Parserpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Davpear.horde.org1.0.02.0.0alpha12.0.0alpha1Horde_Exceptionpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Formpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Grouppear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Historypear.horde.org2.1.03.0.0alpha13.0.0alpha1Horde_Icalendarpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Mailpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Mimepear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Permspear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Prefspear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Routespear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Sharepear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Supportpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Text_Filterpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Urlpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Utilpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_Viewpear.horde.org2.0.03.0.0alpha13.0.0alpha1gettextHorde_ActiveSyncpear.horde.org2.4.03.0.0alpha13.0.0alpha1Horde_Dbpear.horde.org2.0.03.0.0alpha13.0.0alpha1Horde_OpenXchangepear.horde.org1.0.02.0.0alpha12.0.0alpha1Horde_Testpear.horde.org2.0.03.0.0alpha13.0.0alpha1hordeRolepear.horde.org3.0.0alpha13.0.0alphaalpha2011-03-09GPL-2.0
* First alpha release for Horde 4.
3.0.0beta13.0.0betabeta2011-03-16GPL-2.0
* Fix saving tasks via PUT method.
* Fix deleting task lists.
* Fix hiding the External prefs group (Bug #9643).
3.0.0RC13.0.0betabeta2011-03-23GPL-2.0
* First release candidate for Horde 4.
* [gwr] Adapted the Kolab driver to Horde 4.
3.0.0RC23.0.0betabeta2011-03-30GPL-2.0
* Second release candidate for Horde 4.
3.0.03.0.0stablestable2011-04-06GPL-2.0
* First stable release for Horde 4.
* [jan] Fix application-specific permission checks (Bug #9786).
* [mjr] Datatree share to SQL upgrade script refactored for Horde 4.
* [jan] Fix creating new tasks through WebDAV (Bug #9675).
* [jan] Move all executable scripts to bin/ and prefix with nag-.
3.0.13.0.0stablestable2011-05-03GPL-2.0
* [jan] Fix reversed logic of displaying sub-task tree icons depending on the text direction (Bug #10033).
* [jan] Fix encoding of non-ascii characters in parent task drop-down.
* [mjr] Fix editing tasklists (Bug #9965).
3.0.23.0.0stablestable2011-07-05GPL-2.0
* [jan] Add Ukrainian translation (Andriy Kopystyansky <anri@polynet.lviv.ua>).
3.0.33.0.0stablestable2011-08-02GPL-2.0
* [jan] Don't load all shares with requested permissions from the backend if $conf['share']['hidden'] is enabled.
3.0.43.0.0stablestable2011-10-18GPL-2.0
* [jan] Add missing parameters when PUTing tasks (Bug #10545).
* [mjr] Fix removeUserData implementation.
* [gwr] Fix Kolab object attribute handling for tasks.
* [gwr] Avoid including the owner name into the default share.
3.0.53.0.0stablestable2011-11-02GPL-2.0
* [cjh] Nicer date/time input with fewer input fields and helper javascript.
* [mjr] Add ability to choose which tasklists to synchronize.
* [mjr] Fix task export to ActiveSync for tasks with no due dates or reminders.
3.0.63.0.0stablestable2011-11-22GPL-2.0
* [jan] Don't display task details of private tasks via the API (Bug #10712).
* [jan] Use correct locale when parsing quick tasks (thpo+horde@dotrc.de, Bug #10720).
3.0.73.0.0stablestable2011-12-13GPL-2.0
* [jan] Fix setting custom alarm methods (Bug #9543).
* [jan] Update Japanese translation (Hiromi Kimura <hiromi@tac.tsukuba.ac.jp>).
3.0.83.0.0stablestable2012-05-29GPL-2.0
* [jan] Update Swedish translation (Per Olof Ljungmark <peo@bsdlabs.com>).
* [jan] Update Italian translation (Massimo Malabotta <mmalabotta@units.it>).
* [jan] Update Hungarian translation (Zoltán Németh <nemeth.zoltan@etit.hu>).
3.0.93.0.0stablestable2012-05-29GPL-2.0
* [mms] Fix getTasklist() API call.
* [jan] Fix changing sort direction.
* [jan] Fix sorting by owner if displaying external task sources.
* [jan] Update Turkish translation (İstanbul Technical University).
2012-08-294.0.0beta14.0.0beta1betabetaGPL-2.0
* [mjr] Add support for smart tasklists.
* [mjr] Migrate categories to tags.
* [jan] Fix fatal error if the task form doesn't validate.
* [jan] Add recurring tasks (Request #2150).
4.0.0beta24.0.0beta1betabeta2012-10-12GPL-2.0
* [jan] Synchronize tags with Kolab categories.
* [mjr] Fix deleting tasks from the task form.
* [mjr] Implement Purge Completed Tasks LoginTask (Request #2520).
* [gwr] Mark the initial default share as such with the Kolab backend.
* [gwr] Always mark the initial share as default tasklist.
* [jan] Move complete task list management into the sidebar.
* [jan] Allow to edit task list color in traditional view.
* [mjr] Tweak display of smartlist links in list view.
* [mjr] Fix fatal recursion error when searching with smartlists were present.
4.0.0RC14.0.0beta1betabeta2012-10-27GPL-2.0
* [jan] Fix export links for tasklists (Bug #11576).
* [mjr] Fix various issues with importing CSV files.
* [mjr] Format datetime fields when exporting a task as a hash (Bug #11537).
* [jan] Fix retrieving tasks from Kolab backend by UID (Bug #11532).
* [jan] Fix toggling tasklists (Bug #11511).
* [jan] Fix listing tasks from Kolab backends.
* [jan] Fix sending alarm e-mail messages.
4.0.04.0.0stablestable2012-10-30GPL-2.0
* [jan] Update Polish translation (Krzysztof Kozera <krzysztof113@o2.pl>).
4.0.14.0.0stablestable2012-11-27GPL-2.0
* [jan] Use Kolab modification and creation dates in the UI (Bug #11591).
* [gwr] Ignore empty dates (Bug #11736).
* [jan] Use encoded UIDs as IDs in Kolab driver.
* [mjr] Fix support for due/start dates for certain ActiveSync clients (Bug #11693).
* [jan] Fix importing tasks from iCalendar (Bug #11681).
* [jan] Fix listing of recurring tasks via timeobjects API.
4.0.24.0.0stablestable2013-01-10GPL-2.0
* [mjr] Fix closing the quick task dialog when cancel is pressed (Bug #11848).
* [mjr] Fix displaying and editing tasks from search results (Bug #11847).
* [jan] Update Basque translation (Ibon Igartua <ibon.igartua@ehu.es>).
* [mjr] Fix incoming task sync with certain combinations of due date values.
* [mjr] Fix quick task addition without session cookies enabled (Bug #11875, Thomas Jarosch <thomas.jarosch@intra2net.com>).
* [jan] Fix editing tasks from the merged list view in smartmobile mode (Bug #11840).
* [jan] Use default Kolab folder for default taks list preference.
* [jan] Mark Kolab folder as default when changing default task list preference.
4.0.34.0.0stablestable2013-05-29GPL-2.0
* [jan] Fix recurring tasks in Kolab driver (Thomas Jarosch <thomas.jarosch@intra2net.com>, Bug #12222).
* [mjr] Fix setting alarm time when syncing (Bug #12201, <thomas.jarosch@intra2net.com>).
* [mjr] Fix issue causing due dates to be offset when syncing (Bug #12200, <thomas.jarosch@intra2net.com>)
* [jan] Fix listing alarms of recurring tasks.
* [jan] Fix default task list in portal block (Bug #12133).
* [jan] Fix displaying of due dates of recurring events in some views.
* [jan] Update broken rows due to migration bug in PostgreSQL (Bug #12101).
* [mjr] Fix uncompleting tasks from task view in smartmobile view (Bug #12098, <ctimoteo@sapo.pt>).
* [jan] Include parent tasks in cost object labels.
* [jan] Only return completed tasks up to a week old as cost objects.
* [mjr] Set correct timezone on incoming ActiveSync tasks (Bug #12053).
* [jan] Update French translation (Paul De Vlieger <paul.de_vlieger@moniut.univ-bpclermont.fr>).
2013-05-074.1.0beta14.0.0betastableGPL-2.0
* [jan] Add CalDAV server support (Request #4267).
* [jan] Update design to closer match the Horde 5 UI.
4.1.0beta24.0.0betastable2013-05-14GPL-2.0
* [jan] Fix incorrect dependencies.
4.1.0RC14.1.0betastable2013-05-29GPL-2.0
* [jan] Don't include external tasks in listTasks() API results.
4.1.04.1.0stablestable2013-06-05GPL-2.0
* Final release.
4.1.14.1.0stablestable2013-07-16GPL-2.0
* [jan] Display CalDAV URL of system tasklists (Bug #12342).
* [mjr] Only query Horde_History when it's required.
* [jan] Fix system tasklist listing via WebDAV.
* [rla] Add system tasklist support for Nag CalDAV access (Request #12342).
* [jan] Fix task description tooltip (Bug #12421).
* [mjr] Persist the tasklist and parent when creating a new task and using "Save and New" (Bug #12400).
* [mjr] Fix tag browsing of shared tasklists (Bug #12405).
* [mjr] Fix importing VTODOs containing RELATED-TO fields (Bug #12355).
* [mjr] Add API methods for using history modification sequences.
4.1.24.1.0stablestable2013-08-27GPL-2.0
* [jan] Fix highlighting task type tabs in list view.
* [jan] Fix fatal errors if DAV support is disabled (Bug #12481).
* [mjr] Fix requesting changes by modification sequence (Bug #12507, Thomas Jarosch <thomas.jarosch@intra2net.com>).
4.1.34.1.0stablestable2013-10-29GPL-2.0
* [jan] SECURITY: Fix XSS vulnerabilities when deleting task lists.
* [jan] Fix updating alarm if completing a task recurrence.
* [jan] Fix editing tasks via CalDAV (Bug #12745).
4.1.44.1.0stablestable2014-03-10GPL-2.0
* [jan] Update Danish translation (Erling Preben Hansen <erling@eph.dk>).
* [jan] Don't return future tasks from cost objects API.
4.1.54.1.0stablestable2014-07-07GPL-2.0
* [jan] Fix link to task in alarm emails.
* [jan] Update Korean translation (Deokgon Kim <dgkim@dgkim.net>).
4.2.0alpha14.2.0alphaalpha2014-05-05GPL-2.0
* [jan] Show external tasks in the correct task type.
* [jan] Purge old tasks from versions < 2.3 too.
* [jan] Allow to synchronize shared task lists via CalDAV (Request #12380).
* [jan] Add script to import tasks from Open-Xchange.
* [jan] Add recurrence controls to basic view (Request #11545).
* [mjr] Add button for creating sub-task from task view (Request #12309).
* [mjr] Add ability to return to existing search results after navigating to a task (Request #12879).
* [jan] Make shares table compatible with Oracle.
* [mjr] Add API support for multiple tasklists via ActiveSync.
4.2.0beta14.2.0betaalpha2014-06-04GPL-2.0
* [jan] Only use URL rewriting for saving/completing tasks if pretty URLs are enabled in the configuration.
* [jan] Don't include smart lists in top menu.
* [jan] Fix top menu URLs to create new tasks in specific task lists.
4.2.0beta24.2.0betaalpha2014-06-17GPL-2.0
* [jan] Always display system task lists to administrators.
* [mjr] Group task fields into tabs on edit form (Request #13228).
4.2.0RC14.2.0betaalpha2014-07-01GPL-2.0
*
4.2.04.2.0stablestable2014-07-08GPL-2.0
* [mjr] Fix returning to previously selected tab after task is saved (Bug #13308).
* [mjr] Fix fatal error when saving a task, only one tasklist exists, and at least one SmartList exists.
4.2.14.2.0stablestable2014-08-04GPL-2.0
* [jan] Don't include external tasks with WebDAV task list.
* [jan] Fix displayed CalDAV URLs for shared task lists (Bug #12380).
* [mjr] Fix remembering auto determined default share (Bug #13360).
* [mjr] Add EAS client created shares to the sync_lists preference (Bug #13359).
* [mjr] Add missing API support for searchTags API (Bug #13364).
* [mjr] Fix detecting task completion when sending task change notifications (Alfonso Marín Marín <almarin@um.es>, Bug #13350).
4.2.24.2.0stablestable2014-10-29GPL-2.0
* [jan] Export nicer display names for users and resources via WebDAV.
* [jan] Export custom owner and read-only properties via WebDAV.
* [jan] Fix search attribute of SQL shares on Oracle.
* [mjr] Fix adding a new tasklist to the syncronized tasklists (Bug #13642).
* [jan] Pass current start date of recurring tasks to dynamic interface.
* [jan] Fix setting task list name for external tasks.
* [jan] Fix link to create sub-task from a task view.
* [jan] Fix listing of recurring tasks with start dates.
* [mjr] Add missing categories to EAS tasks.
* [jan] Fix sidebar display of system task lists in basic view.
* [jan] Fix retrieving individual tasks via WebDAV.
4.2.34.2.0stablestable2014-11-21GPL-2.0
* [mjr] Fix issue with initially creating nag_shares table.
4.2.44.2.0stablestable2015-02-10GPL-2.0
* [jan] Don't list task lists without SHOW permissions (Bug #13837).
* [jan] Use consistent checkbox.
* [jan] Don't allow deletion of tasks with sub-tasks (Bug #13809).
* [jan] Don't allow completion of tasks with incomplete sub-tasks and vice versa.
* [jan] Don't wipe out whole task lists when PUTing individual tasks via WebDAV.
4.2.54.2.0stablestable2015-06-18GPL-2.0
* [jan] Fix positioning of dialog windows.
* [jan] Don't delete existing completions when editing a recurring task.
* [jan] Only delete the last completion when toggling a recurring task to incomplete.
4.2.64.2.0stablestable2015-10-21GPL-2.0
* [jan] Update Italian translation.
* [mjr] Fix indentation level when searching (Bug #14138).
* [mjr] Remove object reference from Content when object is deleted (Bug #14112).
* [mjr] Remove tags from Content when tagged object is deleted (Bug #14112).
* [jan] Fix linking to external tasks from smartmobile view.
4.2.74.2.0stablestable2016-02-02GPL-2.0
* [mjr] Enforce child tasks must be completed before parent tasks in edit form (Bug #12308).
* [jan] Use access rules compatible with both Apache 2.2 and 2.4.
* [mjr] Fix possible fatal error due to tasks created with an alarm but no due date (Bug #14214).
* [mjr] Fix storage of parent field when using Kolab driver (Bug #14197, mike.gabriel@das-netzwerkteam.de).
* [mjr] Fix switching tab/filters when using Kolab driver (Bug #14196, mike.gabriel@das-netzwerkteam.de).
* [mjr] Fix moving subtasks to another tasklist when parent task is moved (Bug #14209).
* [mjr] Fix fatal error when saving tasks when system shares are present in certain cases.
* [mjr] Return recurring tasks via listTimeObjects API (Bug #14137).
* [mjr] Prevent orphaning tasks via the API.
nag-4.2.7/app/controllers/CompleteTask.php 0000664 0001750 0001750 00000001767 12654116077 016653 0 ustar jan jan getRequestVars();
if (isset($requestVars['task']) && isset($requestVars['tasklist'])) {
$nag_task = new Nag_CompleteTask();
$result = $nag_task->result($requestVars['task'], $requestVars['tasklist']);
} else {
$result = array('error' => 'missing parameters');
}
$requestVars = $request->getGetVars();
if (!empty($requestVars['format']) &&
$requestVars['format'] == 'json') {
$response->setContentType('application/json');
$response->setBody(json_encode($result));
} elseif ($requestVars['url']) {
$response->setRedirectUrl($requestVars['url']);
}
}
}
nag-4.2.7/app/controllers/SaveTask.php 0000664 0001750 0001750 00000013750 12654116077 015774 0 ustar jan jan getInjector()->getInstance('Horde_Registry');
$notification = $this->getInjector()->getInstance('Horde_Notification');
$form = new Nag_Form_Task($vars, $vars->get('task_id') ? sprintf(_("Edit: %s"), $vars->get('name')) : _("New Task"));
if (!$form->validate($vars)) {
// Hideous
$_REQUEST['actionID'] = 'task_form';
require NAG_BASE . '/task.php';
exit;
}
$form->getInfo($vars, $info);
// Check if we are here due to a search_return push.
if ($vars->search_return) {
Horde::url('list.php', true)->add(array('actionID' => 'search_return', 'list' => $vars->list, 'tab_name' => $vars->tab_name))->redirect();
}
// Check if we are here due to a deletebutton push
if ($vars->deletebutton) {
try {
$share = $nag_shares->getShare($info['old_tasklist']);
} catch (Horde_Share_Exception $e) {
$notification->push(sprintf(_("Access denied deleting task: %s"), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
$task = Nag::getTask($info['old_tasklist'], $info['task_id']);
$task->loadChildren();
if (!$share->hasPermission($registry->getAuth(), Horde_Perms::DELETE)) {
$notification->push(_("Access denied deleting task"), 'horde.error');
Horde::url('list.php', true)->redirect();
} else {
$storage = $this->getInjector()
->getInstance('Nag_Factory_Driver')
->create($info['old_tasklist']);
try {
$storage->delete($info['task_id']);
$notification->push(_("Task successfully deleted"), 'horde.success');
Horde::url('list.php', true)->redirect();
} catch (Nag_Exception $e) {
$notification->push(sprintf(_("Error deleting task: %s"), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
}
}
if ($prefs->isLocked('default_tasklist') ||
count($this->_getTasklists()) <= 1) {
$info['tasklist_id'] = $info['old_tasklist'] = Nag::getDefaultTasklist(Horde_Perms::EDIT);
}
try {
$share = $nag_shares->getShare($info['tasklist_id']);
} catch (Horde_Share_Exception $e) {
$notification->push(sprintf(_("Access denied saving task: %s"), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
if (!$share->hasPermission($registry->getAuth(), Horde_Perms::EDIT)) {
$notification->push(_("Access denied saving task to this task list."), 'horde.error');
Horde::url('list.php', true)->redirect();
}
/* If a task id is set, we're modifying an existing task. Otherwise,
* we're adding a new task with the provided attributes. */
if (!empty($info['task_id']) && !empty($info['old_tasklist'])) {
$storage = $this->getInjector()
->getInstance('Nag_Factory_Driver')
->create($info['old_tasklist']);
$info['tasklist'] = $info['tasklist_id'];
try {
$storage->modify($info['task_id'], $info);
} catch (Nag_Exception $e) {
$notification->push(sprintf(_("There was a problem saving the task: %s."), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
} else {
/* Check permissions. */
$perms = $this->getInjector()->getInstance('Horde_Core_Perms');
if ($perms->hasAppPermission('max_tasks') !== true &&
$perms->hasAppPermission('max_tasks') <= Nag::countTasks()) {
Horde::url('list.php', true)->redirect();
}
/* Creating a new task. */
$storage = $this->getInjector()
->getInstance('Nag_Factory_Driver')
->create($info['tasklist_id']);
// These must be unset since the form sets them to NULL
unset($info['owner']);
unset($info['uid']);
try {
$newid = $storage->add($info);
} catch (Nag_Exception $e) {
$notification->push(sprintf(_("There was a problem saving the task: %s."), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
}
$notification->push(sprintf(_("Saved %s."), $info['name']), 'horde.success');
/* Return to the last page or to the task list. */
if ($vars->savenewbutton) {
$url = Horde::url('task.php', true)->add(array(
'actionID' => 'add_task',
'tasklist_id' => $info['tasklist_id'],
'parent' => $info['parent']));
} else {
$url = Horde_Util::getFormData('url', (string)Horde::url('list.php', true));
$url = Horde::url($url, true);
}
$response->setRedirectUrl($url);
}
/**
* Return tasklists the current user has PERMS_EDIT on.
* See Bug: 13837.
*
* @return array A hash of tasklist objects.
*/
protected function _getTasklists()
{
$tasklist_enums = array();
$user = $GLOBALS['registry']->getAuth();
foreach (Nag::listTasklists(false, Horde_Perms::SHOW, false) as $tl_id => $tl) {
if (!$tl->hasPermission($user, Horde_Perms::EDIT)) {
continue;
}
$tasklist_enums[$tl_id] = Nag::getLabel($tl);
}
return $tasklist_enums;
}
}
nag-4.2.7/bin/nag-convert-datatree-shares-to-sql 0000775 0001750 0001750 00000012763 12654116077 017627 0 ustar jan jan #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/nag/lib/Application.php';
}
Horde_Registry::appInit('nag', array('cli' => true));
$db = $injector->getInstance('Horde_Db_Adapter');
$error_cnt = 0;
$delete_dt_data = false;
$answer = $cli->prompt('Do you want to keep your old datatree data or delete it?', array('Keep', 'Delete'));
if ($answer == 1) {
$delete_dt_data = true;
}
$answer = $cli->prompt(sprintf("Data will be copied into the new tables, and %s be deleted from the datatree.\n Is this what you want?", $delete_dt_data ? 'WILL' : 'WILL NOT'), array('y' => 'Yes', 'n' => 'No'));
if ($answer != 'y') {
exit;
}
/* Get the share entries */
try {
$shares_result = $db->selectAssoc('SELECT datatree_id, datatree_name FROM horde_datatree WHERE group_uid = ' . $db->quoteString('horde.shares.nag'));
} catch (Horde_Db_Exception $e) {
$cli->message($e->getMessage(), 'cli.error');
exit;
}
$query = 'SELECT attribute_name, attribute_key, attribute_value FROM horde_datatree_attributes WHERE datatree_id = ?';
foreach ($shares_result as $share_id => $share_name) {
$data = array('share_name' => $share_name);
try {
$rows = $db->select($query, array($share_id));
} catch (Horde_Db_Exception $e) {
$cli->message($e->getMessage(), 'cli.error');
exit;
}
$users = array();
$groups = array();
foreach ($rows as $row) {
if ($row['attribute_name'] == 'perm_groups') {
/* Group table entry */
$groups[] = array('group_uid' => $row['attribute_key'],
'perm' => $row['attribute_value']);
} elseif ($row['attribute_name'] == 'perm_users') {
/* User table entry */
$users[] = array('user_uid' => $row['attribute_key'],
'perm' => $row['attribute_value']);
} else {
/* Everything else goes in the main share table */
switch ($row['attribute_name']) {
case 'perm_creator':
case 'perm_default':
case 'perm_guest':
$data[$row['attribute_name']] = $row['attribute_value'];
break;
case 'owner':
$data['share_owner'] = $row['attribute_value'];
break;
case 'name':
// Note the key to the $data array is not related to
// the attribute_name field in the dt_attributes table.
$data['attribute_name'] = $row['attribute_value'];
break;
case 'desc':
$data['attribute_desc'] = $row['attribute_value'];
break;
}
}
}
/* Set flags */
$data['share_flags'] = 0;
if (count($users)) {
$data['share_flags'] |= 1;
}
if (count($groups)) {
$data['share_flags'] |= 2;
}
/* Insert the new data */
$cli->message('Migrating share data for share_id: ' . $share_id, 'cli.message');
$error = false;
$db->beginDbTransaction();
try {
$nextId = insertData('nag_shares', $data, $db);
} catch (Horde_Db_Exception $e) {
$cli->message($e->getMessage(), 'cli.error');
$error = true;
}
if (count($groups)) {
foreach ($groups as $group) {
$group['share_id'] = $nextId;
try {
insertData('nag_shares_groups', $group, $db);
} catch (Horde_Db_Exception $e) {
$cli->message($e->getMessage(), 'cli.error');
$error = true;
}
}
}
if (count($users)) {
foreach ($users as $user) {
$user['share_id'] = $nextId;
try {
$result = insertData('nag_shares_users', $user, $db);
} catch (Horde_Db_Exception $e) {
$cli->message($result->getMessage(), 'cli.error');
$error = true;
}
}
}
/* Delete the datatree data, but ONLY if it was requested */
if ($delete_dt_data && !$error) {
$cli->message('DELETING datatree data for share_id: ' . $share_id, 'cli.message');
try {
$db->delete('DELETE FROM horde_datatree_attributes WHERE datatree_id = ?', array($share_id));
$db->delete('DELETE FROM horde_datatree WHERE datatree_id = ?', array($share_id));
} catch (Horde_Db_Exception $e) {
$cli->message($e->getMessage(), 'cli.error');
$error = true;
}
}
unset($row, $rows, $data, $groups, $users);
if ($error) {
$db->rollbackDbTransaction();
$cli->message('Rollback for share data for share_id: ' . $share_id, 'cli.message');
++$error_cnt;
} else {
$db->commitDbTransaction();
$cli->message('Commit for share data for share_id: ' . $share_id, 'cli.message');
}
}
if ($error_cnt) {
$cli->message(sprintf("Encountered %u errors.", $error_cnt));
}
echo "\nDone.\n";
/**
* Helper function
*/
function insertData($table, $data, $db)
{
$fields = array_keys($data);
$values = array_values($data);
$sql = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES (' . str_repeat('?, ', count($values) - 1) . '?)';
return $db->insert($sql, $values);
}
nag-4.2.7/bin/nag-convert-sql-shares-to-sqlng 0000775 0001750 0001750 00000002570 12654116077 017155 0 ustar jan jan #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/nag/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('nag', array('cli' => true));
require_once $baseDir . 'migration/6_nag_upgrade_sqlng.php';
$db = $injector->getInstance('Horde_Db_Adapter');
$migration = new NagUpgradeSqlng($db);
$delete = $cli->prompt('Delete existing shares from the NEW backend before migrating the OLD backend? This should be done to avoid duplicate entries or primary key collisions in the storage backend from earlier migrations.', array('y' => 'Yes', 'n' => 'No'), 'n');
if ($delete == 'y' || $delete == 'Y') {
$db->delete('DELETE FROM nag_sharesng');
$db->delete('DELETE FROM nag_sharesng_users');
$db->delete('DELETE FROM nag_sharesng_groups');
}
$migration->dataUp();
nag-4.2.7/bin/nag-create-missing-add-histories-sql 0000775 0001750 0001750 00000003045 12654116077 020115 0 ustar jan jan #!/usr/bin/env php
*/
if (file_exists(__DIR__ . '/../../nag/lib/Application.php')) {
$baseDir = __DIR__ . '/../';
} else {
require_once 'PEAR/Config.php';
$baseDir = PEAR_Config::singleton()
->get('horde_dir', null, 'pear.horde.org') . '/nag/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('nag', array('cli' => true));
$history = $GLOBALS['injector']->getInstance('Horde_History');
// Run through every tasklist.
$tasklists = $nag_shares->listAllShares();
foreach ($tasklists as $tasklist => $share) {
$cli->writeln("Creating default histories for $tasklist ...");
// List all tasks.
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist);
$storage->retrieve();
$tasks = $storage->listTasks();
foreach ($tasks as $taskId => $task) {
$log = $history->getHistory('nag:' . $tasklist . ':' . $task['uid']);
$created = false;
foreach ($log as $entry) {
if ($entry['action'] == 'add') {
$created = true;
break;
}
}
if (!$created) {
$history->log('nag:' . $tasklist . ':' . $task['uid'], array('action' => 'add'), true);
}
}
}
$cli->writeln("** Default histories successfully created ***");
nag-4.2.7/bin/nag-import-openxchange 0000775 0001750 0001750 00000023012 12654116077 015454 0 ustar jan jan #!/usr/bin/env php
*/
// Init application.
if (file_exists(__DIR__ . '/../../nag/lib/Application.php')) {
$baseDir = __DIR__ . '/../';
} else {
require_once 'PEAR/Config.php';
$baseDir = PEAR_Config::singleton()
->get('horde_dir', null, 'pear.horde.org') . '/nag/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('nag', array('cli' => true, 'user_admin' => true));
// Read command line parameters.
if ($argc < 3 || $argc > 4) {
$cli->message('Too many or too few parameters.', 'cli.error');
$cli->writeln('Usage: nag-import-openxchange url [ file | user password ]');
$cli->writeln($cli->indent('url is the URL of an Open-Xchange AJAX endpoint'));
$cli->writeln($cli->indent('file is a file with space separated user names and passwords to import'));
$cli->writeln($cli->indent('personal task lists.'));
$cli->writeln($cli->indent('user and password are credentials of an administrator user to import public'));
$cli->writeln($cli->indent('task lists.'));
exit;
}
$admin = $argc == 4;
// Basic objects and variables.
$endpoint = parse_url($argv[1]);
$cli->message('Opening endpoint ' . $argv[1]);
$ox = new Horde_OpenXchange_Tasks(array('endpoint' => $argv[1]));
$users = array();
// Prepare handle on user/password list.
if ($admin) {
$fp = fopen('php://temp', 'r+');
fwrite($fp, $argv[2] . ' ' . $argv[3]);
rewind($fp);
} else {
if (!is_readable($argv[2]) || !filesize($argv[2])) {
$cli->message($argv[2] . ' is not readable or empty', 'cli.error');
exit(1);
}
$fp = fopen($argv[2], 'r');
}
if (!$fp) {
exit(1);
}
// Loop through all users.
while ($row = fgetcsv($fp, 0, ' ')) {
$user = $row[0];
if (is_null($user)) {
continue;
}
$ox->logout();
$ox->login($user, $row[1]);
$registry->setAuth($user, array());
$cli->message('Importing ' . $user . '\'s task lists');
// Reset user prefs
$prefs = $injector->getInstance('Horde_Core_Factory_Prefs')
->create('nag', array(
'cache' => false,
'user' => $user
)
);
// Load task lists for current user.
$targets = Nag::listTasklists(true, Horde_Perms::EDIT, false);
$count = 0;
$tasklists = $ox->listResources(
$admin
? Horde_OpenXchange_Tasks::RESOURCE_PUBLIC
: Horde_OpenXchange_Tasks::RESOURCE_PRIVATE
);
$default = $ox->getConfig('folder/tasks');
// Loop through all task lists.
foreach ($tasklists as $folderId => $tasklist) {
// Track UIDs, OX doesn't provide any means to find recurring tasks.
$uids = array();
// Check if we already have an task list matching the name.
$target = null;
foreach ($targets as $id => $info) {
if ($tasklist['label'] == $info->get('name')) {
$target = $id;
break;
}
}
if ($target) {
$cli->message('Task List "' . $tasklist['label'] . '" found, updating...');
} else {
// Create new task list.
$cli->message('Task List "' . $tasklist['label'] . '" not found, creating...');
$params = array(
'name' => $tasklist['label'],
'color' => Nag::randomColor(),
'description' => '',
'system' => $admin,
);
$share = Nag::addTasklist($params);
foreach ($tasklist['hordePermission']['group'] as $group => $perm) {
$share->addGroupPermission($group, $perm);
}
foreach ($tasklist['hordePermission']['user'] as $user => $perm) {
$share->addUserPermission($user, $perm);
}
$target = $share->getName();
}
if ($folderId == $default) {
$prefs->setValue('default_tasklist', $target);
}
// Initiate driver.
try {
$driver = $injector
->getInstance('Nag_Factory_Driver')
->create($target);
} catch (Nag_Exception $e) {
$cli->message(' ' . sprintf(_("Connection failed: %s"), $e->getMessage()), 'cli.error');
continue;
}
$tasks = $ox->listTasks($folderId);
// Loop through all tasks.
foreach ($tasks as $task) {
$newTask = array(
'name' => $task['title'],
'desc' => $task['description'],
'estimate' => $task['duration'],
'tags' => $task['categories'],
'uid' => $task['uid'],
'private' => $task['private'],
);
if ($task['start']) {
$newTask['start'] = $task['start'] / 1000;
}
if ($task['end']) {
$due = new Horde_Date($task['end'] / 1000, 'UTC');
$due->hour = $due->min = $due->sec = 0;
$newTask['due'] = $due->timestamp();
if ($task['alarm']) {
$newTask['alarm'] = ($task['end'] - $task['alarm']) / 60000;
}
}
switch ($task['priority']) {
case Horde_OpenXchange_Tasks::PRIORITY_HIGH:
$newTask['priority'] = 1;
break;
case Horde_OpenXchange_Tasks::PRIORITY_MEDIUM:
$newTask['priority'] = 3;
break;
case Horde_OpenXchange_Tasks::PRIORITY_LOW:
$newTask['priority'] = 5;
break;
}
if ($task['recur_type']) {
if (!isset($uids[$task['uid']])) {
$recurrence = new Horde_Date_Recurrence(new Horde_Date($newTask['due']));
switch ($task['recur_type']) {
case 3:
if ($task['day_in_month']) {
$task['recur_type'] = Horde_Date_Recurrence::RECUR_MONTHLY_DATE;
} else {
$task['recur_type'] = Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY;
}
break;
case 4:
if ($task['day_in_month']) {
$task['recur_type'] = Horde_Date_Recurrence::RECUR_YEARLY_DATE;
} else {
$task['recur_type'] = Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY;
}
break;
}
$recurrence->setRecurType($task['recur_type']);
if (!empty($task['recur_count'])) {
$recurrence->setRecurCount($task['recur_count']);
} elseif ($task['recur_end']) {
$end = new Horde_Date($task['recur_end'] / 1000, 'UTC');
$end->mday++;
$end->hour = $end->min = $end->sec = 0;
$recurrence->setRecurEnd($end);
}
if (!empty($task['recur_interval'])) {
$recurrence->setRecurInterval($task['recur_interval']);
}
if (!empty($task['recur_days'])) {
$recurrence->setRecurOnDay($task['recur_days']);
}
$newTask['recurrence'] = $recurrence;
$uids[$task['uid']] = $newTask;
}
if ($task['status'] == Horde_OpenXchange_Tasks::STATUS_DONE) {
$due = new Horde_Date($task['end'] / 1000, 'UTC');
$uids[$task['uid']]['recurrence']->addCompletion(
$due->year, $due->month, $due->mday
);
}
if ($task['end_date'] > $uids[$task['uid']]['end_date']) {
if ($task['status'] == Horde_OpenXchange_Tasks::STATUS_DONE) {
$uids[$task['uid']]['completed'] = true;
$uids[$task['uid']]['completed_date'] = $task['completed'] / 1000;
} else {
$uids[$task['uid']]['completed'] = false;
$uids[$task['uid']]['completed_date'] = null;
}
}
continue;
}
try {
$driver->add($newTask);
$count++;
} catch (Nag_Exception $e) {
$cli->message(' ' . $e->getMessage(), 'cli.error');
}
}
// Loop through all recurring tasks.
foreach ($uids as $task) {
try {
$driver->add($task);
$count++;
} catch (Nag_Exception $e) {
$cli->message(' ' . $e->getMessage(), 'cli.error');
}
}
}
$cli->message(' Added ' . $count . ' tasks', 'cli.success');
$count = 0;
}
nag-4.2.7/bin/nag-import-vtodos 0000775 0001750 0001750 00000002741 12654116077 014501 0 ustar jan jan #!/usr/bin/env php
*/
$baseFile = __DIR__ . '/../../nag/lib/Application.php';
if (file_exists($baseFile)) {
require_once $baseFile;
} else {
require_once 'PEAR/Config.php';
require_once PEAR_Config::singleton()
->get('horde_dir', null, 'pear.horde.org') . '/nag/lib/Application.php';
}
Horde_Registry::appInit('nag', array('cli' => true));
// Read command line parameters.
if (count($argv) != 3) {
$cli->message('Too many or too few parameters.', 'cli.error');
usage();
}
$tasklist = $argv[1];
$user = $argv[2];
// Read standard input.
$vtodo = $cli->readStdin();
if (empty($vtodo)) {
$cli->message('No import data provided.', 'cli.error');
usage();
}
// Set user.
$registry->setAuth($user, array());
// Import data.
try {
$result = $registry->tasks->import($vtodo, 'text/calendar', $tasklist);
} catch (Horde_Exception $e) {
$cli->fatal($result->toString());
}
$cli->message('Imported successfully ' . count($result) . ' tasks', 'cli.success');
function usage()
{
$GLOBALS['cli']->writeln('Usage: nag-import-vtodos tasklist user');
exit;
}
nag-4.2.7/config/.htaccess 0000664 0001750 0001750 00000000174 12654116077 013453 0 ustar jan jan
Require all denied
Deny from all
nag-4.2.7/config/conf.xml 0000664 0001750 0001750 00000002151 12654116077 013321 0 ustar jan jan
Storage System Settingssql
nag_tasksTasklist Handler Settingsdefault
Menu settingstrue
nag-4.2.7/config/hooks.php.dist 0000664 0001750 0001750 00000003045 12654116077 014453 0 ustar jan jan \0', $text);
// $text = preg_replace('/(bug|ticket|request|enhancement|issue):\s*#?(\d+)/i', '\0', $text);
//
// $text = preg_replace_callback('/\[\[note: ?(.*)\]\]/i', create_function('$m', 'return \'\' . htmlspecialchars($m[0]) . \'\';'), $text);
// $text = preg_replace_callback('/\[\[task: ?(.*)\]\]/i', create_function('$m', 'return \'\' . htmlspecialchars($m[0]) . \'\';'), $text);
//
// return $text;
// }
/**
* TODO
*/
// public function description_help()
// {
// return '
To create a link to a bug, use #123 where 123 is the bug number. To create a link to another task, use [[task: name]], where name is the beginning of the task name. To create a link to a note, use [[note: title]] where title is the beginning of the note title.
';
// }
}
nag-4.2.7/config/menu.php.dist 0000664 0001750 0001750 00000002422 12654116077 014272 0 ustar jan jan 'http://www.example.com/',
* 'text' => 'Example, Inc.',
* 'icon' => 'example.png',
* 'icon_path' => 'http://www.example.com/images/',
* 'target' => '_blank',
* 'onclick' => ''
* );
*
* You can also add a "separator" (a spacer) between menu items. To add a
* separator, simply add a new string to the $_menu array set to the text
* 'separator'. It should look like this:
*
* $_menu[] = 'separator';
*
* $Id: 1e96670cdc2393844249e3e71d187b67b07faff4 $
*/
$_menu = array();
/* Add your custom entries below this line. */
nag-4.2.7/config/prefs.php 0000664 0001750 0001750 00000026416 12654116077 013514 0 ustar jan jan _("General Preferences"),
'label' => _("Display Preferences"),
'desc' => _("Change your task sorting and display preferences."),
'members' => array('tasklist_columns', 'sortby', 'altsortby', 'sortdir'),
);
$prefGroups['deletion'] = array(
'column' => _("General Preferences"),
'label' => _("Deleting Tasks"),
'desc' => _("Delete behaviour"),
'members' => array('delete_opt', 'purge_completed_interval', 'purge_completed_keep'),
);
$prefGroups['tasks'] = array(
'column' => _("General Preferences"),
'label' => _("Task Defaults"),
'desc' => _("Defaults for new tasks"),
'members' => array('default_due', 'default_due_days', 'default_due_time'),
);
$prefGroups['share'] = array(
'column' => _("Task List and Share Preferences"),
'label' => _("Default Task List"),
'desc' => _("Choose your default task list."),
'members' => array('default_tasklist'),
);
$prefGroups['sync'] = array(
'column' => _("Task List and Share Preferences"),
'label' => _("Synchronization Preferences"),
'desc' => _("Choose the task lists to use for synchronization with external devices."),
'members' => array('sync_lists', 'activesync_no_multiplex'),
);
$prefGroups['notification'] = array(
'column' => _("Task List and Share Preferences"),
'label' => _("Notifications"),
'desc' => _("Choose if you want to be notified of task changes and task alarms."),
'members' => array('task_notification', 'task_notification_exclude_self', 'task_alarms_select'),
);
$prefGroups['external'] = array(
'column' => _("Task List and Share Preferences"),
'label' => _("External Data"),
'desc' => _("Show data from other applications or sources."),
'members' => array('show_external'),
);
// columns in the list view
$_prefs['tasklist_columns'] = array(
'value' => 'a:2:{i:0;s:8:"priority";i:1;s:3:"due";}',
'type' => 'multienum',
'enum' => array(
'tasklist' => _("Task List"),
'priority' => _("Priority"),
'assignee' => _("Assignee"),
'due' => _("Due Date"),
'start' => _("Start Date"),
'estimate' => _("Estimated Time")
),
'desc' => _("Select the columns that should be shown in the list view:")
);
// user preferred sorting column
$_prefs['sortby'] = array(
'value' => Nag::SORT_PRIORITY,
'type' => 'enum',
'enum' => array(
Nag::SORT_PRIORITY => _("Priority"),
Nag::SORT_NAME => _("Task Name"),
Nag::SORT_DUE => _("Due Date"),
Nag::SORT_START => _("Start Date"),
Nag::SORT_COMPLETION => _("Completed?"),
Nag::SORT_ESTIMATE => _("Estimated Time"),
Nag::SORT_ASSIGNEE => _("Assignee"),
Nag::SORT_OWNER => _("Task List")
),
'desc' => _("Sort tasks by:"),
);
// alternate sort column
$_prefs['altsortby'] = array(
'value' => Nag::SORT_DUE,
'type' => 'enum',
'enum' => array(
Nag::SORT_PRIORITY => _("Priority"),
Nag::SORT_NAME => _("Task Name"),
Nag::SORT_DUE => _("Due Date"),
Nag::SORT_START => _("Start Date"),
Nag::SORT_COMPLETION => _("Completed?"),
Nag::SORT_ESTIMATE => _("Estimated Time"),
Nag::SORT_ASSIGNEE => _("Assignee"),
Nag::SORT_OWNER => _("Task List")
),
'desc' => _("Then:"),
);
// user preferred sorting direction
$_prefs['sortdir'] = array(
'value' => Nag::SORT_ASCEND,
'type' => 'enum',
'enum' => array(
Nag::SORT_ASCEND => _("Ascending"),
Nag::SORT_DESCEND => _("Descending")
),
'desc' => _("Sort direction:"),
);
// preference for delete confirmation dialog.
$_prefs['delete_opt'] = array(
'value' => 1,
'type' => 'checkbox',
'desc' => _("Do you want to confirm deleting entries?"),
);
// how often to purge completed tasks?
$_prefs['purge_completed_interval'] = array(
'value' => 0,
'type' => 'enum',
'enum' => array_merge(array(0 => _("Never")), Horde_LoginTasks::getLabels()),
'desc' => _("Purge completed tasks how often:")
);
$_prefs['purge_completed_keep'] = array(
'value' => 30,
'type' => 'number',
'desc' => _("Purge completed tasks older than this amount of days.")
);
// default to tasks having a due date?
$_prefs['default_due'] = array(
'value' => 0,
'type' => 'checkbox',
'desc' => _("When creating a new task, should it default to having a due date?"),
);
// default number of days out for due dates
$_prefs['default_due_days'] = array(
'value' => 1,
'type' => 'number',
'zero' => true,
'desc' => _("When creating a new task, how many days in the future should the default due date be (0 means today)?"),
);
// default due time
$_prefs['default_due_time'] = array(
'value' => 'now',
'type' => 'enum',
'enum' => array(),
'desc' => _("What do you want to be the default due time for tasks?"),
'on_init' => function($ui) {
$enum = array('now' => _("The current hour"));
$twentyfour = $GLOBALS['prefs']->getValue('twentyFour');
for ($i = 0; $i < 24; ++$i) {
$value = sprintf('%02d:00', $i);
$enum[$value] = $twentyfour
? $value
: sprintf('%02d:00 ' . ($i >= 12 ? _("pm") : _("am")), ($i % 12 ? $i % 12 : 12));
}
$ui->prefs['default_due_time']['enum'] = $enum;
}
);
// new task notifications
$_prefs['task_notification'] = array(
'value' => '',
'type' => 'enum',
'enum' => array(
'' => _("No"),
'owner' => _("On my task lists only"),
'show' => _("On all shown task lists"),
'read' => _("On all task lists I have read access to")
),
'desc' => _("Choose if you want to be notified of new, edited, and deleted tasks by email:"),
);
$_prefs['task_notification_exclude_self'] = array(
'value' => 0,
'locked' => false,
'type' => 'checkbox',
'desc' => _("Don't send me a notification if I've added, changed or deleted the task?")
);
// alarm methods
$_prefs['task_alarms_select'] = array(
'type' => 'special',
'handler' => 'Nag_Prefs_Special_TaskAlarms',
'suppress' => function() {
return empty($GLOBALS['conf']['alarms']['driver']);
}
);
$_prefs['task_alarms'] = array(
'value' => 'a:1:{s:6:"notify";a:0:{}}'
);
// show data from other applications that can be listed as tasks?
$_prefs['show_external'] = array(
'value' => 'a:0:{}',
'type' => 'multienum',
'enum' => array('whups' => $GLOBALS['registry']->get('name', 'whups')),
'desc' => _("Show data from any of these other applications in your task list?"),
'suppress' => function() {
return !$GLOBALS['registry']->hasMethod('getListTypes', 'whups');
}
);
// show complete/incomplete tasks?
$_prefs['show_completed'] = array(
'value' => 1,
'type' => 'enum',
'enum' => array(
Nag::VIEW_ALL => _("All tasks"),
Nag::VIEW_INCOMPLETE => _("Incomplete tasks"),
Nag::VIEW_COMPLETE => _("Complete tasks"),
Nag::VIEW_FUTURE => _("Future tasks")
),
'desc' => _("Show complete, incomplete, or all tasks in the task list?"),
);
// default tasklists
// Set locked to true if you don't want users to have multiple task lists.
$_prefs['default_tasklist'] = array(
'value' => '',
'type' => 'enum',
'enum' => array(),
'desc' => _("Your default task list:"),
'on_init' => function($ui) {
$enum = array();
foreach (Nag::listTasklists(false, Horde_Perms::EDIT, false) as $key => $val) {
$enum[$key] = Nag::getLabel($val);
}
$ui->prefs['default_tasklist']['enum'] = $enum;
},
'on_change' => function() {
$GLOBALS['injector']->getInstance('Nag_Factory_Tasklists')
->create()
->setDefaultShare($GLOBALS['prefs']->getValue('default_tasklist'));
$sync = @unserialize($GLOBALS['prefs']->getValue('sync_lists'));
$haveDefault = false;
$default = Nag::getDefaultTasklist(Horde_Perms::EDIT);
foreach ($sync as $cid) {
if ($cid == $default) {
$haveDefault = true;
break;
}
}
if (!$haveDefault) {
$sync[] = $default;
$GLOBALS['prefs']->setValue('sync_lists', serialize($sync));
}
}
);
// store the task lists to diplay
$_prefs['display_tasklists'] = array(
'value' => 'a:0:{}'
);
// Tasklists use for synchronization
$_prefs['sync_lists'] = array(
'value' => 'a:0:{}',
'type' => 'multienum',
'enum' => array(),
'desc' => _("Select the task lists that, in addition to the default, should be used for synchronization with external devices:"),
'on_init' => function($ui) {
$enum = array();
$sync = @unserialize($GLOBALS['prefs']->getValue('sync_lists'));
if (empty($sync)) {
$GLOBALS['prefs']->setValue('sync_lists', serialize(array(Nag::getDefaultTasklist())));
}
foreach (Nag::listTasklists(false, Horde_Perms::EDIT, false) as $key => $list) {
if ($list->getName() != Nag::getDefaultTasklist(Horde_Perms::EDIT)) {
$enum[$key] = Nag::getLabel($list);
}
}
$ui->prefs['sync_lists']['enum'] = $enum;
},
'on_change' => function() {
$sync = @unserialize($GLOBALS['prefs']->getValue('sync_lists'));
$haveDefault = false;
$default = Nag::getDefaultTasklist(Horde_Perms::EDIT);
foreach ($sync as $cid) {
if ($cid == $default) {
$haveDefault = true;
break;
}
}
if (!$haveDefault) {
$sync[] = $default;
$GLOBALS['prefs']->setValue('sync_lists', serialize($sync));
}
if ($GLOBALS['conf']['activesync']['enabled'] && !$GLOBALS['prefs']->getValue('activesync_no_multiplex')) {
try {
$sm = $GLOBALS['injector']->getInstance('Horde_ActiveSyncState');
$sm->setLogger($GLOBALS['injector']->getInstance('Horde_Log_Logger'));
$devices = $sm->listDevices($GLOBALS['registry']->getAuth());
foreach ($devices as $device) {
$sm->removeState(array(
'devId' => $device['device_id'],
'id' => Horde_Core_ActiveSync_Driver::TASKS_FOLDER_UID,
'user' => $GLOBALS['registry']->getAuth()
));
}
$GLOBALS['notification']->push(_("All state removed for your ActiveSync devices. They will resynchronize next time they connect to the server."));
} catch (Horde_ActiveSync_Exception $e) {
$GLOBALS['notification']->push(_("There was an error communicating with the ActiveSync server: %s"), $e->getMessage(), 'horde.error');
}
}
}
);
// @todo We default to using multiplex since that is the current behavior
// For Nag 5 we should default to separate.
$_prefs['activesync_no_multiplex'] = array(
'type' => 'checkbox',
'desc' => _("Support separate task lists?"),
'value' => 0
); nag-4.2.7/config/routes.php 0000664 0001750 0001750 00000000325 12654116077 013705 0 ustar jan jan connect('/t/complete',
array(
'controller' => 'CompleteTask',
));
$mapper->connect('/t/save',
array(
'controller' => 'SaveTask',
));
nag-4.2.7/docs/vtodo/todo.ics 0000664 0001750 0001750 00000000702 12654116077 014135 0 ustar jan jan BEGIN:VCALENDAR
BEGIN:VTODO
DTSTAMP:19980130T134500Z
SEQUENCE:2
UID:<1234@example.com>
ORGANIZER:MAILTO:
ATTENDEE;PARTSTAT=ACCEPTED:MAILTO:
DUE:19980415T235959
STATUS:NEEDS-ACTION
SUMMARY:Submit Income Taxes
BEGIN:VALARM
ACTION:AUDIO
TRIGGER;VALUE=DATE-TIME:19980403T120000
ATTACH;FMTTYPE=audio/basic:http://host.com/pub/audio-files/ssbanner.aud
REPEAT:4
DURATION:PT1H
END:VALARM
END:VTODO
END:VCALENDAR
nag-4.2.7/docs/CHANGES 0000600 0001750 0001750 00000073241 12654116077 012326 0 ustar jan jan ------
v4.2.7
------
[mjr] Enforce child tasks must be completed before parent tasks in edit form
(Bug #12308).
[jan] Use access rules compatible with both Apache 2.2 and 2.4.
[mjr] Fix possible fatal error due to tasks created with an alarm but no due
date (Bug #14214).
[mjr] Fix storage of parent field when using Kolab driver (Bug #14197,
mike.gabriel@das-netzwerkteam.de).
[mjr] Fix switching tab/filters when using Kolab driver (Bug #14196,
mike.gabriel@das-netzwerkteam.de).
[mjr] Fix moving subtasks to another tasklist when parent task is moved (Bug
#14209).
[mjr] Fix fatal error when saving tasks when system shares are present in
certain cases.
[mjr] Return recurring tasks via listTimeObjects API (Bug #14137).
[mjr] Prevent orphaning tasks via the API.
------
v4.2.6
------
[jan] Update Italian translation.
[mjr] Fix indentation level when searching (Bug #14138).
[mjr] Remove object reference from Content when object is deleted (Bug #14112).
[mjr] Remove tags from Content when tagged object is deleted (Bug #14112).
[jan] Fix linking to external tasks from smartmobile view.
------
v4.2.5
------
[jan] Fix positioning of dialog windows.
[jan] Don't delete existing completions when editing a recurring task.
[jan] Only delete the last completion when toggling a recurring task to
incomplete.
------
v4.2.4
------
[jan] Don't list task lists without SHOW permissions (Bug #13837).
[jan] Use consistent checkbox.
[jan] Don't allow deletion of tasks with sub-tasks (Bug #13809).
[jan] Don't allow completion of tasks with incomplete sub-tasks and vice versa.
[jan] Don't wipe out whole task lists when PUTing individual tasks via WebDAV.
------
v4.2.3
------
[mjr] Fix issue with initially creating nag_shares table.
------
v4.2.2
------
[jan] Export nicer display names for users and resources via WebDAV.
[jan] Export custom owner and read-only properties via WebDAV.
[jan] Fix search attribute of SQL shares on Oracle.
[mjr] Fix adding a new tasklist to the syncronized tasklists (Bug #13642).
[jan] Pass current start date of recurring tasks to dynamic interface.
[jan] Fix setting task list name for external tasks.
[jan] Fix link to create sub-task from a task view.
[jan] Fix listing of recurring tasks with start dates.
[mjr] Add missing categories to EAS tasks.
[jan] Fix sidebar display of system task lists in basic view.
[jan] Fix retrieving individual tasks via WebDAV.
------
v4.2.1
------
[jan] Don't include external tasks with WebDAV task list.
[jan] Fix displayed CalDAV URLs for shared task lists (Bug #12380).
[mjr] Fix remembering auto determined default share (Bug #13360).
[mjr] Add EAS client created shares to the sync_lists preference (Bug #13359).
[mjr] Add missing API support for searchTags API (Bug #13364).
[mjr] Fix detecting task completion when sending task change notifications
(Alfonso Marín Marín , Bug #13350).
------
v4.2.0
------
[mjr] Fix returning to previously selected tab after task is saved (Bug
#13308).
[mjr] Fix fatal error when saving a task, only one tasklist exists, and at
least one SmartList exists.
-----------
v4.2.0beta2
-----------
[jan] Always display system task lists to administrators.
[mjr] Group task fields into tabs on edit form (Request #13228).
-----------
v4.2.0beta1
-----------
[jan] Only use URL rewriting for saving/completing tasks if pretty URLs are
enabled in the configuration.
[jan] Don't include smart lists in top menu.
[jan] Fix top menu URLs to create new tasks in specific task lists.
------------
v4.2.0alpha1
------------
[jan] Show external tasks in the correct task type.
[jan] Purge old tasks from versions < 2.3 too.
[jan] Allow to synchronize shared task lists via CalDAV (Request #12380).
[jan] Add script to import tasks from Open-Xchange.
[jan] Add recurrence controls to basic view (Request #11545).
[mjr] Add button for creating sub-task from task view (Request #12309).
[mjr] Add ability to return to existing search results after navigating to a
task (Request #12879).
[jan] Make shares table compatible with Oracle.
[mjr] Add API support for multiple tasklists via ActiveSync.
------
v4.1.5
------
[jan] Fix link to task in alarm emails.
[jan] Update Korean translation (Deokgon Kim ).
------
v4.1.4
------
[jan] Update Danish translation (Erling Preben Hansen ).
[jan] Don't return future tasks from cost objects API.
------
v4.1.3
------
[jan] SECURITY: Fix XSS vulnerabilities when deleting task lists.
[jan] Fix updating alarm if completing a task recurrence.
[jan] Fix editing tasks via CalDAV (Bug #12745).
------
v4.1.2
------
[jan] Fix highlighting task type tabs in list view.
[jan] Fix fatal errors if DAV support is disabled (Bug #12481).
[mjr] Fix requesting changes by modification sequence (Bug #12507, Thomas
Jarosch ).
------
v4.1.1
------
[jan] Display CalDAV URL of system tasklists (Bug #12342).
[mjr] Only query Horde_History when it's required.
[jan] Fix system tasklist listing via WebDAV.
[rla] Add system tasklist support for Nag CalDAV access (Request #12342).
[jan] Fix task description tooltip (Bug #12421).
[mjr] Persist the tasklist and parent when creating a new task and using "Save
and New" (Bug #12400).
[mjr] Fix tag browsing of shared tasklists (Bug #12405).
[mjr] Fix importing VTODOs containing RELATED-TO fields (Bug #12355).
[mjr] Add API methods for using history modification sequences.
------
v4.1.0
------
[jan] Don't include external tasks in listTasks() API results.
-----------
v4.1.0beta2
-----------
[jan] Fix incorrect dependencies.
-----------
v4.1.0beta1
-----------
[jan] Add CalDAV server support (Request #4267).
[jan] Update design to closer match the Horde 5 UI.
------
v4.0.3
------
[jan] Fix recurring tasks in Kolab driver (Thomas Jarosch
, Bug #12222).
[mjr] Fix setting alarm time when syncing (Bug #12201,
).
[mjr] Fix issue causing due dates to be offset when syncing (Bug #12200,
)
[jan] Fix listing alarms of recurring tasks.
[jan] Fix default task list in portal block (Bug #12133).
[jan] Fix displaying of due dates of recurring events in some views.
[jan] Update broken rows due to migration bug in PostgreSQL (Bug #12101).
[mjr] Fix uncompleting tasks from task view in smartmobile view
(ctimoteo@sapo.pt, Bug #12098).
[jan] Include parent tasks in cost object labels.
[jan] Only return completed tasks up to a week old as cost objects.
[mjr] Set correct timezone on incoming ActiveSync tasks (Bug #12053).
[jan] Update French translation (Paul De Vlieger
).
------
v4.0.2
------
[mjr] Fix closing the quick task dialog when cancel is pressed (Bug #11848).
[mjr] Fix displaying and editing tasks from search results (Bug #11847).
[jan] Update Basque translation (Ibon Igartua ).
[mjr] Fix incoming task sync with certain combinations of due date values.
[mjr] Fix quick task addition without session cookies enabled (Bug #11875,
Thomas Jarosch ).
[jan] Fix editing tasks from the merged list view in smartmobile mode (Bug
#11840).
[jan] Use default Kolab folder for default taks list preference.
[jan] Mark Kolab folder as default when changing default task list preference.
------
v4.0.1
------
[jan] Use Kolab modification and creation dates in the UI (Bug #11591).
[gwr] Ignore empty dates (Bug #11736).
[jan] Use encoded UIDs as IDs in Kolab driver.
[mjr] Fix support for due/start dates for certain ActiveSync clients (Bug
#11693).
[jan] Fix importing tasks from iCalendar (Bug #11681).
[jan] Fix listing of recurring tasks via timeobjects API.
------
v4.0.0
------
[jan] Update Polish translation (Krzysztof Kozera ).
---------
v4.0.0RC1
---------
[jan] Fix export links for tasklists (Bug #11576).
[mjr] Fix various issues with importing CSV files.
[mjr] Format datetime fields when exporting a task as a hash (Bug #11537).
[jan] Fix retrieving tasks from Kolab backend by UID (Bug #11532).
[jan] Fix toggling tasklists (Bug #11511).
[jan] Fix listing tasks from Kolab backends.
[jan] Fix sending alarm e-mail messages.
-----------
v4.0.0beta2
-----------
[jan] Synchronize tags with Kolab categories.
[mjr] Fix deleting tasks from the task form.
[mjr] Implement Purge Completed Tasks LoginTask (Request #2520).
[gwr] Mark the initial default share as such with the Kolab backend.
[gwr] Always mark the initial share as default tasklist.
[jan] Move complete task list management into the sidebar.
[jan] Allow to edit task list color in traditional view.
[mjr] Tweak display of smartlist links in list view.
[mjr] Fix fatal recursion error when searching with smartlists were present.
-----------
v4.0.0beta1
-----------
[mjr] Add support for smart tasklists.
[mjr] Migrate categories to tags.
[jan] Fix fatal error if the task form doesn't validate.
[jan] Add recurring tasks (Request #2150).
----------
v3.0.9-git
----------
[mms] Fix getTasklist() API call.
[jan] Fix changing sort direction.
[jan] Fix sorting by owner if displaying external task sources.
[jan] Update Turkish translation (İstanbul Technical University).
------
v3.0.8
------
[jan] Update Swedish translation (Per Olof Ljungmark ).
[jan] Update Italian translation (Massimo Malabotta ).
[jan] Update Hungarian translation (Zoltán Németh ).
------
v3.0.7
------
[jan] Fix setting custom alarm methods (Bug #9543).
[jan] Update Japanese translation (Hiromi Kimura ).
------
v3.0.6
------
[jan] Don't display task details of private tasks via the API (Bug #10712).
[jan] Use correct locale when parsing quick tasks (thpo+horde@dotrc.de, Bug
#10720).
------
v3.0.5
------
[cjh] Nicer date/time input with fewer input fields and helper javascript.
[mjr] Add ability to choose which tasklists to synchronize.
[mjr] Fix task export to ActiveSync for tasks with no due dates or reminders.
------
v3.0.4
------
[jan] Add missing parameters when PUTing tasks (Bug #10545).
[mjr] Fix removeUserData implementation.
[gwr] Fix Kolab object attribute handling for tasks.
[gwr] Avoid including the owner name into the default share.
------
v3.0.3
------
[jan] Don't load all shares with requested permissions from the backend if
$conf['share']['hidden'] is enabled.
------
v3.0.2
------
[jan] Add Ukrainian translation (Andriy Kopystyansky ).
------
v3.0.1
------
[jan] Fix reversed logic of displaying sub-task tree icons depending on the
text direction (Bug #10033).
[jan] Fix encoding of non-ascii characters in parent task drop-down.
[mjr] Fix editing tasklists (Bug #9965).
----
v3.0
----
[jan] Fix application-specific permission checks (Bug #9786).
[mjr] Datatree share to SQL upgrade script refactored for Horde 4.
[jan] Fix creating new tasks through WebDAV (Bug #9675).
[jan] Move all executable scripts to bin/ and prefix with nag-.
--------
v3.0-RC1
--------
[jan] Update installation and upgrade instructions.
[gwr] Adapted the Kolab driver to Horde 4.
----------
v3.0-BETA1
----------
[mjr] Fix saving tasks via PUT method.
[mjr] Fix deleting task lists.
[mjr] Fix hiding the External prefs group (Bug #9643).
-----------
v3.0-ALPHA1
-----------
[jan] Provide default configuration files instead of .dist versions.
[jan] Send alarm notifications with HTML part and convert to Horde_View.
[jan] Default task lists no longer have the user name as the ID.
[jan] Create a default task list if the user doesn't own any yet.
[jan] Add start date to possible colums in task list (Joel Smith
, Request #9083).
[jan] Add system task lists (Request #2059).
[jan] Set colors per task list (Request #7480).
[cjh] Quick Add support: there is a javascript UI element for quickly adding
tasks, and an API method (tasks/quickAdd) that provides the same
functionality.
[jan] Add individual notification methods for single tasks (Alfonso Marín
Marín ).
------
v2.3.7
------
[jan] Add upgrade scripts for next-generation SQL share driver.
------
v2.3.6
------
[jan] Remove stray closing tag when displaying the date of completion.
[mjr] Do not remove history entries when removing user data (Bug #8755).
------
v2.3.5
------
[jan] Set an alarm to one minute, if users try to set an alarm without time.
[jan] Add missing preference for not sending update notifications to yourself
(Joel Smith , Request #8978).
[gwr] Fix organizer field not saved in tasks (kolab/issue3888).
------
v2.3.4
------
[jan] Add and fix Oracle-specific SQL scripts.
[jan] Fix importing due date attribute from iCalendar (Bug #8644).
[jan] Fix charset when exporting tasks to iCalendar 2.0 (Bug #8637).
[jan] Add Croatian translation (Matej Vela ).
------
v2.3.3
------
[mms] Upgrade prototype.js to v1.6.1.
[jan] Fix synchronization with output compression enabled (Bug #7769).
[jan] Display application name as task list name when listing external tasks.
[jan] Fix importing vTodo data including more iCalendar components.
------
v2.3.2
------
[jan] Don't allow to set alarms if no due date has been set.
[cjh] Work around BC break with Horde versions before 3.2 (Bug #7820).
[cjh] Add URL access to tasks by "starts-with" search on the task name.
[cjh] Add hooks for altering the displayed task description and showing help
text next to the task description entry field.
[cjh] Add estimated time to the fields available in the task list.
[jan] Fix link escaping in notification messages (Alfonso Marín Marín
, Bug #7509).
------
v2.3.1
------
[cjh] Add a PostgreSQL-specific upgrade script for 2.2 to 2.3.
[cjh] Fix fatal error when completing tasks (Bug #7400).
[mms] Upgrade prototype.js to v1.6.0.3.
----
v2.3
----
[jan] Change group field in shares table to work with LDAP groups (Bug #6883).
[jan] Log completion date if adding a completed task (Bug #7275).
[jan] Fix user name conversion with user hooks in the task list panel
(Bug #7366).
[jan] Fix displayed WebDAV subscription URLs in the task list manager.
--------
v2.3-RC1
--------
[jan] Log moving of tasks in the history backend (Bug #3207).
[jan] Fix deleting all tasks over WebDAV (Bug #7004).
[jan] Add Estonian translation (Alar Sing ).
[jan] Send a more detailed notification message and use the recipient's
preferred language and date/time format after a task has changed.
[jan] Add Basque translation (Euskal Herriko Unibertsitatea EHU/UPV
).
[jan] Fix task relationship getting lost when importing tasks
(tkrah@fachschaft.imn.htwk-leipzig.de, bb.apc.ag, Bug #6770).
[jan] Add preference to set columns for the list view.
[jan] Allow to set task assignee.
[jan] Add options to export screen for choosing task lists and task states.
[mjr] Remove user permissions on all shares when deleting a user.
[mjr] Fix issue with removeUserData api that caused the deleted user's task list
to not be deleted (Bug #6969).
----
v2.2
----
[cjh] Improve resource usage in datatree_to_sql share migration script
(Bug #6740).
[jan] Allow to import all fields that can be exported.
[cjh] Fix displayed WebDAV subscription URLs in the panel (Bug #6709).
--------
v2.2-RC3
--------
[bak] Move tasklists into per-owner subdirectories when viewed through WebDAV.
(Request #6595)
[cjh] Apply fix for http://dev.rubyonrails.org/ticket/11473 to prototype.js
(Request #6590).
[cjh] Add an upgrade script for the new SQL share driver (Request #6109).
[jan] Correctly determine default task list in the API (SyncML, WebDAV).
[cjh] Show who created or made the last change to a task along with the date
(Request #6305).
[jan] Add Turkish translation (METU ).
--------
v2.2-RC2
--------
[jan] SECURITY: Fix privilege escalation in Horde API.
[cjh] SECURITY: Fix missing ownership validation on share changes.
[cjh] Fix sorting tasks by Task list.
[jan] Fix tasks losing their parent task when being completed (Bug #6035).
[cjh] Add the ability to filter the list of task lists in the panel.
[cjh] New share management UI that doesn't require JavaScript.
[jan] Fix 2006-04-18_add_creator_and_assignee_fields.php upgrade script.
--------
v2.2-RC1
--------
[jan] Implement WebDAV access.
[jan] Add exportTasklist() API method.
[jan] Add SQL upgrade script.
[jan] Show estimated time including sub-tasks.
[cjh] Fix generation of UIDs with PHP 5.2+.
----------
v2.2-ALPHA
----------
[cjh] Add preferences for turning due dates on by default, setting the
default due date's number of days in the future, and setting the
default due date's time.
[cjh] Add vCalendar 2.0 alarm export (munzli@olmero.ch, Bug #4851).
[jan] Add sub-tasks.
[jan] Add start dates for tasks.
[jan] Add support for the Horde_Alarm framework (requires Horde 3.2).
[cjh] Default to a javascript quick search, if available, with options
for the full search and a direct link to full search if javascript
is unavailable.
[cjh] Allow explicitly searching All, Incomplete, or Completed tasks
(Request #4222).
[cjh] Allow configuring the tasklists that the task summary block shows,
and let the user set a title for the block to differentiate multiple
instances (Request #2388).
[cjh] Show task description tooltips in the task summary block (Request #3444).
[cjh] Store completion date and add history entries for task completion.
[cjh] Move tasklist selection/deselection to a collapsible panel.
[mas] Conform to WCAG 1.0 Priority 2/Section 508 accessibility guidelines.
(Request #4080)
[cjh] Allow using the checkboxes to mark tasks as incomplete as well
as complete (michael.sheldon@credativ.de, Request #4250).
[cjh] Sort date fields correctly with JS table sorting.
[jan] Add private flag.
[jan] Add a field for estimated time being spent on a task.
[mdj] Add support for split read/write database.
[mdj] Add failover support for SQL backend.
[cjh] Add a tree block for showing current alarms.
------
v2.1.4
------
[jan] SECURITY: Fix privilege escalation in Horde API.
[cjh] SECURITY: Fix missing ownership validation on share changes.
[cjh] Send iCalendar data as UTF-8.
------
v2.1.3
------
[jan] Show alarms for overdue tasks too.
[jan] Add Catalan translation (Jordi Giralt ).
------
v2.1.2
------
[jan] Show error message if imported file didn't contain tasks.
[jan] Add categories from imported contacts to the user's categories.
[jan] Fix import of CSV data.
[jan] Add Slovenian translation (Duck ).
[jan] Show personal tasklist by default with disabled preferences (Bug #4078).
------
v2.1.1
------
[jan] Only load completed/incomplete tasks from backend where sufficient
(Request #2387).
[jan] Don't show tooltips for tasks without read permissions (Bug #3836).
[jan] Fix fatal error when dealing with very old tasks that don't have a UID
yet (Bug #3818).
[jan] Add 1.1 to 2.x upgrade script for Oracle.
----
v2.1
----
[ben] Better support for MS-SQL.
--------
v2.1-RC1
--------
[jan] Add Portuguese translation (Manuel Menezes de Sequeira
).
[jan] Add preference to send email notifications to users when tasks have
been added, edited, or deleted in their calendars (kevin_myer@iu13.org,
Request #2332).
[jan] Confirm task deletions (Request #1155).
[jan] Add CLI script to import vTodo data.
[cjh] Add support for dynamic re-sorting of the task list, including saving
the sort preferences on any changes.
[cjh] Deprecate the DataTree tasks driver.
[cjh] Make the completed checkbox in task view functional
(martin@mein-horde.de, Bug #2157).
[mas] Change any output of and tags to and for better
accessibility support.
[jan] Add permissions to restrict number of tasks.
[jan] Add print view to task list (kevin_myer@iu13.org, Bug #1875).
[cjh] Add ics.php, which can be used for integration with Sunbird,
iCal, et. al.
[jan] Show category colors in portal block (Brandon Knitter
, Bug #1126).
------
v2.0.4
------
[cjh] Close several XSS vulnerabilities with task and tasklist data.
------
v2.0.3
------
[jan] Allow to import more than one task from vTodo data at once.
------
v2.0.2
------
[cjh] Add a PostgreSQL upgrade script (Bug #1780).
[cjh] Use bind variables in the SQL driver (selsky@columbia.edu, Bug #1681).
[cjh] Fix problem where you couldn't select any tasklists once you'd
deselected all of them.
[jan] Add shortcut icon (favicon.ico).
[jan] Fix warnings if no tasks to export exist.
[jan] Fix CSV imports (Bug #1387).
[cjh] Allow hiding the Task List column in the List view.
------
v2.0.1
------
[jan] Allow to select the "Unfiled" category in the portal block (Mathieu
Clabaut , Bug #1237).
[cjh] Don't highlight the New Task menu item when editing tasks.
[cjh] Use a checkbox for editing completed/incomplete in the task
edit view, to match the list view (Bug #1212).
[jan] Fix print button.
[jan] Add Japanese translation (Hiromi Kimura ).
----
v2.0
----
[cjh] UIDs need to be stored in the History system with a nag: prefix and
with the tasklist_id so as not to confuse different instances of the
same task (if two users both have a task on their seperate tasklists,
they should have unique histories for that task).
[jan] Fix upgrade script to create unique IDs.
--------
v2.0-RC3
--------
[jan] Add Polish translation (Piotr Kuczynski ).
[cjh] Add My Tasklists menu entry.
--------
v2.0-RC2
--------
[jan] Add Latvian translation (Janis Eisaks ).
--------
v2.0-RC1
--------
[jan] Tweak layout of the summary block.
---------
v2.0-BETA
---------
[jan] Add special black-on-white styles for message printing.
[cjh] Users can now specify a secondary sort column
(Andrew Coleman ).
[cjh] Preserve searches while re-sorting (Francois Marier ).
[jan] Add access keys.
[cjh] Add Kolab drivers (Stuart Bingë ).
----------
v2.0-ALPHA
----------
[cjh] GUIDs now only contain nag: and the task ID - sharename is not needed.
[cjh] Task IDs are now 32-character unique strings, to be useable as GUIDs.
The SQL table definition has changed; conversion scripts are in scripts/.
[jan] Add Indonesian language (Slamin ).
[cjh] Add import and export in vTodo (iCalendar) format.
[cjh] Make sure that the correct categories for a task's current tasklist
are always used.
[cjh] Track addition, modification, and deletion of tasks
with the Horde History:: API.
[cjh] Change how tasks are stored in the SQL driver. scripts/create_sequence.php
will update an existing database with no loss of data.
[cjh] Show tooltips containing task descriptions.
[cjh] Add a preference for the default category
(Brian Keifer ).
[cjh] Make the due date in the summary block configurable
(Mathieu CLABAUT ).
[cjh] Add task alarms (Mathieu CLABAUT ).
[jan] Add UTF-8 support and charset parameter for backend drivers.
[cjh] Only show selected task categories on the summary screen
(John Morrissey ).
[cjh] Much more comprehensive permissions checking, and support for
guest access.
[cjh] Use the global shares editing page for changing/assigning share permissions.
[cjh] Add options for showing, sorting by, and grouping by task owner
(Brian Keifer ).
[mac] Allow importing onto any available Task list.
[cjh] Add an option to show only complete tasks, and add links on the
task view to switch between all, complete, and incomplete.
[mac] Add shared task lists.
------
v1.1.3
------
[jan] Close XSS when setting the parent frame's page title by javascript (cjh).
------
v1.1.2
------
[jan] Add Polish translation (Piotr Kuczynski ).
[cjh] Fix changing of task attributes that have not been set previously
(rvs@angara.ru, Bug #569).
------
v1.1.1
------
[jan] Add Indonesian language (Slamin ).
[jan] Add Arabic (Syria) translation (Platinum Development Team
).
[jan] Add Romanian translation (Eugen Hoanca ,
Marius Dragulescu ).
[jon] Display the task navbar above the description in the task view, as well
(John Morrissey ).
----
v1.1
----
[jan] Add Greek translation (Stefanos I. Dimitriou ).
[jan] Add print task button (mac).
[jan] Add Slovak translation (Ivan Noris ).
[jan] Add ability to create new categories (mac).
[jan] Add Nag::addParameter().
[jan] Add Norwegian Bokmaal translation (Torstein S. Hansen ).
[jan] Add Bulgarian translation (Miroslav Pendev ).
[jan] Add preferences for showing priorities and due dates in the summary
(Brian Keifer ).
[jan] Add Lithuanian translation (Darius Matuliauskas ).
[jan] Replace = with ).
[jan] Add Danish translation (Martin List-Petersen ).
----
v1.0
----
[jan] Add Hungarian translation (Laszlo L. Tornoci ).
[jan] Add Simplified Chinese translation (Peter Wang ).
[jan] Add Korean translation (J.I Kim ).
[jan] Add Norwegian Nynorsk translation (Per-Stian Vatne ).
[cjh] Completed tasks aren't overdue, no matter what the due date.
[cjh] Filter out completed tasks earlier if requested, so the task count
is correct.
[cjh] Simplify color scheme a bit.
[cjh] Add category column.
[cjh] Remove task_added column.
[cjh] Keep track of when a task was last modified (for sync purposes).
[jan] Add Finnish translation (Tero Matinlassi ).
[jan] Add Brazilian Portuguese translation (Antonio Dias ).
[cjh] Close a potential problem with register_globals On and $js_onLoad.
[cjh] Add new task link to the summary (Quinn Wilson ).
[cjh] Use the new PrefsUI class.
[cjh] Remove the STORAGE_* constants in favor of PEAR_Errors.
[cjh] Add a preference to not show completed tasks in the task list.
[cjh] Fix completion widget in the task modification screen.
[jon] Adapt to the new Horde::img() syntax.
[cjh] Switch output compression to ob_gzhandler().
[cjh] Use NAG_TEMPLATES constant for all template paths.
[cjh] Use $registry->get() for all Registry information.
[cjh] Prefix all application constants with NAG_.
[cjh] Use the new Notification system.
[jon] Enable the "portability" option in the SQL driver.
[jan] Remove the standard value for the language preference. The language to
fall back to should be set Horde wide in lang.php instead.
[jan] Add Swedish translation (Andreas Dahlén ).
[jan] Add Traditional Chinese translation (David Chang ).
[bjn] Change 'en' and 'en_EN' locales to 'en_US' (default).
[cjh] Change Nag_Storage:: to Nag_Driver::.
[cjh] Allow setting priority for multiple tasks in the list view.
[cjh] Change task priorities to be 1-5 (matches Palm todo list).
[jon] Apply a "strike-through" style to closed (completed) tasks.
[jon] Remove task dependency code.
[jon] Add completion status. (Paul Cooper )
[jan] Add French translation (Mikhaël Janson ).
[cjh] Let the Registry handle retrieving preferences.
[jan] Due tasks can now be shown in Kronolith.
[jan] Add Italian translation (Giovanni Meneghetti ).
[cjh] Add Czech translation (pchytil@asp.ogi.edu).
[cjh] Add Russian translation (Ignat Ikryanov ).
[avsm] Replace $conf['paths'] with the $registry equivalents.
[jon] Updated to reflect the changes in PEAR DB's quoteString().
[cjh] Add $conf['menu']['apps'] support.
[cjh] Add low and high priority icons.
[cjh] Show a note icon if a task has a description.
[jon] Added the concept of task priorities (low, medium, high).
[jon] Return to the task detail view after additions or modifications.
[jon] Allow a task to be dependent on another task.
[cjh] Add translation framework.
[jon] Added Nag::formatDate() to format internal date values.
------
v0.0.1
------
[cjh] Start application-specific constants at 100 and prefix them with NAG_.
[cjh] Add a Horde summary API function.
[jon] Added initial help text.
[cjh] Use prefs.gif and generic prefs templates from Horde.
[jon] Display task descriptions using a proportional (fixed) font.
[jon] Added externally-accessible API for registry integration.
[jon] HORDE_BASE is now defined in lib/base.php instead of config/conf.php.
[jon] Added detailed installation documentation (docs/INSTALL).
[jon] Moved task retrieval out of base.php for more efficiency and granularity.
[jon] Display overdue tasks in a different color.
[jon] Added status support for Horde messages / errors.
[cjh] Make multiple task deletion from the task list work.
[cjh] Make task storage work when deleting more than one task.
[jon] Added rudimentary searching capabilities.
[cjh] Define the HORDE_BASE constant in config/conf.php, and use it when
referring to any of Horde's files.
[jon] Added the ability to add, remove, and delete tasks.
[jon] Made the task view date and time formats separately configurable.
[jon] Added individual task viewing (with descriptions).
[jon] Added sorting to the task list.
[jon] Added initial preferences support.
[jon] Added an initial "List Tasks" implementation
[jon] Added the 'sql' storage implementation.
nag-4.2.7/docs/CREDITS 0000664 0001750 0001750 00000012404 12654116077 012357 0 ustar jan jan ======================
Nag Development Team
======================
Core Developers
===============
- Chuck Hagenbuch
- Jan Schneider
Localization
============
===================== ======================================================
Arabic (Syria) Platinum Development Team
Basque Euskal Herriko Unibertsitatea
Brazilian Portuguese Antonio Dias
Fabio Gomes
Luis Felipe Marzagao
Eduardo de Carli
Bulgarian Miroslav Pendev
Catalan Jordi Giralt
correuUPC
Chinese (Simplified) Peter Wang
Chinese (Traditional) David Chang
Croatian Matej Vela
Czech Pavel Chytil
Michael Grafnetter
Danish Martin List-Petersen
Brian Truelsen
Niels Baggesen
Erling Preben Hansen
Dutch Jan Kuipers
Ariën Huisken
Arjen de Korte
Estonian Alar Sing
Finnish Tero Matinlassi
Leena Heino
French Mikhaël Janson
Benoit St-André
Pierre Lachance
Thierry Thomas
Yannick Sebastia
Laurent Foucher
Paul De Vlieger
German Jan Schneider
Greek Stefanos I. Dimitriou
Silligardos Xristoforos
Anagnostopoulos Apostolis
Konstantinos C. Milosis
Hungarian Laszlo L. Tornoci
Andras Galos
Zoltán Németh
Kiraly Laszlo
Indonesian Slamin
Italian Giovanni Meneghetti
Marco Pirovano
Cristian Manoni
Massimo Malabotta
Massimo Balestrieri
Japanese Hiromi Kimura
Korean J.I Kim
Deokgon Kim
Josh Kim
Latvian Janis Eisaks
Lithuanian Darius Matuliauskas
Vilius Šumskas
Norwegian Bokmaal Torstein S. Hansen
Norwegian Nynorsk Per-Stian Vatne
Polish Piotr Kuczynski
Piotr Adamcio
Tadeusz Lesiecki
Piotr Tarnowski
Krzysztof Kozera
Maciej Uhlig
Portuguese Manuel Menezes de Sequeira
Romanian Eugen Hoanca
Marius Dragulescu
Russian Ignat Ikryanov
Alexey Zakharov
Slovak Ivan Noris
Martin Matuška
Slovenian Duck
Spanish Raúl Alvarez Venegas
Manuel Perez Ayala
Juan C. Blanco
Swedish Andreas Dahlén
Joaquim Homrighausen
Per Olof Ljungmark
Turkish Istanbul Technical University IT Department
Middle East Technical University
Ukrainian Andriy Kopystyansky
===================== ======================================================
Inactive Developers
===================
- Jon Parise
nag-4.2.7/docs/INSTALL 0000664 0001750 0001750 00000013423 12654116077 012372 0 ustar jan jan ===================
Installing Nag H5
===================
:Contact: nag@lists.horde.org
.. contents:: Contents
.. section-numbering::
This document contains instructions for installing the Nag web-based todo list
application on your system.
For information on the capabilities and features of Nag, see the file README_
in the top-level directory of the Nag distribution.
Prerequisites
=============
To function properly, Nag **requires** the following:
1. A working Horde installation.
Nag runs within the `Horde Application Framework`_, a set of common tools
for web applications written in PHP. You must install Horde before
installing Nag.
.. Important:: Nag H5 requires version 5.0+ of the Horde Framework -
earlier versions of Horde will **not** work.
.. Important:: Be sure to have completed all of the steps in the
`horde/docs/INSTALL`_ file for the Horde Framework before
installing Nag. Many of Nag's prerequisites are also
Horde prerequisites. Additionally, many of Nag's optional
features are configured via the Horde install.
.. _`Horde Application Framework`: http://www.horde.org/apps/horde
2. SQL support in PHP *or* a configured Kolab Server.
Nag will store its data in either an SQL database or on a Kolab Server.
If you use SQL, build PHP with whichever SQL driver you require; see the
Horde INSTALL_ file for details.
Installing Nag
==============
The **RECOMMENDED** way to install Nag is using the PEAR installer.
Alternatively, if you want to run the latest development code or get the
latest not yet released fixes, you can install Nag from Git.
Installing with PEAR
~~~~~~~~~~~~~~~~~~~~
First follow the instructions in `horde/docs/INSTALL`_ to prepare a PEAR
environment for Horde and install the Horde Framework.
When installing Nag through PEAR now, the installer will automatically install
any dependencies of Nag too. If you want to install Nag with all optional
dependencies, but without the binary PECL packages that need to be compiled,
specify both the ``-a`` and the ``-B`` flag::
pear install -a -B horde/nag
By default, only the required dependencies will be installed::
pear install horde/nag
If you want to install Nag even with all binary dependencies, you need to
remove the ``-B`` flag. Please note that this might also try to install PHP
extensions through PECL that might need further configuration or activation in
your PHP configuration::
pear install -a horde/nag
Installing from Git
~~~~~~~~~~~~~~~~~~~
See http://www.horde.org/source/git.php
Configuring Nag
===============
1. Configuring Horde for Nag
Nag requires a permanent ``Shares`` backend in Horde to manage tasklists and
to add tasks to tasklists. If you didn't setup a Share backend yet, go to
the configuration interface, select Horde from the list of applications and
select the ``Shares`` tab. Unless you are using Kolab, you should select
``SQL``. Make sure that you ran the necessary scripts to create a storage
backend for the Share system, e.g. one of the ``create.*.sql``.
2. Configuring Nag
You must login to Horde as a Horde Administrator to finish the configuration
of Nag. Use the Horde ``Administration`` menu item to get to the
administration page, and then click on the ``Configuration`` icon to get the
configuration page. Select ``Tasks`` from the selection list of
applications. Fill in or change any configuration values as needed. When
done click on ``Generate Tasks Configuration`` to generate the ``conf.php``
file. If your web server doesn't have write permissions to the Nag
configuration directory or file, it will not be able to write the file. In
this case, go back to ``Configuration`` and choose one of the other methods
to create the configuration file ``nag/config/conf.php``.
Documentation on the format and purpose of the other configuration files in
the ``config/`` directory can be found in each file. You may create
``*.local.php`` versions of these files if you wish to customize Nag's
appearance and behavior. See the header of the configuration files for
details and examples. The defaults will be correct for most sites.
3. Creating the database table
Once you finished the configuration in the previous step, you can create all
database tables by clicking the ``DB schema is out of date.`` link in the
Nag row of the configuration screen.
Alternatively you creating the Nag database tables can be accomplished with
horde's ``horde-db-migrate`` utility. If your database is properly setup in the Horde configuration, just run the following::
horde/bin/horde-db-migrate nag
4. Testing Nag
Use Nag to create, modify, and delete tasks. Test at least the following:
- Creating a new task
- Modifying a task
- Completing a task
- Deleting a task
Obtaining Support
=================
If you encounter problems with Nag, help is available!
The Horde Frequently Asked Questions List (FAQ), available on the Web at
http://wiki.horde.org/FAQ
The Horde Project runs a number of mailing lists, for individual applications
and for issues relating to the project as a whole. Information, archives, and
subscription information can be found at
http://www.horde.org/community/mail
Lastly, Horde developers, contributors and users may also be found on IRC,
on the channel #horde on the Freenode Network (irc.freenode.net).
Please keep in mind that Nag is free software written by volunteers. For
information on reasonable support expectations, please read
http://www.horde.org/community/support
Thanks for using Nag!
The Horde team
.. _README: README
.. _INSTALL:
.. _`horde/docs/INSTALL`: ../../horde/docs/INSTALL
.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/TRANSLATIONS
nag-4.2.7/docs/lighttpd-nag.conf 0000664 0001750 0001750 00000001071 12654116077 014566 0 ustar jan jan ## This file should be reviewed prior to inclusion in your lighttpd
## configuration. Specifically, if you have horde somewhere other than
## /horde you will need to edit the following rules to match your server
## configuration.
## This file should be included in your lighttpd.conf file with the "include"
## directive. Example:
## include "path/to/lighttpd-nag.conf"
## The exact path you use will of course depend on your specific configuration.
url.rewrite-once += (
## Rampage Rewrite Rules
"^/horde/nag/t/(.*)$" => "/horde/rampage.php/$1"
)
nag-4.2.7/docs/RELEASE_NOTES 0000664 0001750 0001750 00000002013 12654116077 013305 0 ustar jan jan Configuration and update anything that's highlighted as
outdated.
Upgrading Nag from 2.2.x to 2.3.x
=================================
Some fields in the SQL share driver tables have been changed. Execute the
provided SQL script to update your database if you are using the native SQL
share driver.
mysql --user=root --password= < scripts/upgrades/2.2_to_2.3.sql
Upgrading Nag from 2.1.x to 2.2.x
=================================
SQL Backends
------------
A few new fields have been added to the default SQL table layout.
Execute the provided SQL script to update your data to the new Nag version,
e.g.::
mysql --user=root --password= < scripts/upgrades/2.1_to_2.2.sql
You also have to execute the provided PHP script::
php scripts/upgrades/2006-04-18_add_creator_and_assignee_fields.php
New Beta SQL Share Driver Support
---------------------------------
A new beta-level SQL Horde_Share driver has been added in Horde 3.2. This driver
offers significant performance improvements over the existing Datatree driver,
but it has not received the same level of testing, thus the beta designation.
In order to make use of this driver, you must be using Horde 3.2-RC3 or
later. The new tables needed for this driver already should have been created
by the step above.
If you want to use the new SQL Share driver, you must also execute the
provided PHP script to migrate your existing share data to the new format::
nag-convert-datatree-shares-to-sql
.. _INSTALL: INSTALL
nag-4.2.7/js/calendar.js 0000664 0001750 0001750 00000005754 12654116077 013144 0 ustar jan jan /**
* calendar.js - Calendar related javascript.
*
* Copyright 2010-2016 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (GPL). If you
* did not receive this file, see http://www.horde.org/licenses/gpl.
*
* @author Michael Slusarz
* @category Horde
* @package Nag
*/
var NagCalendar =
{
calendarSelect: function(e)
{
var prefix, radio;
switch (e.element().identify()) {
case 'dueimg':
prefix = 'due';
radio = 'due_type_specified';
break;
case 'recur_endimg':
prefix = 'recur_end';
radio = 'recur_end_specified';
break;
case 'startimg':
prefix = 'start';
radio = 'start_date_specified';
break;
default:
return;
}
$(prefix + '_date').setValue(e.memo.toString(Nag.conf.date_format));
$(radio).setValue(1);
this.updateWday(prefix);
},
updateWday: function(p)
{
var d = this.getFormDate(p);
if (d) {
$(p + '_wday').update('(' + Horde_Calendar.fullweekdays[d.getDay()] + ')');
}
},
getFormDate: function(p)
{
return Date.parseExact($F(p + '_date'), Nag.conf.date_format);
},
clickHandler: function(e)
{
if (e.isRightClick()) {
return;
}
var elt = e.element(),
id = elt.readAttribute('id');
switch (id) {
case 'dueimg':
case 'startimg':
case 'recur_endimg':
Horde_Calendar.open(elt, this.getFormDate(id.slice(0, -3)));
e.stop();
break;
case 'due_am_pm_am':
case 'due_am_pm_am_label':
case 'due_am_pm_pm':
case 'due_am_pm_pm_label':
$('due_type_specified').setValue(1);
break;
}
},
changeHandler: function(e)
{
switch (e.element().readAttribute('id')) {
case 'due_date':
this.updateWday('due');
// Fall-through
case 'due_time':
$('due_type_specified').setValue(1);
break;
case 'start_date':
this.updateWday('start');
// Fall-through
case 'start_time':
$('start_date_specified').setValue(1);
break;
case 'alarm_unit':
case 'alarm_value':
$('alarmon').setValue(1);
break;
}
},
onDomLoad: function()
{
this.updateWday('due');
this.updateWday('start');
this.updateWday('recur_end');
$('nag_form_task_active').observe('click', this.clickHandler.bindAsEventListener(this));
$('nag_form_task_active').observe('change', this.changeHandler.bindAsEventListener(this));
}
};
document.observe('dom:loaded', NagCalendar.onDomLoad.bind(NagCalendar));
document.observe('Horde_Calendar:select', NagCalendar.calendarSelect.bindAsEventListener(NagCalendar));
nag-4.2.7/js/smartmobile.js 0000664 0001750 0001750 00000033630 12654116077 013703 0 ustar jan jan /**
* Base smartmobile application logic for Nag.
*
* Copyright 2011-2016 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (GPL). If you
* did not receive this file, see http://www.horde.org/licenses/gpl.
*
* @author Michael J Rubinsky
* @category Horde
* @license http://www.horde.org/licenses/gpl GPL
* @package Nag
*/
var NagMobile = {
tasklists: {},
tasks: {},
currentList: undefined,
/**
* Toggle the completion status of the task.
*
* @param object d The data object.
*/
toggleComplete: function(d)
{
var parsed = d.options.parsedUrl;
HordeMobile.doAction(
'smartmobileToggle',
{
task: parsed.params.task_id,
tasklist: parsed.params.tasklist
},
function(r) { NagMobile.toggleCompleteCallback(r, d.options.data) }
);
},
/**
* Callback for the toggleComplete action
*
* @param object r The response object.
* @param object elt The element containing the task.
*/
toggleCompleteCallback: function(r, elt)
{
if (!r.data) {
return;
}
switch (r.data) {
case 'complete':
NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')].cp = true;
if (Nag.conf.showCompleted == 'incomplete' ||
Nag.conf.showCompleted == 'future-incomplete') {
// Hide the task
elt.parent().remove();
} else {
elt.jqmData('icon', 'check')
.find('span.ui-icon')
.removeClass('ui-icon-nag-unchecked')
.addClass('ui-icon-check');
NagMobile.styleTask(elt, NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')]);
}
break;
default:
NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')].cp = false;
if (Nag.conf.showCompleted == 'complete') {
// Hide the task
elt.parent().remove();
} else {
elt.jqmData('icon', 'minus')
.find('span.ui-icon')
.removeClass('ui-icon-check')
.addClass('ui-icon-nag-unchecked');
NagMobile.styleTask(elt, NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')]);
}
}
},
/**
* Get a task from the server.
*
* @param object d The data object.
*/
getTask: function(d)
{
var parsed = d.options.parsedUrl;
HordeMobile.doAction(
'getTask',
{
task: parsed.params.task_id,
tasklist: parsed.params.tasklist
},
NagMobile.getTaskCallback
);
$('#nag-taskform-view a[href^="#task-delete"]').show();
HordeMobile.changePage('nag-taskform-view', d);
},
/**
* Callback for the getTask action.
*
* @param object r The response object.
*/
getTaskCallback: function(r)
{
if (!r.task) {
return;
}
var task = r.task,
f = $('form')[0];
f.reset();
$("#task_title").val(task.n);
$("#task_desc").val(task.de);
$("#task_assignee").val(task.as);
$("task_private").prop("checked", task.pr).checkboxradio("refresh");
if (task.dd) {
$("#task_due").val(Date.parse(task.dd).toString('yyyy-MM-dd'));
}
if (task.s) {
$("#task_start").val(Date.parse(task.s).toString('yyyy-MM-dd'));
}
var myselect = $("#task_priority");
myselect[0].selectedIndex = task.pr - 1;
myselect.selectmenu("refresh");
$("#task_completed").prop("checked", task.cp).checkboxradio("refresh");
$("#task_estimate").val(task.e);
$("#task_id").val(task.id);
$("#tasklist").val(task.l);
},
/**
* Get a list of tasklists from the server and display the nag-lists view.
*
* @param object d The data object.
*/
toLists: function(d)
{
HordeMobile.changePage('nag-lists', d);
HordeMobile.doAction(
'getTaskLists',
{},
NagMobile.getTasklistsCallback
);
},
/**
* Callback for the getTaskLists action
*
* @param object r The response object.
*/
getTasklistsCallback: function(r)
{
if (!r.tasklists) {
return;
}
var list = $('#nag-lists :jqmData(role="listview")'),
count = 0;
list.empty();
$.each(r.tasklists, function(i, l) {
count = count + l.count;
NagMobile.insertTasklist(list, l, false);
});
NagMobile.insertTasklist(
list,
{
'name': Nag.strings.all,
'count': count
},
true
);
list.listview('refresh');
},
/**
* Insert a tasklist element into the tasklist list.
*
* @param object el The UL element.
* @param object l The list hash.
* @param boolean top Place new list at top of list if true.
*/
insertTasklist: function(el, l, top)
{
var url = HordeMobile.createUrl('nag-list', { tasklist: l.id }),
list;
NagMobile.tasklists[l.id] = l;
list = $('
';
}
return '';
}
/**
* Get HTML to represent the currently selected tags.
*
* @return string
*/
protected function _getTagTrail()
{
if ($this->_browser->tagCount() >= 1) {
$html = '
';
}
return '';
}
/**
* Get HTML for a link to remove a tag from the current search.
*
* @param string $tag The tag we want the link for.
*
* @return string
*/
protected function _linkRemoveTag($tag)
{
return Horde::url('list.php')
->add(array('actionID' => 'browse_remove', 'tag' => $tag));
}
/**
* Get HTML for a link to add a new tag to the current search.
*
* @param string $tag The tag we want to add.
*
* @return string
*/
protected function _linkAddTag($tag)
{
return Horde::url('list.php')->add(array('actionID' => 'browse_add', 'tag' => $tag));
}
}
nag-4.2.7/lib/.htaccess 0000664 0001750 0001750 00000000174 12654116077 012754 0 ustar jan jan
Require all denied
Deny from all
nag-4.2.7/lib/Api.php 0000664 0001750 0001750 00000162310 12654116077 012401 0 ustar jan jan '%application%/view.php?tasklist=|tasklist|&task=|task|&uid=|uid|'
);
/**
* Returns a number of defaults necessary for the ajax view.
*
* @return array A hash with default values.
*/
public function ajaxDefaults()
{
return array(
'URI_TASKLIST_EXPORT' => str_replace(
array('%23', '%2523', '%7B', '%257B', '%7D', '%257D'),
array('#', '#', '{', '{', '}', '}'),
strval($GLOBALS['registry']->downloadUrl('#{tasklist}.ics', array('actionID' => 'export', 'exportTasks' => 1, 'exportID' => Horde_Data::EXPORT_ICALENDAR, 'exportList' => '#{tasklist}'))->setRaw(true))),
'default_tasklist' => Nag::getDefaultTasklist(Horde_Perms::EDIT),
'default_due' => (bool)$GLOBALS['prefs']->getValue('default_due'),
'default_due_days' => (int)$GLOBALS['prefs']->getValue('default_due_days'),
'default_due_time' => $GLOBALS['prefs']->getValue('default_due_time'),
'prefs_url' => strval($GLOBALS['registry']->getServiceLink('prefs', 'nag')->setRaw(true)),
);
}
/**
* Retrieves the current user's task list from storage.
*
* This function will also sort the resulting list, if requested.
* @param arary $options Options array:
* - altsortby: (string) The secondary sort field. Same values as sortdir.
* DEFAULT: altsortby pref is used.
* - completed: (integer) Which task to retrieve.
* DEFAULT: show_completed pref is used.
* - sortby: (string) A Nag::SORT_* constant for the field to sort by.
* DEFAULT: sortby pref is used.
* - sortdir: (string) Direction of sort. NAG::SORT_ASCEND or NAG::SORT_DESCEND.
* DEFAULT: sortdir pref is used.
* - include_tags: (boolean) Autoload all tags.
* DEFAULT: false (Tags are lazy loaded as needed.)
* - json: (boolean) Return data as JSON.
* DEFAULT: false (Data is returned as Nag_Task)
* - tasklists: (array) An array of tasklists to include.
* DEFAULT: Use $GLOBALS['display_tasklists'];
*
* @return array An array of the requested tasks.
*/
public function listTasks(array $options = array())
{
global $prefs;
$completedArray = array(
'incomplete' => Nag::VIEW_INCOMPLETE,
'all' => Nag::VIEW_ALL,
'complete' => Nag::VIEW_COMPLETE,
'future' => Nag::VIEW_FUTURE,
'future_incomplete' => Nag::VIEW_FUTURE_INCOMPLETE);
// Prevent null tasklists value from obscuring the default value.
if (array_key_exists('tasklists', $options) && empty($options['tasklists'])) {
unset($options['tasklists']);
}
if (is_null($options['completed']) || !isset($completedArray[$options['completed']])) {
$options['completed'] = $prefs->getValue('show_completed');
} else {
$options['completed'] = $completedArray[$options['completed']];
}
$options = array_merge(
array(
'sortby' => $prefs->getValue('sortby'),
'sortdir' => $prefs->getValue('sortdir'),
'altsortby' => $prefs->getValue('altsortby'),
'tasklists' => $GLOBALS['display_tasklists'],
'include_tags' => false,
'external' => false,
'json' => false
),
$options
);
$tasks = Nag::listTasks($options);
$tasks->reset();
$list = array();
while ($task = $tasks->each()) {
$list[$task->id] = $options['json'] ? $task->toJson() : $task->toHash();
}
return $list;
}
/**
* Returns a list of task lists.
*
* @param boolean $owneronly Only return tasklists that this user owns?
* Defaults to false.
* @param integer $permission The permission to filter tasklists by.
* @param boolean $smart Include smart tasklists in results.
*
* @return array The task lists.
*/
public function listTasklists($owneronly = false, $permission = Horde_Perms::SHOW, $smart = true)
{
return Nag::listTasklists($owneronly, $permission, $smart);
}
/**
* Returns a task list.
*
* @param string $name A task list name.
*
* @return Horde_Share_Object The task list.
*/
public function getTasklist($name)
{
try {
$tasklist = $GLOBALS['nag_shares']->getShare($name);
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
throw new Nag_Exception($e);
}
if (!$tasklist->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied(_("You are not allowed to retrieve this task list."));
}
return $tasklist;
}
/**
* Adds a new task list.
*
* @param string $name Task list name.
* @param string $description Task list description.
* @param string $color Task list color.
* @param array $params Any addtional parameters needed. @since 4.2.1
* - synchronize: (boolean) If true, add task list to the list of
* task lists to syncronize.
* DEFAULT: false (do not add to the list).
*
* @return string The new tasklist's id.
*/
public function addTasklist($name, $description = '', $color = '', array $params = array())
{
$tasklist = Nag::addTasklist(array('name' => $name, 'description' => $description, 'color' => $color));
$name = $tasklist->getName();
if (!empty($params['synchronize'])) {
$sync = @unserialize($GLOBALS['prefs']->getValue('sync_lists'));
$sync[] = $name;
$GLOBALS['prefs']->setValue('sync_lists', serialize($sync));
}
return $name;
}
/**
* Updates an existing task list.
*
* @param string $name A task list name.
* @param array $info Hash with task list information.
*/
public static function updateTasklist($name, $info)
{
try {
$tasklist = $GLOBALS['nag_shares']->getShare($name);
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
throw new Nag_Exception($e);
}
return Nag::updateTasklist($tasklist, $info);
}
/**
* Deletes a task list.
*
* @param string $id A task list id.
*/
public function deleteTasklist($id)
{
$tasklist = $GLOBALS['nag_shares']->getShare($id);
return Nag::deleteTasklist($tasklist);
}
/**
* Returns the displayed task lists.
*
* @return array Displayed tasklists.
*/
public function getDisplayedTasklists()
{
return $GLOBALS['display_tasklists'];
}
/**
* Sets the displayed task lists.
*
* @param array $list Displayed tasklists.
*/
public function setDisplayedTasklists($list)
{
$GLOBALS['display_tasklists'] = $list;
$GLOBALS['prefs']->setValue('display_tasklists', serialize($list));
}
/**
* Returns the last modification timestamp of a given uid.
*
* @param string $uid The uid to look for.
* @param string $tasklist The tasklist to look in.
*
* @return integer The timestamp for the last modification of $uid.
*/
public function modified($uid, $tasklist = null)
{
$modified = $this->getActionTimestamp($uid, 'modify', $tasklist);
if (empty($modified)) {
$modified = $this->getActionTimestamp($uid, 'add', $tasklist);
}
return $modified;
}
/**
* Browse through Nag's object tree.
*
* @param string $path The level of the tree to browse.
* @param array $properties The item properties to return. Defaults to 'name',
* 'icon', and 'browseable'.
*
* @return array The contents of $path
*/
public function browse($path = '', $properties = array())
{
global $injector, $nag_shares, $registry;
// Default properties.
if (!$properties) {
$properties = array('name', 'icon', 'browseable');
}
if (substr($path, 0, 3) == 'nag') {
$path = substr($path, 3);
}
$path = trim($path, '/');
$parts = explode('/', $path);
$currentUser = $registry->getAuth();
if (empty($path)) {
// This request is for a list of all users who have tasklists
// visible to the requesting user.
$tasklists = Nag::listTasklists(false, Horde_Perms::READ);
$owners = array();
foreach ($tasklists as $tasklist) {
$owners[$tasklist->get('owner') ? $registry->convertUsername($tasklist->get('owner'), false) : '-system-'] = $tasklist->get('owner') ?: '-system-';
}
$results = array();
foreach ($owners as $externalOwner => $internalOwner) {
if (in_array('name', $properties)) {
$results['nag/' . $externalOwner]['name'] = $injector
->getInstance('Horde_Core_Factory_Identity')
->create($internalOwner)
->getName();
}
if (in_array('icon', $properties)) {
$results['nag/' . $externalOwner]['icon'] = Horde_Themes::img('user.png');
}
if (in_array('browseable', $properties)) {
$results['nag/' . $externalOwner]['browseable'] = true;
}
if (in_array('read-only', $properties)) {
$results['nag/' . $externalOwner]['read-only'] = true;
}
}
return $results;
} elseif (count($parts) == 1) {
// This request is for all tasklists owned by the requested user
$owner = $parts[0] == '-system-' ? '' : $registry->convertUsername($parts[0], true);
$tasklists = $nag_shares->listShares(
$currentUser,
array('perm' => Horde_Perms::SHOW,
'attributes' => $owner));
$results = array();
foreach ($tasklists as $tasklistId => $tasklist) {
if ($parts[0] == '-system-' && $tasklist->get('owner')) {
continue;
}
$retpath = 'nag/' . $parts[0] . '/' . $tasklistId;
if (in_array('name', $properties)) {
$results[$retpath]['name'] = sprintf(_("Tasks from %s"), Nag::getLabel($tasklist));
$results[$retpath . '.ics']['name'] = Nag::getLabel($tasklist);
}
if (in_array('displayname', $properties)) {
$results[$retpath]['displayname'] = Nag::getLabel($tasklist);
$results[$retpath . '.ics']['displayname'] = Nag::getLabel($tasklist) . '.ics';
}
if (in_array('owner', $properties)) {
$results[$retpath]['owner'] = $results[$retpath . '.ics']['owner'] = $tasklist->get('owner') ? $registry->convertUsername($tasklist->get('owner'), false) : '-system-';
}
if (in_array('icon', $properties)) {
$results[$retpath]['icon'] = Horde_Themes::img('nag.png');
$results[$retpath . '.ics']['icon'] = Horde_Themes::img('mime/icalendar.png');
}
if (in_array('browseable', $properties)) {
$results[$retpath]['browseable'] = $tasklist->hasPermission($currentUser, Horde_Perms::READ);
$results[$retpath . '.ics']['browseable'] = false;
}
if (in_array('read-only', $properties)) {
$results[$retpath]['read-only'] = $results[$retpath . '.ics']['read-only'] = !$tasklist->hasPermission($currentUser, Horde_Perms::EDIT);
}
if (in_array('contenttype', $properties)) {
$results[$retpath . '.ics']['contenttype'] = 'text/calendar';
}
}
return $results;
} elseif (count($parts) == 2 && substr($parts[1], -4) == '.ics') {
//
// This is a request for the entire tasklist in iCalendar format.
//
$tasklist = substr($parts[1], 0, -4);
if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
throw new Nag_Exception(_("Invalid task list file requested."), 404);
}
$ical_data = $this->exportTasklist($tasklist, 'text/calendar');
return array(
'data' => $ical_data,
'mimetype' => 'text/calendar',
'contentlength' => strlen($ical_data),
'mtime' => $_SERVER['REQUEST_TIME']
);
} elseif (count($parts) == 2) {
//
// This request is browsing into a specific tasklist. Generate the
// list of items and represent them as files within the directory.
//
try {
$tasklist = $nag_shares->getShare($parts[1]);
} catch (Horde_Exception_NotFound $e) {
throw new Nag_Exception(_("Invalid task list requested."), 404);
} catch (Horde_Share_Exception $e) {
throw new Nag_Exception($e->getMessage, 500);
}
if (!$tasklist->hasPermission($currentUser, Horde_Perms::READ)) {
throw new Nag_Exception(_("Invalid task list requested."), 404);
}
$storage = $injector->getInstance('Nag_Factory_Driver')->create($parts[1]);
try {
$storage->retrieve();
} catch (Nag_Exception $e) {
throw new Nag_Exception($e->getMessage, 500);
}
$icon = Horde_Themes::img('nag.png');
$owner = $tasklist->get('owner')
? $registry->convertUsername($tasklist->get('owner'), false)
: '-system-';
$results = array();
$storage->tasks->reset();
while ($task = $storage->tasks->each()) {
$key = 'nag/' . $parts[0] . '/' . $parts[1] . '/' . $task->id;
if (in_array('name', $properties)) {
$results[$key]['name'] = $task->name;
}
if (in_array('owner', $properties)) {
$results[$key]['owner'] = $owner;
}
if (in_array('icon', $properties)) {
$results[$key]['icon'] = $icon;
}
if (in_array('browseable', $properties)) {
$results[$key]['browseable'] = false;
}
if (in_array('read-only', $properties)) {
$results[$key]['read-only'] = !$tasklist->hasPermission($currentUser, Horde_Perms::EDIT);
}
if (in_array('contenttype', $properties)) {
$results[$key]['contenttype'] = 'text/calendar';
}
if (in_array('modified', $properties)) {
$results[$key]['modified'] = $this->modified($task->uid, $parts[1]);
}
if (in_array('created', $properties)) {
$results[$key]['created'] = $this->getActionTimestamp($task->uid, 'add', $parts[1]);
}
}
return $results;
} else {
//
// The only valid request left is for either a specific task item.
//
if (count($parts) == 3 &&
Nag::hasPermission($parts[1], Horde_Perms::READ)) {
//
// This request is for a specific item within a given task list.
//
/* Create a Nag storage instance. */
$storage = $injector->getInstance('Nag_Factory_Driver')
->create($parts[1]);
$storage->retrieve();
try {
$task = $storage->get($parts[2]);
} catch (Nag_Exception $e) {
throw new Nag_Exception($e->getMessage(), 500);
}
$result = array(
'data' => $this->export($task->uid, 'text/calendar'),
'mimetype' => 'text/calendar');
$modified = $this->modified($task->uid, $parts[1]);
if (!empty($modified)) {
$result['mtime'] = $modified;
}
return $result;
} else {
//
// All other requests are a 404: Not Found
//
return false;
}
}
}
/**
* Saves a file into the Nag tree.
*
* @param string $path The path where to PUT the file.
* @param string $content The file content.
* @param string $content_type The file's content type.
*
* @return array The event UIDs
*/
public function put($path, $content, $content_type)
{
if (substr($path, 0, 3) == 'nag') {
$path = substr($path, 3);
}
$path = trim($path, '/');
$parts = explode('/', $path);
if (count($parts) == 2 &&
substr($parts[1], -4) == '.ics') {
// Workaround for WebDAV clients that are not smart enough to send
// the right content type. Assume text/calendar.
if ($content_type == 'application/octet-stream') {
$content_type = 'text/calendar';
}
$tasklist = substr($parts[1], 0, -4);
} elseif (count($parts) == 3) {
$tasklist = $parts[1];
// Workaround for WebDAV clients that are not smart enough to send
// the right content type. Assume the same format we send
// individual tasklist items: text/calendar
if ($content_type == 'application/octet-stream') {
$content_type = 'text/calendar';
}
} else {
throw new Nag_Exception(_("Invalid task list name supplied."), 403);
}
if (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
// FIXME: Should we attempt to create a tasklist based on the
// filename in the case that the requested tasklist does not exist?
throw new Nag_Exception(_("Task list does not exist or no permission to edit"), 403);
}
// Store all currently existings UIDs. Use this info to delete UIDs not
// present in $content after processing.
$ids = array();
if (count($parts) == 2) {
$uids_remove = array_flip($this->listUids($tasklist));
} else {
$uids_remove = array();
}
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist);
switch ($content_type) {
case 'text/calendar':
case 'text/x-vcalendar':
$iCal = new Horde_Icalendar();
if (!($content instanceof Horde_Icalendar_Vtodo)) {
if (!$iCal->parsevCalendar($content)) {
throw new Nag_Exception(_("There was an error importing the iCalendar data."), 400);
}
} else {
$iCal->addComponent($content);
}
foreach ($iCal->getComponents() as $content) {
if (!($content instanceof Horde_Icalendar_Vtodo)) {
continue;
}
$task = new Nag_Task();
$task->fromiCalendar($content);
$task->tasklist = $tasklist;
$create = true;
if (isset($task->uid)) {
try {
$existing = $storage->getByUID($task->uid);
$create = false;
} catch (Horde_Exception_NotFound $e) {
}
}
if (!$create) {
// Entry exists, remove from uids_remove list so we
// won't delete in the end.
unset($uids_remove[$task->uid]);
if ($existing->private &&
$existing->owner != $GLOBALS['registry']->getAuth()) {
continue;
}
// Check if our task is newer then the existing - get
// the task's history.
$history = $GLOBALS['injector']->getInstance('Horde_History');
$created = $modified = null;
try {
$log = $history->getHistory('nag:' . $tasklist . ':' . $task->uid);
foreach ($log as $entry) {
switch ($entry['action']) {
case 'add':
$created = $entry['ts'];
break;
case 'modify':
$modified = $entry['ts'];
break;
}
}
} catch (Exception $e) {
}
if (empty($modified) && !empty($add)) {
$modified = $add;
}
if (!empty($modified) &&
$modified >= $content->getAttribute('LAST-MODIFIED')) {
// LAST-MODIFIED timestamp of existing entry
// is newer: don't replace it.
continue;
}
// Don't change creator/owner.
$task->owner = $existing->owner;
try {
$storage->modify($existing->id, $task->toHash());
} catch (Nag_Exception $e) {
throw new Nag_Exception($e->getMessage(), 500);
}
$ids[] = $task->uid;
} else {
try {
$newTask = $storage->add($task->toHash());
} catch (Nag_Exception $e) {
throw new Nag_Exception($e->getMessage(), 500);
}
// use UID rather than ID
$ids[] = $newTask[1];
}
}
break;
default:
throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $content_type), 400);
}
if (Nag::hasPermission($tasklist, Horde_Perms::DELETE)) {
foreach (array_keys($uids_remove) as $uid) {
$this->delete($uid);
}
}
return $ids;
}
/**
* Deletes a file from the Nag tree.
*
* @param string $path The path to the file.
*
* @return string The event's UID
* @throws Nag_Exception
*/
public function path_delete($path)
{
if (substr($path, 0, 3) == 'nag') {
$path = substr($path, 3);
}
$path = trim($path, '/');
$parts = explode('/', $path);
if (count($parts) == 2) {
// @TODO Deny deleting of the entire tasklist for now.
// Allow users to delete tasklists but not create them via WebDAV
// will be more confusing than helpful. They are, however, still
// able to delete individual task items within the tasklist folder.
throw Nag_Exception(_("Deleting entire task lists is not supported."), 403);
// To re-enable the functionality just remove this if {} block.
}
if (substr($parts[1], -4) == '.ics') {
$tasklistID = substr($parts[1], 0, -4);
} else {
$tasklistID = $parts[1];
}
if (!(count($parts) == 2 || count($parts) == 3) ||
!Nag::hasPermission($tasklistID, Horde_Perms::DELETE)) {
throw new Nag_Exception(_("Task list does not exist or no permission to delete"), 403);
}
/* Create a Nag storage instance. */
try {
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklistID);
$storage->retrieve();
} catch (Nag_Exception $e) {
throw new Nag_Exception(sprintf(_("Connection failed: %s"), $e->getMessage()), 500);
}
if (count($parts) == 3) {
// Delete just a single entry
return $storage->delete($parts[2]);
} else {
// Delete the entire task list
try {
$storage->deleteAll();
} catch (Nag_Exception $e) {
throw new Nag_Exception(sprintf(_("Unable to delete task list \"%s\": %s"), $tasklistID, $e->getMessage()), 500);
}
// Remove share and all groups/permissions.
$share = $GLOBALS['nag_shares']->getShare($tasklistID);
try {
$GLOBALS['nag_shares']->removeShare($share);
} catch (Horde_Share_Exception $e) {
throw new Nag_Exception($e->getMessage());
}
}
}
/**
* Returns an array of UIDs for all tasks that the current user is authorized
* to see.
*
* @param mixed $tasklists The tasklist or an array of taskslists to list.
*
* @return array An array of UIDs for all tasks
* the user can access.
*
* @throws Horde_Exception_PermissionDenied
* @throws Nag_Exception
*/
public function listUids($tasklists = null)
{
if (!isset($GLOBALS['conf']['storage']['driver'])) {
throw new Nag_Exception(_("Not configured"));
}
if (empty($tasklists)) {
$tasklists = Nag::getSyncLists();
} else {
if (!is_array($tasklists)) {
$tasklists = array($tasklists);
}
foreach ($tasklists as $list) {
if (!Nag::hasPermission($list, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
}
}
$tasks = Nag::listTasks(array(
'tasklists' => $tasklists,
'completed' => Nag::VIEW_ALL,
'include_history' => false)
);
$uids = array();
$tasks->reset();
while ($task = $tasks->each()) {
$uids[] = $task->uid;
}
return $uids;
}
/**
* Returns an array of UIDs for tasks that have had $action happen since
* $timestamp.
*
* @param string $action The action to check for - add, modify, or delete.
* @param integer $timestamp The time to start the search.
* @param mixed $tasklists The tasklists to be used. If 'null', the
* user's default tasklist will be used.
* @param integer $end The optional ending timestamp.
* @param boolean $isModSeq If true, $timestamp and $end are modification
* sequences and not timestamps. @since 4.1.1
*
* @return array An array of UIDs matching the action and time criteria.
*
* @throws Horde_History_Exception
* @throws InvalidArgumentException
*/
public function listBy($action, $timestamp, $tasklist = null, $end = null, $isModSeq = false)
{
if (empty($tasklist)) {
$tasklist = Nag::getSyncLists();
$results = array();
foreach ($tasklist as $list) {
$results = array_merge($results, $this->listBy($action, $timestamp, $list, $end, $isModSeq));
}
return $results;
}
$filter = array(array('op' => '=', 'field' => 'action', 'value' => $action));
if (!empty($end) && !$isModSeq) {
$filter[] = array('op' => '<', 'field' => 'ts', 'value' => $end);
}
if (!$isModSeq) {
$histories = $GLOBALS['injector']
->getInstance('Horde_History')
->getByTimestamp('>', $timestamp, $filter, 'nag:' . $tasklist);
} else {
$histories = $GLOBALS['injector']
->getInstance('Horde_History')
->getByModSeq($timestamp, $end, $filter, 'nag:' . $tasklist);
}
// Strip leading nag:username:.
return preg_replace('/^([^:]*:){2}/', '', array_keys($histories));
}
/**
* Method for obtaining all server changes between two timestamps. Basically
* a wrapper around listBy(), but returns an array containing all adds,
* edits and deletions.
*
* @param integer $start The starting timestamp
* @param integer $end The ending timestamp.
* @param boolean $isModSeq If true, $timestamp and $end are
* modification sequences and not
* timestamps. @since 4.1.1
* @param string|array $tasklists The sources to check. @since 4.2.0
*
* @return array An hash with 'add', 'modify' and 'delete' arrays.
*/
public function getChanges($start, $end, $isModSeq = false, $tasklists = null)
{
return array(
'add' => $this->listBy('add', $start, $tasklists, $end, $isModSeq),
'modify' => $this->listBy('modify', $start, $tasklists, $end, $isModSeq),
'delete' => $this->listBy('delete', $start, $tasklists, $end, $isModSeq));
}
/**
* Return all changes occuring between the specified modification
* sequences.
*
* @param integer $start The starting modseq.
* @param integer $end The ending modseq.
* @param string|array $tasklists The sources to check. @since 4.2.0
*
* @return array The changes @see getChanges()
* @since 4.1.1
*/
public function getChangesByModSeq($start, $end, $tasklists = null)
{
return $this->getChanges($start, $end, true, $tasklists);
}
/**
* Returns the timestamp of an operation for a given uid an action.
*
* @param string $uid The uid to look for.
* @param string $action The action to check for - add, modify, or delete.
* @param string $tasklist The tasklist to be used. If 'null', the
* user's default tasklist will be used.
* @param boolean $modSeq Request a modification sequence instead of a
* timestamp. @since 4.1.1
*
* @return integer The timestamp for this action.
*
* @throws InvalidArgumentException
* @throws Horde_Exception_PermissionDenied
* @thorws Horde_History_Exception
*/
public function getActionTimestamp($uid, $action, $tasklist = null, $modSeq = false)
{
if ($tasklist === null) {
$tasklist = Nag::getDefaultTasklist(Horde_Perms::READ);
} elseif (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
if (!$modSeq) {
return $GLOBALS['injector']
->getInstance('Horde_History')
->getActionTimestamp('nag:' . $tasklist . ':' . $uid, $action);
} else {
return $GLOBALS['injector']
->getInstance('Horde_History')
->getActionModSeq('nag:' . $tasklist . ':' . $uid, $action);
}
}
/**
* Return the largest modification sequence from the history backend.
*
* @param string $id Limit the check to this tasklist. @since 4.2.0
*
* @return integer The modseq.
* @since 4.1.1
*/
public function getHighestModSeq($id = null)
{
$parent = 'nag';
if (!empty($id)) {
$parent .= ':' . $id;
}
return $GLOBALS['injector']->getInstance('Horde_History')->getHighestModSeq($parent);
}
/**
* Imports one or more tasks represented in the specified content type.
*
* If a UID is present in the content and the task is already in the
* database, a replace is performed rather than an add.
*
* @param string $content The content of the task.
* @param string $contentType What format is the data in? Currently supports:
* text/calendar
* text/x-vcalendar
* @param string $tasklist The tasklist into which the task will be
* imported. If 'null', the user's default
* tasklist will be used.
*
* @return string The new UID on one import, an array of UIDs on multiple imports,
*/
public function import($content, $contentType, $tasklist = null)
{
if ($tasklist === null) {
$tasklist = Nag::getDefaultTasklist(Horde_Perms::EDIT);
} elseif (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
/* Create a Nag_Driver instance. */
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist);
switch ($contentType) {
case 'text/x-vcalendar':
case 'text/calendar':
case 'text/x-vtodo':
$iCal = new Horde_Icalendar();
if (!($content instanceof Horde_Icalendar_Vtodo)) {
if (!$iCal->parsevCalendar($content)) {
throw new Nag_Exception(_("There was an error importing the iCalendar data."));
}
} else {
$iCal->addComponent($content);
}
$components = $iCal->getComponents();
if (count($components) == 0) {
throw new Nag_Exception(_("No iCalendar data was found."));
}
$ids = array();
foreach ($components as $content) {
if ($content instanceof Horde_Icalendar_Vtodo) {
$task = new Nag_Task($storage);
$task->fromiCalendar($content);
if (isset($task->uid)) {
try {
$existing = $storage->getByUID($task->uid);
$task->owner = $existing->owner;
$storage->modify($existing->id, $task->toHash());
} catch ( Horde_Exception_NotFound $e ) {
$storage->add($task->toHash());
}
$ids[] = $task->uid;
} else {
$hash = $task->toHash();
unset($hash['uid']);
$newTask = $storage->add($hash);
// use UID rather than ID
$ids[] = $newTask[1];
}
}
}
if (count($ids) == 0) {
throw new Nag_Exception(_("No iCalendar data was found."));
} else if (count($ids) == 1) {
return $ids[0];
}
return $ids;
case 'activesync':
$task = new Nag_Task();
$task->fromASTask($content);
$hash = $task->toHash();
unset($hash['uid']);
$results = $storage->add($hash);
/* array index 0 is id, 1 is uid */
return $results[1];
}
throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
/**
* Adds a task.
*
* @param array $task A hash with task information.
*
* @throws Horde_Exception_PermissionDenied
*/
public function addTask(array $task)
{
if (!$GLOBALS['registry']->isAdmin() &&
!Nag::hasPermission($task['tasklist'], Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($task['tasklist']);
return $storage->add($task);
}
/**
* Imports one or more tasks parsed from a string.
*
* @param string $text The text to parse into
* @param string $tasklist The tasklist into which the task will be
* imported. If 'null', the user's default
* tasklist will be used.
*
* @return array The UIDs of all tasks that were added.
* @throws Horde_Exception_PermissionDenied
*/
public function quickAdd($text, $tasklist = null)
{
if ($tasklist === null) {
$tasklist = Nag::getDefaultTasklist(Horde_Perms::EDIT);
} elseif (!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
return Nag::createTasksFromText($text, $tasklist);
}
/**
* Toggles the task completion flag.
*
* @param string $task_id The task ID.
* @param string $tasklist_id The tasklist that contains the task.
*
* @return boolean|string True if the task has been toggled, a due date if
* there are still incomplete recurrences, otherwise
* false.
*/
public function toggleCompletion($task_id, $tasklist_id)
{
if (!Nag::hasPermission($tasklist_id, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
try {
$share = $GLOBALS['nag_shares']->getShare($tasklist_id);
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
throw new Nag_Exception($e);
}
$task = Nag::getTask($tasklist_id, $task_id);
$completed = $task->completed;
try {
$task->toggleComplete();
} catch (Nag_Exception $e) {
Horde::log($e->getMessage(), 'DEBUG');
return false;
}
$task->save();
$due = $task->getNextDue();
if ($task->completed == $completed) {
if ($due) {
return $due->toJson();
}
return false;
}
return true;
}
/**
* Exports a task, identified by UID, in the requested content type.
*
* @param string $uid Identify the task to export.
* @param string $contentType What format should the data be in?
* A string with one of:
* - text/calendar: iCalendar 2.0. Recommended as this is specified in
* RFC 2445.
* - text/x-vcalendar: vCalendar 1.0 format. Still in wide use.
* - activesync: Horde_ActiveSync_Message_Task.
* - raw: Nag_Task.
* @param array $options Any additional options for the exporter.
*
* @return string The requested data.
*/
public function export($uid, $contentType, array $options = array())
{
$task = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create('')
->getByUID($uid);
if (!Nag::hasPermission($task->tasklist, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
$version = '2.0';
switch ($contentType) {
case 'text/x-vcalendar':
$version = '1.0';
case 'text/calendar':
// Create the new iCalendar container.
$iCal = new Horde_Icalendar($version);
$iCal->setAttribute('PRODID', '-//The Horde Project//Nag ' . $GLOBALS['registry']->getVersion() . '//EN');
$iCal->setAttribute('METHOD', 'PUBLISH');
// Create new vTodo object.
$vTodo = $task->toiCalendar($iCal);
$vTodo->setAttribute('VERSION', $version);
$iCal->addComponent($vTodo);
return $iCal->exportvCalendar();
case 'activesync':
return $task->toASTask($options);
case 'raw':
return $task;
default:
throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
}
/**
* Returns a task object.
*
* @param string $tasklist A tasklist id.
* @param string $id A task id.
*
* @return Nag_Task The matching task object.
*/
public function getTask($tasklist, $id)
{
if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
return Nag::getTask($tasklist, $id);
}
/**
* Exports a tasklist in the requested content type.
*
* @param string $tasklist The tasklist to export.
* @param string $contentType What format should the data be in?
* A string with one of:
*
* text/calendar (VCALENDAR 2.0. Recommended as
* this is specified in rfc2445)
* text/x-vcalendar (old VCALENDAR 1.0 format.
* Still in wide use)
*
*
* @return string The iCalendar representation of the tasklist.
*/
public function exportTasklist($tasklist, $contentType)
{
if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
$tasks = Nag::listTasks(array(
'tasklists' => array($tasklist),
'completed' => Nag::VIEW_ALL,
'external' => false,
'include_tags' => true));
$version = '2.0';
switch ($contentType) {
case 'text/x-vcalendar':
$version = '1.0';
case 'text/calendar':
$share = $GLOBALS['nag_shares']->getShare($tasklist);
$iCal = new Horde_Icalendar($version);
$iCal->setAttribute('X-WR-CALNAME', $share->get('name'));
$tasks->reset();
while ($task = $tasks->each()) {
$iCal->addComponent($task->toiCalendar($iCal));
}
return $iCal->exportvCalendar();
}
throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
/**
* Deletes a task identified by UID.
*
* @param string|array $uid Identify the task to delete, either a single UID
* or an array.
*
* @return boolean Success or failure.
*/
public function delete($uid)
{
// Handle an arrray of UIDs for convenience
if (is_array($uid)) {
foreach ($uid as $g) {
$result = $this->delete($g);
}
return true;
}
$factory = $GLOBALS['injector']->getInstance('Nag_Factory_Driver');
$task = $factory->create('')->getByUID($uid);
if (!$GLOBALS['registry']->isAdmin() &&
!Nag::hasPermission($task->tasklist, Horde_Perms::DELETE)) {
throw new Horde_Exception_PermissionDenied();
}
return $factory->create($task->tasklist)->delete($task->id);
}
/**
* Deletes a task identified by tasklist and ID.
*
* @param string $tasklist A tasklist id.
* @param string $id A task id.
*/
public function deleteTask($tasklist, $id)
{
if (!$GLOBALS['registry']->isAdmin() &&
!Nag::hasPermission($tasklist, Horde_Perms::DELETE)) {
throw new Horde_Exception_PermissionDenied();
}
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist);
return $storage->delete($id);
}
/**
* Replaces the task identified by UID with the content represented in the
* specified content type.
*
* If you want to replace multiple tasks with the UID specified in the
* VCALENDAR data, you may use $this->import instead. This automatically does a
* replace if existings UIDs are found.
*
*
* @param string $uid Identify the task to replace.
* @param string $content The content of the task.
* @param string $contentType What format is the data in? Currently supports:
* - text/x-vcalendar
* - text/calendar
*
* @return boolean Success or failure.
*/
public function replace($uid, $content, $contentType)
{
$factory = $GLOBALS['injector']->getInstance('Nag_Factory_Driver');
$existing = $factory->create('')->getByUID($uid);
$taskId = $existing->id;
$owner = $existing->owner;
if (!Nag::hasPermission($existing->tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
switch ($contentType) {
case 'text/calendar':
case 'text/x-vcalendar':
if (!($content instanceof Horde_Icalendar_Vtodo)) {
$iCal = new Horde_Icalendar();
if (!$iCal->parsevCalendar($content)) {
throw new Nag_Exception(_("There was an error importing the iCalendar data."));
}
$components = $iCal->getComponents();
$component = null;
foreach ($components as $content) {
if ($content instanceof Horde_Icalendar_Vtodo) {
if ($component !== null) {
throw new Nag_Exception(_("Multiple iCalendar components found; only one vTodo is supported."));
}
$component = $content;
}
}
if ($component === null) {
throw new Nag_Exception(_("No iCalendar data was found."));
}
}
$task = new Nag_Task();
$task->fromiCalendar($content);
$task->owner = $owner;
$factory->create($existing->tasklist)->modify($taskId, $task->toHash());
break;
case 'activesync':
$task = new Nag_Task();
$task->fromASTask($content);
$task->owner = $owner;
$factory->create($existing->tasklist)->modify($taskId, $task->toHash());
break;
default:
throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
return $result;
}
/**
* Changes a task identified by tasklist and ID.
*
* @param string $tasklist A tasklist id.
* @param string $id A task id.
* @param array $task A hash with overwriting task information.
*/
public function updateTask($tasklist, $id, $task)
{
if (!$GLOBALS['registry']->isAdmin() &&
!Nag::hasPermission($tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist);
$existing = $storage->get($id);
$task['owner'] = $existing->owner;
return $storage->modify($id, $task);
}
/**
* Lists active tasks as cost objects.
*
* @todo Implement $criteria parameter.
*
* @param array $criteria Filter attributes
*/
public function listCostObjects($criteria)
{
$tasks = Nag::listTasks(array(
'completed' => Nag::VIEW_ALL,
'include_history' => false)
);
$result = array();
$tasks->reset();
$last_week = $_SERVER['REQUEST_TIME'] - 7 * 86400;
while ($task = $tasks->each()) {
if (($task->completed && $task->completed_date < $last_week) ||
($task->start && $task->start > $_SERVER['REQUEST_TIME'])) {
continue;
}
$result[$task->id] = array(
'id' => $task->id,
'active' => !$task->completed,
'name' => $task->name
);
for ($parent = $task->parent; $parent->parent; $parent = $parent->parent) {
$result[$task->id]['name'] = $parent->name . ': '
. $result[$task->id]['name'];
}
if (!empty($task->estimate)) {
$result[$task->id]['estimate'] = $task->estimate;
}
}
if (count($result) == 0) {
return array();
} else {
return array(array('category' => _("Tasks"),
'objects' => array_values($result)));
}
}
public function listTimeObjectCategories()
{
$categories = array();
$tasklists = Nag::listTasklists(false, Horde_Perms::SHOW | Horde_Perms::READ);
foreach ($tasklists as $tasklistId => $tasklist) {
$categories[$tasklistId] = array('title' => Nag::getLabel($tasklist), 'type' => 'share');
}
return $categories;
}
/**
* Lists active tasks as time objects.
*
* @param array $categories The time categories (from
* listTimeObjectCategories) to list.
* @param mixed $start The start date of the period.
* @param mixed $end The end date of the period.
*/
public function listTimeObjects($categories, $start, $end)
{
$allowed_tasklists = Nag::listTasklists(false, Horde_Perms::READ);
foreach ($categories as $tasklist) {
if (!isset($allowed_tasklists[$tasklist])) {
throw new Horde_Exception_PermissionDenied();
}
}
$timeobjects = array();
$start = new Horde_Date($start);
$start_ts = $start->timestamp();
$end = new Horde_Date($end);
$end_ts = $end->timestamp();
// List incomplete tasks.
$tasks = Nag::listTasks(array(
'tasklists' => $categories,
'completed' => Nag::VIEW_FUTURE_INCOMPLETE,
'include_history' => false)
);
$tasks->reset();
while ($task = $tasks->each()) {
// If there's no due date, it's not a time object.
if (!$task->due ||
$task->due > $end_ts ||
(!$task->recurs() && $task->due + 1 < $start_ts) ||
($task->recurs() && $task->recurrence->getRecurEnd() &&
$task->recurrence->getRecurEnd()->timestamp() + 1 < $start_ts)) {
continue;
}
$due_date = date('Y-m-d\TH:i:s', $task->due);
$recurrence = null;
if ($task->recurs()) {
$recurrence = array(
'type' => $task->recurrence->getRecurType(),
'interval' => $task->recurrence->getRecurInterval(),
'end' => $task->recurrence->getRecurEnd(),
'count' => $task->recurrence->getRecurCount(),
'days' => $task->recurrence->getRecurOnDays(),
'exceptions' => $task->recurrence->getExceptions(),
'completions' => $task->recurrence->getCompletions());
}
$timeobjects[$task->id] = array(
'id' => $task->id,
'title' => $task->name,
'description' => $task->desc,
'start' => $due_date,
'end' => $due_date,
'recurrence' => $recurrence,
'color' => $allowed_tasklists[$task->tasklist]->get('color'),
'owner' => $allowed_tasklists[$task->tasklist]->get('owner'),
'permissions' => $GLOBALS['nag_shares']->getPermissions($task->tasklist, $GLOBALS['registry']->getAuth()),
'variable_length' => false,
'params' => array(
'task' => $task->id,
'tasklist' => $task->tasklist,
),
'link' => Horde::url('view.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id)),
'edit_link' => Horde::url('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'modify_task')),
'delete_link' => Horde::url('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'delete_task')),
'ajax_link' => 'task:' . $task->tasklist . ':' . $task->id
);
}
return $timeobjects;
}
/**
* Saves properties of a time object back to the task that it represents.
*
* At the moment only the title, description and due date are saved.
*
* @param array $timeobject A time object hash.
* @throws Nag_Exception
*/
public function saveTimeObject(array $timeobject)
{
if (!Nag::hasPermission($timeobject['params']['tasklist'], Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($timeobject['params']['tasklist']);
$existing = $storage->get($timeobject['id']);
$info = array();
if (isset($timeobject['start'])) {
$info['due'] = new Horde_Date($timeobject['start']);
$info['due'] = $info['due']->timestamp();
}
if (isset($timeobject['title'])) {
$info['name'] = $timeobject['title'];
}
if (isset($timeobject['description'])) {
$info['desc'] = $timeobject['description'];
}
$storage->modify($timeobject['id'], $info);
}
/**
* Returns a list of available sources.
*
* @param boolean $writeable If true, limits to writeable sources.
* @param boolean $sync_only Only include synchable address books.
*
* @return array An array of the available sources. Keys are source IDs,
* values are source titles.
* @since 4.2.0
*/
public function sources($writeable = false, $sync_only = false)
{
$out = array();
foreach (Nag::listTasklists(false, $writeable ? Horde_Perms::EDIT : Horde_Perms::READ, false) as $key => $val) {
$out[$key] = $val->get('name');
}
if ($sync_only) {
$syncable = Nag::getSyncLists();
$out = array_intersect_key($out, array_flip($syncable));
}
return $out;
}
/**
* Retrieve the UID for the current user's default tasklist.
*
* @return string UID.
* @since 4.2.0
*/
public function getDefaultShare()
{
return Nag::getDefaultTasklist(Horde_Perms::EDIT);
}
/**
* Retrieve the list of used tag_names, tag_ids and the total number
* of resources that are linked to that tag.
*
* @param array $tags An optional array of tag_ids. If omitted, all tags
* will be included.
*
* @return array An array containing tag_name, and total
*/
public function listTagInfo($tags = null, $user = null)
{
return $GLOBALS['injector']->getInstance('Nag_Tagger')
->getTagInfo($tags, 500, null, $user);
}
/**
* SearchTags API:
* Returns an application-agnostic array (useful for when doing a tag search
* across multiple applications)
*
* The 'raw' results array can be returned instead by setting $raw = true.
*
* @param array $names An array of tag_names to search for.
* @param integer $max The maximum number of resources to return.
* @param integer $from The number of the resource to start with.
* @param string $resource_type The resource type [bookmark, '']
* @param string $user Restrict results to resources owned by $user.
* @param boolean $raw Return the raw data?
*
* @return array An array of results:
*
* 'title' - The title for this resource.
* 'desc' - A terse description of this resource.
* 'view_url' - The URL to view this resource.
* 'app' - The Horde application this resource belongs to.
* 'icon' - URL to an image.
*
*/
public function searchTags($names, $max = 10, $from = 0,
$resource_type = '', $user = null, $raw = false)
{
// TODO: $max, $from, $resource_type not honored
global $injector, $registry;
$results = $injector
->getInstance('Nag_Tagger')
->search(
$names,
array('user' => $user));
// Check for error or if we requested the raw data array.
if ($raw) {
return $results;
}
$return = array();
$redirectUrl = Horde::url('redirect.php');
foreach ($results as $task_id) {
try {
$task = $injector->getInstance('Nag_Factory_Driver')
->create(null)
->getByUID($task_id);
$return[] = array(
'title' => $task->name,
'desc' => $task->description,
'view_url' => $redirectUrl->add('b', $task->id),
'app' => 'nag'
);
} catch (Exception $e) {
}
}
return $return;
}
}
nag-4.2.7/lib/Application.php 0000664 0001750 0001750 00000055465 12654116077 014147 0 ustar jan jan true,
'modseq' => true,
);
/**
*/
public $version = 'H5 (4.2.7)';
/**
* Global variables defined:
* $nag_shares - TODO
*/
protected function _init()
{
// Set the timezone variable.
$GLOBALS['registry']->setTimeZone();
/* For now, autoloading the Content_* classes depend on there being a
* registry entry for the 'content' application that contains at least
* the fileroot entry. */
$GLOBALS['injector']->getInstance('Horde_Autoloader')
->addClassPathMapper(
new Horde_Autoloader_ClassPathMapper_Prefix('/^Content_/', $GLOBALS['registry']->get('fileroot', 'content') . '/lib/'));
// Create a share instance.
$GLOBALS['nag_shares'] = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create();
Nag::initialize();
}
/**
*/
public function perms()
{
return array(
'max_tasks' => array(
'title' => _("Maximum Number of Tasks"),
'type' => 'int'
)
);
}
/**
* Generate links in the sidebar.
*
* @param Horde_Menu The menu object.
*/
public function menu($menu)
{
global $conf;
$menu->add(Horde::url('list.php'), _("_List Tasks"), 'nag-list', null, null, null, (basename($_SERVER['PHP_SELF']) == 'list.php' || strpos($_SERVER['PHP_SELF'], 'nag/index.php') !== false) ? 'current' : null);
/* Search. */
$menu->add(Horde::url('search.php'), _("_Search"), 'nag-search');
/* Import/Export. */
if ($conf['menu']['import_export']) {
$menu->add(Horde::url('data.php'), _("_Import/Export"), 'horde-data');
}
}
/**
* Add additional items to the sidebar.
*
* @param Horde_View_Sidebar $sidebar The sidebar object.
*/
public function sidebar($sidebar)
{
// @TODO: Implement an injector factory for this.
global $display_tasklists, $page_output, $prefs;
$perms = $GLOBALS['injector']->getInstance('Horde_Core_Perms');
if (Nag::getDefaultTasklist(Horde_Perms::EDIT) &&
($perms->hasAppPermission('max_tasks') === true ||
$perms->hasAppPermission('max_tasks') > Nag::countTasks())) {
$sidebar->addNewButton(
_("_New Task"),
Horde::url('task.php')->add('actionID', 'add_task'));
if ($GLOBALS['browser']->hasFeature('dom')) {
$page_output->addScriptFile('scriptaculous/effects.js', 'horde');
$page_output->addScriptFile('redbox.js', 'horde');
$blank = new Horde_Url();
$sidebar->newExtra = $blank->link(
array_merge(
array('onclick' => 'RedBox.showInline(\'quickAddInfoPanel\'); $(\'quickText\').focus(); return false;'),
Horde::getAccessKeyAndTitle(_("_Quick Add"), false, true)
)
);
require_once NAG_TEMPLATES . '/quick.inc';
}
}
$list = Horde::url('list.php');
$edit = Horde::url('tasklists/edit.php');
$user = $GLOBALS['registry']->getAuth();
$sidebar->containers['my'] = array(
'header' => array(
'id' => 'nag-toggle-my',
'label' => _("My Task Lists"),
'collapsed' => false,
),
);
if (!$GLOBALS['prefs']->isLocked('default_tasklist')) {
$sidebar->containers['my']['header']['add'] = array(
'url' => Horde::url('tasklists/create.php'),
'label' => _("Create a new Task List"),
);
}
if ($GLOBALS['registry']->isAdmin()) {
$sidebar->containers['system'] = array(
'header' => array(
'id' => 'nag-toggle-system',
'label' => _("System Task Lists"),
'collapsed' => true,
),
);
$sidebar->containers['system']['header']['add'] = array(
'url' => Horde::url('tasklists/create.php')->add('system', 1),
'label' => _("Create a new System Task List"),
);
}
$sidebar->containers['shared'] = array(
'header' => array(
'id' => 'nag-toggle-shared',
'label' => _("Shared Task Lists"),
'collapsed' => true,
),
);
foreach (Nag::listTasklists(false, Horde_Perms::SHOW, false) as $name => $tasklist) {
$url = $list->add(array(
'display_tasklist' => $name,
'actionID' => in_array($name, $display_tasklists)
? 'remove_displaylist'
: 'add_displaylist'
));
$row = array(
'selected' => in_array($name, $display_tasklists),
'url' => $url,
'label' => Nag::getLabel($tasklist),
'color' => $tasklist->get('color') ?: '#dddddd',
'edit' => $edit->add('t', $tasklist->getName()),
'type' => 'checkbox',
);
if ($GLOBALS['registry']->isAdmin() &&
is_null($tasklist->get('owner'))) {
$sidebar->addRow($row, 'system');
} elseif ($tasklist->get('owner') == $user) {
$sidebar->addRow($row, 'my');
} else {
$sidebar->addRow($row, 'shared');
}
}
}
/**
*/
public function hasPermission($permission, $allowed, $opts = array())
{
if (is_array($allowed)) {
switch ($permission) {
case 'max_tasks':
$allowed = max($allowed);
break;
}
}
return $allowed;
}
/**
* Remove all data for the specified user.
*
* @param string $user The user to remove.
* @throws Nag_Exception
*/
public function removeUserData($user)
{
try {
$shares = $GLOBALS['nag_shares']
->listShares($user, array('attributes' => $user));
} catch (Horde_Share_Exception $e) {
Horde::log($e, 'ERR');
throw new Nag_Exception($e);
}
$error = false;
foreach ($shares as $share) {
$storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($share->getName());
$result = $storage->deleteAll();
try {
$GLOBALS['nag_shares']->removeShare($share);
} catch (Horde_Share_Exception $e) {
Horde::log($e, 'NOTICE');
$error = true;
}
}
/* Now remove perms for this user from all other shares */
try {
$shares = $GLOBALS['nag_shares']->listShares($user);
foreach ($shares as $share) {
$share->removeUser($user);
}
} catch (Horde_Share_Exception $e) {
Horde::log($e, 'NOTICE');
$error = true;
}
if ($error) {
throw new Nag_Exception(sprintf(_("There was an error removing tasks for %s. Details have been logged."), $user));
}
}
/* Alarm method. */
/**
*/
public function listAlarms($time, $user = null)
{
if ((empty($user) || $user != $GLOBALS['registry']->getAuth()) &&
!$GLOBALS['registry']->isAdmin()) {
throw new Horde_Exception_PermissionDenied();
}
$group = $GLOBALS['injector']->getInstance('Horde_Group');
$alarm_list = array();
$tasklists = is_null($user) ?
array_keys($GLOBALS['nag_shares']->listAllShares()) :
$GLOBALS['display_tasklists'];
$alarms = Nag::listAlarms($time, $tasklists);
foreach ($alarms as $alarm) {
try {
$share = $GLOBALS['nag_shares']->getShare($alarm->tasklist);
} catch (Horde_Share_Exception $e) {
continue;
}
if (empty($user)) {
$users = $share->listUsers(Horde_Perms::READ);
$groups = $share->listGroups(Horde_Perms::READ);
foreach ($groups as $gid) {
$users = array_merge($users, $group->listUsers($gid));
}
$users = array_unique($users);
} else {
$users = array($user);
}
foreach ($users as $alarm_user) {
$prefs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Prefs')->create('nag', array(
'cache' => false,
'user' => $alarm_user
));
$GLOBALS['registry']->setLanguageEnvironment($prefs->getValue('language'));
$alarm_list[] = $alarm->toAlarm($alarm_user, $prefs);
}
}
return $alarm_list;
}
/* Topbar method. */
/**
*/
public function topbarCreate(Horde_Tree_Renderer_Base $tree, $parent = null,
array $params = array())
{
global $registry;
switch ($params['id']) {
case 'menu':
$add = Horde::url('task.php', true)->add('actionID', 'add_task');
$tree->addNode(array(
'id' => $parent . '__new',
'parent' => $parent,
'label' => _("New Task"),
'expanded' => false,
'params' => array(
'icon' => Horde_Themes::img('add.png'),
'url' => $add
)
));
$user = $registry->getAuth();
foreach (Nag::listTasklists(false, Horde_Perms::SHOW, false) as $name => $tasklist) {
if (!$tasklist->hasPermission($user, Horde_Perms::EDIT)) {
continue;
}
$tree->addNode(array(
'id' => $parent . $name . '__new',
'parent' => $parent . '__new',
'label' => sprintf(_("in %s"), Nag::getLabel($tasklist)),
'expanded' => false,
'params' => array(
'icon' => Horde_Themes::img('add.png'),
'url' => $add->copy()->add('tasklist_id', $name)
)
));
}
$tree->addNode(array(
'id' => $parent . '__search',
'parent' => $parent,
'label' => _("Search"),
'expanded' => false,
'params' => array(
'icon' => Horde_Themes::img('search.png'),
'url' => Horde::url('search.php')
)
));
break;
}
}
/* Download data. */
/**
* @throws Nag_Exception
*/
public function download(Horde_Variables $vars)
{
global $display_tasklists, $injector, $registry;
switch ($vars->actionID) {
case 'export':
$tasklists = $vars->get('exportList', $display_tasklists);
if (!is_array($tasklists)) {
$tasklists = array($tasklists);
}
/* Get the full, sorted task list. */
$tasks = Nag::listTasks(array(
'tasklists' => $tasklists,
'completed' => $vars->exportTasks,
'include_tags' => true,
'include_history' => false)
);
$tasks->reset();
switch ($vars->exportID) {
case Horde_Data::EXPORT_CSV:
$data = array();
while ($task = $tasks->each()) {
$task = $task->toHash();
$task['desc'] = str_replace(',', '', $task['desc']);
$task['tags'] = implode(',', $task['tags']);
unset(
$task['complete_link'],
$task['delete_link'],
$task['edit_link'],
$task['parent'],
$task['task_id'],
$task['tasklist_id'],
$task['view_link'],
$task['recurrence'],
$task['methods']
);
foreach (array('start', 'due', 'completed_date') as $field) {
if (!empty($task[$field])) {
$date = new Horde_Date($task[$field]);
$task[$field] = $date->format('c');
}
}
$data[] = $task;
}
$injector->getInstance('Horde_Core_Factory_Data')->create('Csv', array('cleanup' => array($this, 'cleanupData')))->exportFile(_("tasks.csv"), $data, true);
exit;
case Horde_Data::EXPORT_ICALENDAR:
$iCal = new Horde_Icalendar();
$iCal->setAttribute(
'PRODID',
'-//The Horde Project//Nag ' . $registry->getVersion() . '//EN');
while ($task = $tasks->each()) {
$iCal->addComponent($task->toiCalendar($iCal));
}
return array(
'data' => $iCal->exportvCalendar(),
'name' => _("tasks.ics"),
'type' => 'text/calendar'
);
}
}
}
/**
*/
public function cleanupData()
{
$GLOBALS['import_step'] = 1;
return Horde_Data::IMPORT_FILE;
}
/* DAV methods. */
/**
*/
public function davGetCollections($user)
{
global $injector, $nag_shares, $registry;
$hordeUser = $registry->convertUsername($user, true);
$shares = $nag_shares->listShares($hordeUser);
$dav = $injector->getInstance('Horde_Dav_Storage');
$tasklists = array();
foreach ($shares as $id => $share) {
if ($user == '-system-' && $share->get('owner')) {
continue;
}
try {
$id = $dav->getExternalCollectionId($id, 'tasks') ?: $id;
} catch (Horde_Dav_Exception $e) {
}
$tasklists[] = array(
'id' => $id,
'uri' => $id,
'{' . CalDAV\Plugin::NS_CALENDARSERVER . '}shared-url' =>
Nag::getUrl(Nag::DAV_CALDAV, $share),
'principaluri' => 'principals/' . $user,
'{http://sabredav.org/ns}owner-principal' =>
'principals/'
. ($share->get('owner')
? $registry->convertUsername($share->get('owner'), false)
: '-system-'
),
'{DAV:}displayname' => Nag::getLabel($share),
'{urn:ietf:params:xml:ns:caldav}calendar-description' =>
$share->get('desc'),
'{http://apple.com/ns/ical/}calendar-color' =>
$share->get('color'),
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre\CalDAV\Property\SupportedCalendarComponentSet(array('VTODO')),
'{http://sabredav.org/ns}read-only' => !$share->hasPermission($hordeUser, Horde_Perms::EDIT),
);
}
return $tasklists;
}
/**
*/
public function davGetObjects($collection)
{
$dav = $GLOBALS['injector']
->getInstance('Horde_Dav_Storage');
$internal = $dav->getInternalCollectionId($collection, 'tasks') ?: $collection;
if (!Nag::hasPermission($internal, Horde_Perms::READ)) {
throw new Nag_Exception("Task List does not exist or no permission to edit");
}
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($internal);
$storage->retrieve();
$storage->tasks->reset();
$tasks = array();
while ($task = $storage->tasks->each()) {
$id = $task->id;
$modified = $this->_modified($internal, $task->uid);
try {
$id = $dav->getExternalObjectId($id, $internal) ?: $id . '.ics';
} catch (Horde_Dav_Exception $e) {
}
$tasks[] = array(
'id' => $id,
'uri' => $id,
'lastmodified' => $modified,
'etag' => '"' . md5($task->id . '|' . $modified) . '"',
'calendarid' => $collection,
);
}
return $tasks;
}
/**
*/
public function davGetObject($collection, $object)
{
$dav = $GLOBALS['injector']
->getInstance('Horde_Dav_Storage');
$internal = $dav->getInternalCollectionId($collection, 'tasks') ?: $collection;
if (!Nag::hasPermission($internal, Horde_Perms::READ)) {
throw new Nag_Exception("Task List does not exist or no permission to edit");
}
try {
$object = $dav->getInternalObjectId($object, $internal) ?: preg_replace('/\.ics$/', '', $object);
} catch (Horde_Dav_Exception $e) {
}
$task = Nag::getTask($internal, $object);
$id = $task->id;
$modified = $this->_modified($internal, $task->uid);
try {
$id = $dav->getExternalObjectId($id, $internal) ?: $id . '.ics';
} catch (Horde_Dav_Exception $e) {
}
$share = $GLOBALS['nag_shares']->getShare($internal);
$ical = new Horde_Icalendar('2.0');
$ical->setAttribute('X-WR-CALNAME', $share->get('name'));
$ical->addComponent($task->toiCalendar($ical));
$data = $ical->exportvCalendar();
return array(
'id' => $id,
'calendardata' => $data,
'uri' => $id,
'lastmodified' => $modified,
'etag' => '"' . md5($task->id . '|' . $modified) . '"',
'calendarid' => $collection,
'size' => strlen($data),
);
}
/**
*/
public function davPutObject($collection, $object, $data)
{
$dav = $GLOBALS['injector']
->getInstance('Horde_Dav_Storage');
$internal = $dav->getInternalCollectionId($collection, 'tasks') ?: $collection;
if (!Nag::hasPermission($internal, Horde_Perms::EDIT)) {
throw new Nag_Exception("Task List does not exist or no permission to edit");
}
$ical = new Horde_Icalendar();
if (!$ical->parsevCalendar($data)) {
throw new Nag_Exception(_("There was an error importing the iCalendar data."));
}
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($internal);
foreach ($ical->getComponents() as $content) {
if (!($content instanceof Horde_Icalendar_Vtodo)) {
continue;
}
$task = new Nag_Task();
$task->fromiCalendar($content);
try {
try {
$existing_id = $dav->getInternalObjectId($object, $internal)
?: preg_replace('/\.ics$/', '', $object);
} catch (Horde_Dav_Exception $e) {
$existing_id = $object;
}
$existing_task = Nag::getTask($internal, $existing_id);
/* Check if our task is newer then the existing - get the
* task's history. */
$modified = $this->_modified($internal, $existing_task->uid);
try {
if (!empty($modified) &&
$content->getAttribute('LAST-MODIFIED') < $modified) {
/* LAST-MODIFIED timestamp of existing entry is newer:
* don't replace it. */
continue;
}
} catch (Horde_Icalendar_Exception $e) {
}
$task->owner = $existing_task->owner;
$storage->modify($existing_task->id, $task->toHash());
} catch (Horde_Exception_NotFound $e) {
$hash = $task->toHash();
$newTask = $storage->add($hash);
$dav->addObjectMap($newTask[0], $object, $internal);
}
}
}
/**
*/
public function davDeleteObject($collection, $object)
{
$dav = $GLOBALS['injector']->getInstance('Horde_Dav_Storage');
$internal = $dav->getInternalCollectionId($collection, 'tasks') ?: $collection;
if (!Nag::hasPermission($internal, Horde_Perms::DELETE)) {
throw new Nag_Exception("Task List does not exist or no permission to delete");
}
try {
$object = $dav->getInternalObjectId($object, $internal)
?: preg_replace('/\.ics$/', '', $object);
} catch (Horde_Dav_Exception $e) {
}
$GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($internal)
->delete($object);
try {
$dav->deleteExternalObjectId($object, $internal);
} catch (Horde_Dav_Exception $e) {
}
}
/**
* Returns the last modification (or creation) date of a task.
*
* @param string $collection A task list ID.
* @param string $object A task UID.
*
* @return integer Timestamp of the last modification.
*/
protected function _modified($collection, $uid)
{
$history = $GLOBALS['injector']
->getInstance('Horde_History');
$modified = $history->getActionTimestamp(
'nag:' . $collection . ':' . $uid,
'modify'
);
if (!$modified) {
$modified = $history->getActionTimestamp(
'nag:' . $collection . ':' . $uid,
'add'
);
}
return $modified;
}
}
nag-4.2.7/lib/CompleteTask.php 0000664 0001750 0001750 00000004276 12654116077 014271 0 ustar jan jan getShare($tasklist);
$task = Nag::getTask($tasklist, $task);
if (!$share->hasPermission($registry->getAuth(), Horde_Perms::EDIT)) {
$result = array('error' => 'permission denied');
$notification->push(_("Access denied completing this task."), 'horde.error');
} else {
$wasCompleted = $task->completed;
$task->loadChildren();
if ($wasCompleted &&
$task->parent &&
$task->parent->completed) {
$result = array('data' => 'complete');
$notification->push(_("Completed parent task, mark it as incomplete first"), 'horde.error');
} elseif (!$wasCompleted && !$task->childrenCompleted()) {
$result = array('data' => 'incomplete');
$notification->push(_("Incomplete sub tasks, complete them first"), 'horde.error');
} else {
$task->toggleComplete();
$task->save();
if ($task->completed) {
$result = array('data' => 'complete');
$notification->push(sprintf(_("Completed %s."), $task->name), 'horde.success');
} elseif (!$wasCompleted) {
$result = array('data' => 'incomplete');
$notification->push(sprintf(_("%s is still incomplete."), $task->name), 'horde.success');
} else {
$result = array('data' => 'incomplete');
$notification->push(sprintf(_("%s is now incomplete."), $task->name), 'horde.success');
}
}
}
} catch (Exception $e) {
$result = array('error' => $e->getMessage());
$notification->push(sprintf(_("There was a problem completing %s: %s"), $task->name, $e->getMessage()), 'horde.error');
}
return $result;
}
}
nag-4.2.7/lib/Driver.php 0000664 0001750 0001750 00000045644 12654116077 013135 0 ustar jan jan
* @author Jan Schneider
* @package Nag
*/
abstract class Nag_Driver
{
/**
* A Nag_Task instance holding the current task list.
*
* @var Nag_Task
*/
public $tasks;
/**
* String containing the current tasklist.
*
* @var string
*/
protected $_tasklist = '';
/**
* Hash containing connection parameters.
*
* @var array
*/
protected $_params = array();
/**
* An error message to throw when something is wrong.
*
* @var string
*/
protected $_errormsg;
/**
* Constructor - just store the $params in our newly-created
* object. All other work is done by initialize().
*
* @param array $params Any parameters needed for this driver.
* @param string $errormsg Custom error message
*
* @return Nag_Driver
*/
public function __construct(array $params = array(), $errormsg = null)
{
$this->tasks = new Nag_Task();
$this->_params = $params;
if (is_null($errormsg)) {
$this->_errormsg = _("The Tasks backend is not currently available.");
} else {
$this->_errormsg = $errormsg;
}
}
/**
* List all alarms near $date.
*
* @param integer $date The unix epoch time to check for alarms.
*
* @return array An array of tasks that have alarms that match.
*/
public function listAlarms($date)
{
if (!$this->tasks->count()) {
$result = $this->retrieve(0);
}
$alarms = array();
$this->tasks->reset();
while ($task = $this->tasks->each()) {
if ($task->alarm &&
($due = $task->getNextDue()) &&
($due->timestamp() - ($task->alarm * 60)) <= $date) {
$alarms[$task_id] = $task;
}
}
return $alarms;
}
/**
* Adds a task and handles notification.
*
* @param array $task A hash with the following possible properties:
* - name: (string) The name (short) of the task.
* - desc: (string) The description (long) of the task.
* - start: (OPTIONAL, integer) The start date of the task.
* - due: (OPTIONAL, integer) The due date of the task.
* - priority: (OPTIONAL, integer) The priority of the task.
* - estimate: (OPTIONAL, float) The estimated time to complete the
* task.
* - completed: (OPTIONAL, integer) The completion state of the task.
* - tags: (OPTIONAL, string) The comma delimited list of tags.
* - alarm: (OPTIONAL, integer) The alarm associated with the task.
* - methods: (OPTIONAL, array) The overridden alarm notification
* methods.
* - uid: (OPTIONAL, string) A Unique Identifier for the task.
* - parent: (OPTIONAL, string) The parent task.
* - private: (OPTIONAL, boolean) Whether the task is private.
* - owner: (OPTIONAL, string) The owner of the event.
* - assignee: (OPTIONAL, string) The assignee of the event.
* - completed_date: (OPTIONAL, integer) The task's completion date.
* - recurrence: (OPTIONAL, Horde_Date_Recurrence|array) Recurrence
* information.
*
* @return array array(ID,UID) of new task
*/
public function add(array $task)
{
$task = array_merge(
array('start' => 0,
'due' => 0,
'priority' => 3,
'estimate' => 0.0,
'completed' => 0,
'tags' => '',
'alarm' => 0,
'methods' => null,
'uid' => strval(new Horde_Support_Guid()),
'parent' => '',
'private' => false,
'owner' => $GLOBALS['registry']->getAuth(),
'assignee' => null,
'completed_date' => 0,
'recurrence' => null),
$task
);
$taskId = $this->_add($task);
$task = $this->get($taskId);
$task->process();
/* Log the creation of this item in the history log. */
$history = $GLOBALS['injector']->getInstance('Horde_History');
try {
$history->log('nag:' . $this->_tasklist . ':' . $task->uid,
array('action' => 'add'),
true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
/* Log completion status changes. */
if ($task->completed) {
try {
$history->log('nag:' . $this->_tasklist . ':' . $task->uid,
array('action' => 'complete'),
true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
}
/* Notify users about the new event. */
$result = Nag::sendNotification('add', $task);
/* Add an alarm if necessary. */
if (!empty($task->due) && !empty($task->alarm) &&
($alarm = $task->toAlarm())) {
$GLOBALS['injector']->getInstance('Horde_Alarm')->set($alarm);
}
return array($taskId, $task->uid);
}
/**
* @see add()
*/
abstract protected function _add(array $task);
/**
* Modifies an existing task and handles notification.
*
* @param string $taskId The task to modify.
* @param array $properties A hash with the following possible properties:
* - name: (string) The name (short) of the task.
* - desc: (string) The description (long) of the task.
* - start: (OPTIONAL, integer) The start date of the task.
* - due: (OPTIONAL, integer) The due date of the task.
* - priority: (OPTIONAL, integer) The priority of the task.
* - estimate: (OPTIONAL, float) The estimated time to complete the
* task.
* - completed: (OPTIONAL, integer) The completion state of the task.
* - tags: (OPTIONAL, string) The tags of the task.
* - alarm: (OPTIONAL, integer) The alarm associated with the task.
* - methods: (OPTIONAL, array) The overridden alarm notification
* methods.
* - uid: (OPTIONAL, string) A Unique Identifier for the task.
* - parent: (OPTIONAL, string) The parent task.
* - private: (OPTIONAL, boolean) Whether the task is private.
* - owner: (OPTIONAL, string) The owner of the event.
* - assignee: (OPTIONAL, string) The assignee of the event.
* - completed_date: (OPTIONAL, integer) The task's completion date.
* - tasklist: (OPTIONAL, string) The new tasklist.
* - recurrence: (OPTIONAL, Horde_Date_Recurrence|array) Recurrence
* information.
*
* @throws Nag_Exception
*/
public function modify($taskId, array $properties)
{
/* Retrieve unmodified task. */
$task = $this->get($taskId);
/* Avoid circular reference. */
if (isset($properties['parent']) &&
$properties['parent'] == $taskId) {
unset($properties['parent']);
}
/* Toggle completion. We cannot simply set the completion flag
* because this might be a recurring task, and marking the
* task complete might only shift the due date to the next
* recurrence. */
$task_completed = $task->completed;
if (isset($properties['completed']) &&
$properties['completed'] != $task->completed) {
if (isset($properties['recurrence'])) {
if ($task->recurs()) {
$completions = $task->recurrence->completions;
$exceptions = $task->recurrence->exceptions;
} else {
$completions = $exceptions = array();
}
$task->recurrence = $properties['recurrence'];
$task->recurrence->completions = $completions;
$task->recurrence->exceptions = $exceptions;
unset($properties['recurrence']);
}
$task->toggleComplete();
unset($properties['completed']);
}
$this->_modify($taskId, array_merge($task->toHash(), $properties));
// Must persist the original completed value so we can detect the
// change when notifications are sent.
$task->completed = $task_completed;
$new_task = $this->get($task->id);
$log_tasklist = $this->_tasklist;
if (isset($properties['tasklist']) &&
$task->tasklist != $properties['tasklist']) {
/* Moving the task to another tasklist. */
try {
$share = $GLOBALS['nag_shares']->getShare($task->tasklist);
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
throw new Nag_Exception($e);
}
if (!$share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::DELETE)) {
$GLOBALS['notification']->push(_("Access denied removing task from this task list."), 'horde.error');
return false;
}
try {
$share = $GLOBALS['nag_shares']->getShare($properties['tasklist']);
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
throw new Nag_Exception($e);
}
if (!$share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
$GLOBALS['notification']->push(_("Access denied moving the task to this task list."), 'horde.error');
}
$moved = $this->_move($task->id, $properties['tasklist']);
$new_storage = $GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($properties['tasklist']);
$new_task = $new_storage->get($task->id);
/* Log the moving of this item in the history log. */
if (!empty($task->uid)) {
$history = $GLOBALS['injector']->getInstance('Horde_History');
try {
$history->log('nag:' . $task->tasklist . ':' . $task->uid, array('action' => 'delete'), true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
try {
$history->log('nag:' . $properties['tasklist'] . ':' . $task->uid, array('action' => 'add'), true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
$log_tasklist = $properties['tasklist'];
}
$task->loadChildren();
if ($task->hasSubTasks()) {
foreach ($task->children as $child_task) {
$child_task = $child_task->toHash();
$child_task['tasklist'] = $properties['tasklist'];
$this->modify($child_task['task_id'], $child_task);
}
}
}
/* Update alarm if necessary. */
$horde_alarm = $GLOBALS['injector']->getInstance('Horde_Alarm');
if ((isset($properties['alarm']) && empty($properties['alarm'])) ||
!empty($properties['completed']) || empty($task->due)) {
$horde_alarm->delete($task->uid);
} else {
$task = $this->get($taskId);
$task->process();
if (!$task->recurs()) {
$alarm = $task->toAlarm();
} else {
$alarm = null;
/* Get current occurrence (task due date) */
$current = $task->recurrence->nextActiveRecurrence(new Horde_Date($task->due));
if ($current) {
/* Advance this occurence by a day to indicate that we
* want the following occurence (Recurrence uses days
* as minimal time duration between occurrences). */
$current->mday++;
/* Only mark this due date completed if there is another
* occurence. */
if ($next = $task->recurrence->nextActiveRecurrence($current)) {
$alarm = $task->toAlarm();
if ($alarm) {
$alarm['start'] = new Horde_Date($next);
$horde_alarm->set($alarm);
}
}
}
}
if ($alarm) {
$alarm['start'] = new Horde_Date($alarm['start']);
$horde_alarm->set($alarm);
}
}
/* Log the modification of this item in the history log. */
if (!empty($task->uid)) {
try {
$GLOBALS['injector']->getInstance('Horde_History')
->log('nag:' . $log_tasklist . ':' . $task->uid,
array('action' => 'modify'),
true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
}
/* Log completion status changes. */
if (isset($properties['completed']) &&
$task->completed != $properties['completed']) {
$attributes = array('action' => 'complete');
if (!$properties['completed']) {
$attributes['ts'] = 0;
}
try {
$GLOBALS['injector']->getInstance('Horde_History')
->log('nag:' . $log_tasklist . ':' . $task->uid,
$attributes,
true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
}
/* Notify users about the changed event. */
try {
$result = Nag::sendNotification('edit', $new_task, $task);
} catch (Nag_Exception $e) {
Horde::log($e, 'ERR');
}
}
/**
* @see modify()
*/
abstract protected function _modify($taskId, array $task);
/**
* Deletes a task and handles notification.
*
* @param string $taskId The task to delete.
*/
public function delete($taskId)
{
/* Get the task's details for use later. */
$task = $this->get($taskId);
$task->loadChildren();
if ($task->hasSubTasks()) {
throw new Nag_Exception(_("Sub tasks exist, delete them first"));
}
$delete = $this->_delete($taskId);
/* Remove tags */
$task->tags = array();
$this->_updateTags($task->toHash());
/* Tell content we removed the object */
$GLOBALS['injector']->getInstance('Content_Objects_Manager')
->delete(array($task->uid), 'task');
/* Log the deletion of this item in the history log. */
if (!empty($task->uid)) {
try {
$GLOBALS['injector']->getInstance('Horde_History')->log('nag:' . $this->_tasklist . ':' . $task->uid, array('action' => 'delete'), true);
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
}
/* Notify users about the deleted event. */
try {
$result = Nag::sendNotification('delete', $task);
} catch (Nag_Exception $e) {
Horde::log($e, 'ERR');
}
/* Delete alarm if necessary. */
if (!empty($task->alarm)) {
$GLOBALS['injector']->getInstance('Horde_Alarm')->delete($task->uid);
}
/* Remove any CalDAV mappings. */
try {
$davStorage = $GLOBALS['injector']
->getInstance('Horde_Dav_Storage');
try {
$davStorage
->deleteInternalObjectId($task->id, $task->tasklist);
} catch (Horde_Exception $e) {
Horde::log($e);
}
} catch (Horde_Exception $e) {
}
}
/**
* Deletes all tasks for the current task list.
*
* @throws Nag_Exception
*/
public function deleteAll()
{
$ids = $this->_deleteAll();
// Update History and Tagger
$history = $GLOBALS['injector']->getInstance('Horde_History');
try {
foreach ($ids as $uid) {
$history->log(
'nag:' . $this->_tasklist . ':' . $uid,
array('action' => 'delete'),
true);
$GLOBALS['injector']->getInstance('Nag_Tagger')
->replaceTags($uid, array(), $GLOBALS['registry']->getAuth(), 'task');
/* Tell content we removed the object */
$GLOBALS['injector']->getInstance('Content_Objects_Manager')
->delete(array($uid), 'task');
}
} catch (Exception $e) {
Horde::log($e, 'ERR');
}
}
/**
* Retrieves tasks from the database.
*
* @throws Nag_Exception
*/
public function retrieve()
{
throw new Nag_Exception($this->_errormsg);
}
/**
* Retrieves sub-tasks from the database.
*
* @param string $parentId The parent id for the sub-tasks to retrieve.
* @param boolean $include_history Include created/modified info?
*
* @return array List of sub-tasks.
* @throws Nag_Exception
*/
public function getChildren($parentId, $include_history = true)
{
throw new Nag_Exception($this->_errormsg);
}
/**
* Retrieves one task from the database.
*
* @param string $taskId The id of the task to retrieve.
*
* @return Nag_Task A Nag_Task object.
* @throws Nag_Exception
*/
public function get($taskId)
{
throw new Nag_Exception($this->_errormsg);
}
/**
* Retrieves one task from the database by UID.
*
* @param string $uid The UID of the task to retrieve.
*
* @return Nag_Task A Nag_Task object.
* @throws Nag_Exception
*/
public function getByUID($uid)
{
throw new Nag_Exception($this->_errormsg);
}
/**
* Helper function to update an existing event's tags to tagger storage.
*
* @param array $task The task to update
*/
protected function _updateTags(array $task)
{
$GLOBALS['injector']->getInstance('Nag_Tagger')->replaceTags(
$task['uid'],
$task['tags'],
$task['owner'],
'task'
);
}
/**
* Helper function to add tags from a newly created event to the tagger.
*
* @param array $task The task to save tags to storage for.
*/
protected function _addTags(array $task)
{
$GLOBALS['injector']->getInstance('Nag_Tagger')->tag(
$task['uid'],
$task['tags'],
$task['owner'],
'task'
);
}
}
nag-4.2.7/lib/Exception.php 0000664 0001750 0001750 00000000505 12654116077 013623 0 ustar jan jan
* @author Chuck Hagenbuch
* @author Jan Schneider
* @package Nag
*/
class Nag
{
/**
* Sort by task name.
*/
const SORT_NAME = 'name';
/**
* Sort by priority.
*/
const SORT_PRIORITY = 'priority';
/**
* Sort by due date.
*/
const SORT_DUE = 'due';
/**
* Sort by start date.
*/
const SORT_START = 'start';
/**
* Sort by completion.
*/
const SORT_COMPLETION = 'completed';
/**
* Sort by owner.
*/
const SORT_OWNER = 'tasklist';
/**
* Sort by estimate.
*/
const SORT_ESTIMATE = 'estimate';
/**
* Sort by assignee.
*/
const SORT_ASSIGNEE = 'assignee';
/**
* Sort in ascending order.
*/
const SORT_ASCEND = 0;
/**
* Sort in descending order.
*/
const SORT_DESCEND = 1;
/**
* Incomplete tasks
*/
const VIEW_INCOMPLETE = 0;
/**
* All tasks
*/
const VIEW_ALL = 1;
/**
* Complete tasks
*/
const VIEW_COMPLETE = 2;
/**
* Future tasks
*/
const VIEW_FUTURE = 3;
/**
* Future and incompleted tasks
*/
const VIEW_FUTURE_INCOMPLETE = 4;
/**
* WebDAV task list.
*/
const DAV_WEBDAV = 1;
/**
* CalDAV task list.
*/
const DAV_CALDAV = 2;
/**
* CalDAV principal.
*/
const DAV_ACCOUNT = 3;
/**
*
* @param integer $seconds
*
* @return string
*/
static public function secondsToString($seconds)
{
$hours = floor($seconds / 3600);
$minutes = ($seconds / 60) % 60;
if ($hours > 1) {
if ($minutes == 0) {
return sprintf(_("%d hours"), $hours);
} elseif ($minutes == 1) {
return sprintf(_("%d hours, %d minute"), $hours, $minutes);
} else {
return sprintf(_("%d hours, %d minutes"), $hours, $minutes);
}
} elseif ($hours == 1) {
if ($minutes == 0) {
return sprintf(_("%d hour"), $hours);
} elseif ($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");
} elseif ($minutes == 1) {
return sprintf(_("%d minute"), $minutes);
} else {
return sprintf(_("%d minutes"), $minutes);
}
}
}
/**
* Parses a complete date-time string into a Horde_Date object.
*
* @param string $date The date-time string to parse.
* @param boolean $withtime Whether time is included in the string.
*
* @return Horde_Date The parsed date.
* @throws Horde_Date_Exception
*/
static public function parseDate($date, $withtime = true)
{
// strptime() is not available on Windows.
if (!function_exists('strptime')) {
return new Horde_Date($date);
}
// strptime() is locale dependent, i.e. %p is not always matching
// AM/PM. Set the locale to C to workaround this, but grab the
// locale's D_FMT before that.
$format = Horde_Nls::getLangInfo(D_FMT);
if ($withtime) {
$format .= ' '
. ($GLOBALS['prefs']->getValue('twentyFour') ? '%H:%M' : '%I:%M %p');
}
$old_locale = setlocale(LC_TIME, 0);
setlocale(LC_TIME, 'C');
// Try exact format match first.
$date_arr = strptime($date, $format);
setlocale(LC_TIME, $old_locale);
if (!$date_arr) {
// Try with locale dependent parsing next.
$date_arr = strptime($date, $format);
if (!$date_arr) {
// Try throwing at Horde_Date finally.
return new Horde_Date($date);
}
}
return new Horde_Date(
array('year' => $date_arr['tm_year'] + 1900,
'month' => $date_arr['tm_mon'] + 1,
'mday' => $date_arr['tm_mday'],
'hour' => $date_arr['tm_hour'],
'min' => $date_arr['tm_min'],
'sec' => $date_arr['tm_sec']));
}
/**
* Retrieves the current user's task list from storage.
*
* This function will also sort the resulting list, if requested.
*
* @param arary $options Options array:
* - altsortby: (string) The secondary sort field. Same values as sortdir.
* DEFAULT: altsortby pref is used.
* - completed: (integer) Which task to retrieve. A Nag::VIEW_* constant.
* DEFAULT: show_completed pref is used.
* - external: (boolean) Whether to include tasks from other applications
* too.
* DEFAULT: true.
* - include_history: (boolean) Autoload created/modified data from
* Horde_History.
* DEFAULT: true (Automatically load history data).
* - include_tags: (boolean) Autoload all tags.
* DEFAULT: false (Tags are lazy loaded as needed.)
* - sortby: (string) A Nag::SORT_* constant for the field to sort by.
* DEFAULT: sortby pref is used.
* - sortdir: (string) Direction of sort. NAG::SORT_ASCEND or
* NAG::SORT_DESCEND.
* DEFAULT: sortdir pref is used.
* - tasklists: (array) An array of tasklists to include.
* DEFAULT: Use $GLOBALS['display_tasklists'];
*
* @return Nag_Task A list of the requested tasks.
*/
static public function listTasks(array $options = array())
{
global $prefs, $registry;
// Prevent null tasklists value from obscuring the default value.
if (array_key_exists('tasklists', $options) && empty($options['tasklists'])) {
unset($options['tasklists']);
}
$options = array_merge(
array(
'sortby' => $prefs->getValue('sortby'),
'sortdir' => $prefs->getValue('sortdir'),
'altsortby' => $prefs->getValue('altsortby'),
'tasklists' => $GLOBALS['display_tasklists'],
'completed' => $prefs->getValue('show_completed'),
'include_tags' => false,
'external' => true,
'include_history' => true
),
$options
);
if (!is_array($options['tasklists'])) {
$options['tasklists'] = array($options['tasklists']);
}
$tasks = new Nag_Task();
foreach ($options['tasklists'] as $tasklist) {
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($tasklist);
// Retrieve the tasklist from storage.
$storage->retrieve($options['completed'], $options['include_history']);
$tasks->mergeChildren($storage->tasks->children);
}
// Process all tasks.
$tasks->process();
if ($options['external'] &&
($apps = @unserialize($prefs->getValue('show_external'))) &&
is_array($apps)) {
foreach ($apps as $app) {
// We look for registered apis that support listAs(taskHash).
if ($app == 'nag' ||
!$registry->hasMethod('getListTypes', $app)) {
continue;
}
try {
$types = $registry->callByPackage($app, 'getListTypes');
} catch (Horde_Exception $e) {
continue;
}
if (empty($types['taskHash'])) {
continue;
}
try {
$newtasks = $registry->callByPackage($app, 'listAs', array('taskHash'));
foreach ($newtasks as $task) {
if (!isset($task['priority'])) {
$task['priority'] = 3;
}
$task['tasklist_id'] = '**EXTERNAL**';
$task['tasklist_name'] = $registry->get('name', $app);
$task = new Nag_Task(null, $task);
if (($options['completed'] == Nag::VIEW_INCOMPLETE &&
($task->completed ||
$task->start > $_SERVER['REQUEST_TIME'])) ||
($options['completed'] == Nag::VIEW_COMPLETE &&
!$task->completed) ||
($options['completed'] == Nag::VIEW_FUTURE &&
($task->completed ||
!$task->start ||
$task->start < $_SERVER['REQUEST_TIME'])) ||
($options['completed'] == Nag::VIEW_FUTURE_INCOMPLETE &&
$task->completed)) {
continue;
}
$tasks->add($task);
}
} catch (Horde_Exception $e) {
Horde::log($e);
}
}
}
// Sort the array.
$tasks->sort($options['sortby'], $options['sortdir'], $options['altsortby']);
// Preload tags if requested.
if ($options['include_tags']) {
$tasks->loadTags();
}
return $tasks;
}
/**
* Returns a single task.
*
* @param string $tasklist A tasklist.
* @param string $task A task id.
*
* @return Nag_Task The task hash.
*/
static public function getTask($tasklist, $task)
{
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($tasklist);
$task = $storage->get($task);
$task->process();
return $task;
}
/**
* Returns the number of taks in task lists that the current user owns.
*
* @return integer The number of tasks that the user owns.
*/
static public function countTasks()
{
static $count;
if (isset($count)) {
return $count;
}
$tasklists = self::listTasklists(true, Horde_Perms::ALL);
$count = 0;
foreach (array_keys($tasklists) as $tasklist) {
/* Create a Nag storage instance. */
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($tasklist);
$storage->retrieve();
/* Retrieve the task list from storage. */
$count += $storage->tasks->count();
}
return $count;
}
/**
* Imports one or more tasks parsed from a string.
*
* @param string $text The text to parse into
* @param string $tasklist The tasklist into which the task will be
* imported. If 'null', the user's default
* tasklist will be used.
*
* @return array The UIDs of all tasks that were added.
*/
static public function createTasksFromText($text, $tasklist = null)
{
if ($tasklist === null) {
$tasklist = self::getDefaultTasklist(Horde_Perms::EDIT);
} elseif (!self::hasPermission($tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($tasklist);
$dateParser = Horde_Date_Parser::factory(
array('locale' => $GLOBALS['prefs']->getValue('language')) );
$quickParser = new Nag_QuickParser();
$tasks = $quickParser->parse($text);
$uids = array();
foreach ($tasks as &$task) {
if (!is_array($task)) {
$name = $task;
$task = array($name);
}
$r = $dateParser->parse($task[0], array('return' => 'result'));
if ($d = $r->guess()) {
$name = $r->untaggedText();
$due = $d->timestamp();
} else {
$name = $task[0];
$due = 0;
}
// Look for tags to be added in the text.
$pattern = '/#\w+/';
$tags = array();
if (preg_match_all($pattern, $name, $results)) {
$tags = $results[0];
$name = str_replace($tags, '', $name);
$tags = array_map(function($x) { return substr($x, -(strlen($x) - 1)); }, $tags);
} else {
$tags = '';
}
if (isset($task['parent'])) {
$newTask = $storage->add(array('name' => $name, 'due' => $due, 'parent' => $tasks[$task['parent']]['id'], 'tags' => $tags));
} else {
$newTask = $storage->add(array('name' => $name, 'due' => $due, 'tags' => $tags));
}
$uids[] = $newTask[1];
$task['id'] = $newTask[0];
}
return $uids;
}
/**
* Returns all the alarms active right on $date.
*
* @param integer $date The unix epoch time to check for alarms.
* @param array $tasklists An array of tasklists
*
* @return array An array of Nag_Task objects with alarms active on $date.
*/
static public function listAlarms($date, array $tasklists = null)
{
if (is_null($tasklists)) {
$tasklists = $GLOBALS['display_tasklists'];
}
$tasks = array();
foreach ($tasklists as $tasklist) {
/* Create a Nag storage instance. */
$storage = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create($tasklist);
/* Retrieve the alarms for the task list. */
$newtasks = $storage->listAlarms($date);
/* 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;
}
/**
* Lists all task lists a user has access to.
*
* @param boolean $owneronly Only return task lists that this user owns?
* Defaults to false.
* @param integer $permission The permission to filter task lists by.
* @param boolean $smart Include SmartLists in the results.
*
* @return array The task lists.
*/
static public function listTasklists($owneronly = false,
$permission = Horde_Perms::SHOW,
$smart = true)
{
if ($owneronly && !$GLOBALS['registry']->getAuth()) {
return array();
}
$att = array();
if ($owneronly) {
$att = array('owner' => $GLOBALS['registry']->getAuth());
}
if (!$smart) {
$att['issmart'] = 0;
}
try {
$tasklists = $GLOBALS['nag_shares']->listShares(
$GLOBALS['registry']->getAuth(),
array('perm' => $permission,
'attributes' => $att,
'sort_by' => 'name'));
// Must explicitly add system shares if we are an admin since
// it's possible for an admin user to have no explicit perms
// added to the share.
if ($GLOBALS['registry']->isAdmin()) {
$tasklists = array_merge(
$tasklists,
$GLOBALS['nag_shares']->listSystemShares()
);
}
} catch (Horde_Share_Exception $e) {
Horde::log($e->getMessage(), 'ERR');
return array();
}
if ($owneronly) {
return $tasklists;
}
$display_tasklists = @unserialize($GLOBALS['prefs']->getValue('display_tasklists'));
if (is_array($display_tasklists)) {
foreach ($display_tasklists as $id) {
try {
$tasklist = $GLOBALS['nag_shares']->getShare($id);
if ($tasklist->hasPermission($GLOBALS['registry']->getAuth(), $permission)) {
$tasklists[$id] = $tasklist;
}
} catch (Horde_Exception_NotFound $e) {
} catch (Horde_Share_Exception $e) {
Horde::log($e);
return array();
}
}
}
return $tasklists;
}
/**
* Returns whether the current user has certain permissions on a tasklist.
*
* @param string $tasklist A tasklist id.
* @param integer $perm A Horde_Perms permission mask.
*
* @return boolean True if the current user has the requested permissions.
*/
static public function hasPermission($tasklist, $perm)
{
try {
$share = $GLOBALS['nag_shares']->getShare($tasklist);
if (!$share->hasPermission($GLOBALS['registry']->getAuth(), $perm)) {
throw new Horde_Exception_NotFound();
}
} catch (Horde_Exception_NotFound $e) {
return false;
}
return true;
}
/**
* Returns the default tasklist for the current user at the specified
* permissions level.
*
* @param integer $permission Horde_Perms constant for permission level
* required.
*
* @return string The default tasklist or null if none.
*/
static public function getDefaultTasklist($permission = Horde_Perms::SHOW)
{
$tasklists = self::listTasklists(false, $permission);
$default_tasklist = $GLOBALS['prefs']->getValue('default_tasklist');
if (isset($tasklists[$default_tasklist])) {
return $default_tasklist;
}
$default_tasklist = $GLOBALS['injector']
->getInstance('Nag_Factory_Tasklists')
->create()
->getDefaultShare();
if (!isset($tasklists[$default_tasklist])) {
reset($tasklists);
$default_tasklist = key($tasklists);
}
$GLOBALS['prefs']->setValue('default_tasklist', $default_tasklist);
return $default_tasklist;
}
/**
* Creates a new share.
*
* @param array $info Hash with tasklist information.
* @param boolean $display Add the new tasklist to display_tasklists
*
* @return Horde_Share The new share.
*/
static public function addTasklist(array $info, $display = true)
{
try {
$tasklist = $GLOBALS['nag_shares']->newShare(
$GLOBALS['registry']->getAuth(),
strval(new Horde_Support_Randomid()), $info['name']);
$tasklist->set('color', $info['color']);
$tasklist->set('desc', $info['description']);
if (!empty($info['system'])) {
$tasklist->set('owner', null);
}
// Smartlist
if (!empty($info['search'])) {
$tasklist->set('search', $info['search']);
$tasklist->set('issmart', 1);
}
$GLOBALS['nag_shares']->addShare($tasklist);
} catch (Horde_Share_Exception $e) {
throw new Nag_Exception($e);
}
if ($display) {
$GLOBALS['display_tasklists'][] = $tasklist->getName();
$GLOBALS['prefs']->setValue('display_tasklists', serialize($GLOBALS['display_tasklists']));
}
return $tasklist;
}
/**
* Updates an existing share.
*
* @param Horde_Share_Object $tasklist The share to update.
* @param array $info Hash with task list information.
*
* @throws Horde_Exception_PermissionDenied
* @throws Nag_Exception
*/
static public function updateTasklist(Horde_Share_Object $tasklist, array $info)
{
if (!$GLOBALS['registry']->getAuth() ||
($tasklist->get('owner') != $GLOBALS['registry']->getAuth() &&
(!is_null($tasklist->get('owner')) || !$GLOBALS['registry']->isAdmin()))) {
throw new Horde_Exception_PermissionDenied(_("You are not allowed to change this task list."));
}
$tasklist->set('name', $info['name']);
$tasklist->set('color', $info['color']);
$tasklist->set('desc', $info['description']);
$tasklist->set('owner', empty($info['system']) ? $GLOBALS['registry']->getAuth() : null);
if ($tasklist->get('issmart')) {
if (empty($info['search'])) {
throw new Nag_Exception(_("Missing valid search criteria"));
}
$tasklist->set('search', $info['search']);
}
try {
$tasklist->save();
} catch (Horde_Share_Exception $e) {
throw new Nag_Exception(sprintf(_("Unable to save task list \"%s\": %s"), $info['name'], $e->getMessage()));
}
}
/**
* Deletes a task list.
*
* @param Horde_Share_Object $tasklist The task list to delete.
*
* @throws Nag_Exception
* @throws Horde_Exception_PermissionDenied
*/
static public function deleteTasklist(Horde_Share_Object $tasklist)
{
if (!$GLOBALS['registry']->getAuth() ||
($tasklist->get('owner') != $GLOBALS['registry']->getAuth() &&
(!is_null($tasklist->get('owner')) || !$GLOBALS['registry']->isAdmin()))) {
throw new Horde_Exception_PermissionDenied(_("You are not allowed to delete this task list."));
}
// Delete the task list.
$storage = &$GLOBALS['injector']->getInstance('Nag_Factory_Driver')->create($tasklist->getName());
$result = $storage->deleteAll();
// Remove share and all groups/permissions.
try {
$GLOBALS['nag_shares']->removeShare($tasklist);
} catch (Horde_Share_Exception $e) {
throw new Nag_Exception($e);
}
}
/**
* Returns the label to be used for a task list.
*
* Attaches the owner name of shared task lists if necessary.
*
* @param Horde_Share_Object A task list.
*
* @return string The task list's label.
*/
public static function getLabel($tasklist)
{
$label = $tasklist->get('name');
if ($tasklist->get('owner') &&
$tasklist->get('owner') != $GLOBALS['registry']->getAuth()) {
$label .= ' [' . $GLOBALS['registry']->convertUsername($tasklist->get('owner'), false) . ']';
}
return $label;
}
/**
* Returns a DAV URL to be used for a task list.
*
* @param integer $type A Nag::DAV_* constant.
* @param Horde_Share_Object A task list.
*
* @return string The task list's URL.
* @throws Horde_Exception
*/
public static function getUrl($type, $tasklist)
{
global $conf, $injector, $registry;
$url = $registry->get('webroot', 'horde');
$rewrite = isset($conf['urls']['pretty']) &&
$conf['urls']['pretty'] == 'rewrite';
switch ($type) {
case Nag::DAV_WEBDAV:
if ($rewrite) {
$url .= '/rpc/nag/';
} else {
$url .= '/rpc.php/nag/';
}
$url = Horde::url($url, true, -1)
. ($tasklist->get('owner')
? $registry->convertUsername($tasklist->get('owner'), false)
: '-system-')
. '/' . $tasklist->getName() . '.ics';
break;
case Nag::DAV_CALDAV:
if ($rewrite) {
$url .= '/rpc/calendars/';
} else {
$url .= '/rpc.php/calendars/';
}
$url = Horde::url($url, true, -1)
. $registry->convertUsername($registry->getAuth(), false)
. '/'
. $injector->getInstance('Horde_Dav_Storage')
->getExternalCollectionId($tasklist->getName(), 'tasks')
. '/';
break;
case Nag::DAV_ACCOUNT:
if ($rewrite) {
$url .= '/rpc/';
} else {
$url .= '/rpc.php/';
}
$url = Horde::url($url, true, -1)
. 'principals/' . $registry->convertUsername($registry->getAuth(), false) . '/';
break;
}
return $url;
}
/**
* Returns a random CSS color.
*
* @return string A random CSS color string.
*/
static public function randomColor()
{
$color = '#';
for ($i = 0; $i < 3; $i++) {
$color .= sprintf('%02x', mt_rand(0, 255));
}
return $color;
}
/**
* Builds the HTML for a priority selection widget.
*
* @param string $name The name of the widget.
* @param integer $selected The default selected priority.
*
* @return string The HTML