⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.127
Server IP:
41.128.143.86
Server:
Linux host.raqmix.cloud 6.8.0-1025-azure #30~22.04.1-Ubuntu SMP Wed Mar 12 15:28:20 UTC 2025 x86_64
Server Software:
Apache
PHP Version:
8.3.23
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
proc
/
self
/
root
/
usr
/
share
/
psa-horde
/
nag
/
lib
/
Edit File: Api.php
<?php /** * Nag external API interface. * * This file defines Nag's external API interface. Other applications can * interact with Nag through this API. * * @package Nag */ class Nag_Api extends Horde_Registry_Api { /** * Links. * * @var array */ protected $_links = array( 'show' => '%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: * <pre> * text/calendar (VCALENDAR 2.0. Recommended as * this is specified in rfc2445) * text/x-vcalendar (old VCALENDAR 1.0 format. * Still in wide use) * </pre> * * @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; $task->uid = $uid; $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: * <pre> * '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. * </pre> */ 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; } }
Simpan