⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.50
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 :
~
/
usr
/
share
/
psa-horde
/
nag
/
lib
/
Edit File: Task.php
<?php /** * Nag_Task handles as single task as well as a list of tasks and implements a * recursive iterator to handle a (hierarchical) list of tasks. * * See the enclosed file COPYING for license information (GPL). If you * did not receive this file, see http://www.horde.org/licenses/gpl. * * @author Jan Schneider <jan@horde.org> * @package Nag * * @property tags array An array of tags this task is tagged with. */ class Nag_Task { /** * The task id. * * @var string */ public $id; /** * This task's tasklist id. * * @var string */ public $tasklist; /** * This task's tasklist name. * * Overrides the $tasklist's share name. * * @var string */ public $tasklist_name; /** * The task uid. * * @var string */ public $uid; /** * The task owner. * * @var string */ public $owner; /** * The task assignee. * * @var string */ public $assignee; /** * The task title. * * @var string */ public $name; /** * The task decription. * * @var string */ public $desc; /** * The start date timestamp. * * @var integer */ public $start; /** * The due date timestamp. * * @var integer */ public $due; /** * Recurrence rules for recurring tasks. * * @var Horde_Date_Recurrence */ public $recurrence; /** * The task priority from 1 = highest to 5 = lowest. * * @var integer */ public $priority; /** * The estimated task length. * * @var float */ public $estimate; /** * Whether the task is completed. * * @var boolean */ public $completed; /** * The completion date timestamp. * * @var integer */ public $completed_date; /** * The creation time. * * @var Horde_Date */ public $created; /** * The creator string. * * @var string */ public $createdby; /** * The last modification time. * * @var Horde_Date */ public $modified; /** * The last-modifier string. * * @var string */ public $modifiedby; /** * The task alarm threshold in minutes. * * @var integer */ public $alarm; /** * The particular alarm methods overridden for this task. * * @var array */ public $methods; /** * Whether the task is private. * * @var boolean */ public $private; /** * URL to view the task. * * @var string */ public $view_link; /** * URL to complete the task. * * @var string */ public $complete_link; /** * URL to edit the task. * * @var string */ public $edit_link; /** * URL to delete the task. * * @var string */ public $delete_link; /** * The parent task's id. * * @var string */ public $parent_id = ''; /** * The parent task. * * @var Nag_Task */ public $parent; /** * The sub-tasks. * * @var array */ public $children = array(); /** * This task's idention (child) level. * * @var integer */ public $indent = 0; /** * Whether this is the last sub-task. * * @var boolean */ public $lastChild; /** * A storage driver. * * @var Nag_Driver */ protected $_storage; /** * Internal flag. * * @var boolean * @see each() */ protected $_inlist = false; /** * Internal pointer. * * @var integer * @see each() */ protected $_pointer = 0; /** * Task id => pointer dictionary. * * @var array */ protected $_dict = array(); /** * Task tags from the storage backend (e.g. Kolab) * * @var array */ public $internaltags; /** * Task tags (lazy loaded). * * @var string */ protected $_tags; /** * Constructor. * * Takes a hash and returns a nice wrapper around it. * * @param Nag_Driver $storage A storage driver. * @param array $task A task hash. */ public function __construct(Nag_Driver $storage = null, array $task = null) { if ($storage) { $this->_storage = $storage; } if ($task) { $this->merge($task); } } /** * Getter. * * Returns the 'id' and 'creator' properties. * * @param string $name Property name. * * @return mixed Property value. */ public function __get($name) { switch ($name) { case 'tags': if (!isset($this->_tags)) { $this->synchronizeTags($GLOBALS['injector']->getInstance('Nag_Tagger')->getTags($this->uid, 'task')); } return $this->_tags; } $trace = debug_backtrace(); trigger_error('Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Setter. * * @param string $name Property name. * @param mixed $value Property value. */ public function __set($name, $value) { switch ($name) { case 'tags': $this->_tags = $value; return; } $trace = debug_backtrace(); trigger_error('Undefined property via __set(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); } /** * Deep clone so we can clone the child objects too. * */ public function __clone() { foreach ($this->children as $key => $value) { $this->children[$key] = clone $value; } } /** * Merges a task hash into this task object. * * @param array $task A task hash. */ public function merge(array $task) { foreach ($task as $key => $val) { switch ($key) { case 'tasklist_id': $key = 'tasklist'; break; case 'task_id': $key = 'id'; break; case 'parent': $key = 'parent_id'; break; } $this->$key = $val; } } /** * Disconnect this task from any child tasks. Used when building search * result sets since child tasks will be re-added if they actually match * the result, and there is no guarentee that a tasks's parent will * be present in the result set. */ public function orphan() { $this->children = array(); $this->_dict = array(); $this->lastChild = null; $this->indent = null; } /** * Saves this task in the storage backend. * * @throws Nag_Exception */ public function save() { $this->_storage->modify($this->id, $this->toHash(true)); } /** * Returns the parent task of this task, if one exists. * * @return mixed The parent task, null if none exists */ public function getParent() { if (!$this->parent_id) { return null; } return Nag::getTask($this->tasklist, $this->parent_id); } /** * Adds a sub task to this task. * * @param Nag_Task $task A sub task. */ public function add(Nag_Task $task, $replace = false) { if (!isset($this->_dict[$task->id])) { $this->_dict[$task->id] = count($this->children); $task->parent = $this; $this->children[] = $task; } elseif ($replace) { $this->children[$this->_dict[$task->id]]= $task; } } /** * Loads all sub-tasks. * * @param */ public function loadChildren($include_history = true) { try { $this->children = $this->_storage->getChildren($this->id, $include_history); } catch (Nag_Exception $e) {} } /** * Merges an array of tasks into this task's children. * * @param array $children A list of Nag_Tasks. * */ public function mergeChildren(array $children) { for ($i = 0, $c = count($children); $i < $c; ++$i) { $this->add($children[$i]); } } /** * Returns a sub task by its id. * * The methods goes recursively through all sub tasks until it finds the * searched task. * * @param string $key A task id. * * @return Nag_Task The searched task or null. */ public function get($key) { return isset($this->_dict[$key]) ? $this->children[$this->_dict[$key]] : null; } /** * Returns whether this is a task (not a container) or contains any sub * tasks. * * @return boolean True if this is a task or has sub tasks. */ public function hasTasks() { return ($this->id) ? true : $this->hasSubTasks(); } /** * Returns whether this task contains any sub tasks. * * @return boolean True if this task has sub tasks. */ public function hasSubTasks() { foreach ($this->children as $task) { if ($task->hasTasks()) { return true; } } return false; } /** * Returns whether all sub tasks are completed. * * @return boolean True if all sub tasks are completed. */ public function childrenCompleted() { foreach ($this->children as $task) { if (!$task->completed || !$task->childrenCompleted()) { return false; } } return true; } /** * Returns whether any tasks in the list are overdue. * * @return boolean True if any task or sub tasks are overdue. */ public function childrenOverdue() { if (!empty($this->due)) { $due = new Horde_Date($this->due); if ($due->compareDate(new Horde_Date(time())) <= 0) { return true; } } foreach ($this->children as $task) { if ($task->childrenOverdue()) { return true; } } return false; } /** * Returns the number of tasks including this and any sub tasks. * * @return integer The number of tasks and sub tasks. */ public function count() { $count = $this->id ? 1 : 0; foreach ($this->children as $task) { $count += $task->count(); } return $count; } /** * Returns the estimated length for this and any sub tasks. * * @return integer The estimated length sum. */ public function estimation() { $estimate = $this->estimate; foreach ($this->children as $task) { $estimate += $task->estimation(); } return $estimate; } /** * Returns whether this task is a recurring task. * * @return boolean True if this is a recurring task. */ public function recurs() { return isset($this->recurrence) && !$this->recurrence->hasRecurType(Horde_Date_Recurrence::RECUR_NONE); } /** * Toggles completion status of this task. Moves a recurring task * to the next occurence on completion. Enforces the rule that sub * tasks must be completed before parent tasks. */ public function toggleComplete($ignore_children = false) { if ($ignore_children) { $this->loadChildren(); if (!$this->completed && !$this->childrenCompleted()) { throw new Nag_Exception(_("Must complete all children tasks.")); } } if ($this->completed) { $this->completed_date = null; $this->completed = false; if ($parent = $this->getParent()) { if ($parent->completed) { $parent->toggleComplete(true); $parent->save(); } } if ($this->recurs()) { /* Only delete the latest completion. */ $completions = $this->recurrence->getCompletions(); sort($completions); list($year, $month, $mday) = sscanf( end($completions), '%04d%02d%02d' ); $this->recurrence->deleteCompletion($year, $month, $mday); } return; } if ($this->recurs()) { /* Get current occurrence (task due date) */ $current = $this->recurrence->nextActiveRecurrence(new Horde_Date($this->due)); if ($current) { $this->recurrence->addCompletion($current->year, $current->month, $current->mday); /* 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 = $this->recurrence->nextActiveRecurrence($current)) { $this->completed = false; return; } } } $this->completed_date = time(); $this->completed = true; } /** * Returns the next start date of this task. * * Takes recurring tasks into account. * * @return Horde_Date The next start date. */ public function getNextStart() { if (!$this->start) { return null; } if (!$this->recurs() || !($completions = $this->recurrence->getCompletions())) { return new Horde_Date($this->start); } sort($completions); list($year, $month, $mday) = sscanf( end($completions), '%04d%02d%02d' ); $lastCompletion = new Horde_Date($year, $month, $mday); $recurrence = clone $this->recurrence; $recurrence->start = new Horde_Date($this->start); return $recurrence->nextRecurrence($lastCompletion); } /** * Returns the next due date of this task. * * Takes recurring tasks into account. * * @return Horde_Date|null The next due date or null if no due date. */ public function getNextDue() { if (!$this->due) { return null; } if (!$this->recurs()) { return new Horde_Date($this->due); } if (!($nextActive = $this->recurrence->nextActiveRecurrence($this->due))) { return null; } return $nextActive; } /** * Format the description - link URLs, etc. * * @return string */ public function getFormattedDescription() { $desc = $GLOBALS['injector'] ->getInstance('Horde_Core_Factory_TextFilter') ->filter($this->desc, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)); try { return Horde::callHook('format_description', array($desc), 'nag'); } catch (Horde_Exception_HookNotSet $e) { return $desc; } } /** * Resets the tasks iterator. * * Call this each time before looping through the tasks. * * @see each() */ public function reset() { foreach (array_keys($this->children) as $key) { $this->children[$key]->reset(); } $this->_pointer = 0; $this->_inlist = false; } /** * Return the task, if present anywhere in this tasklist, regardless of * child depth. * * @param string $taskId The task id we are looking for. * * @return Nag_Task|false The task object, if found. Otherwise false. */ public function hasTask($taskId) { $this->reset(); while ($task = $this->each()) { if ($task->id == $taskId) { return $task; } } return false; } /** * Returns the next task iterating through all tasks and sub tasks. * * Call reset() each time before looping through the tasks: * <code> * $tasks->reset(); * while ($task = $tasks->each() { * ... * } * * @see reset() */ public function each() { if ($this->id && !$this->_inlist) { $this->_inlist = true; return $this; } if ($this->_pointer >= count($this->children)) { return false; } $next = $this->children[$this->_pointer]->each(); if ($next) { return $next; } $this->_pointer++; return $this->each(); } /** * Helper method for getting only a slice of the total tasks in this list. * * @param integer $page The starting page. * @param integer $perpage The count of tasks per page. * * @return Nag_Task The resulting task list. */ public function getSlice($page = 0, $perpage = null) { $this->reset(); // Position at start task $start = $page * (empty($perpage) ? 0 : $perpage); $count = 0; while ($count < $start) { if (!$this->each()) { return new Nag_Task(); } ++$count; } $count = 0; $results = new Nag_Task(); $max = (empty($perpage) ? ($this->count() - $start) : $perpage); while ($count < $max) { if ($next = $this->each()) { $results->add($next); ++$count; } else { $count = $max; } } $results->process(); return $results; } /** * Processes a list of tasks by adding action links, obscuring details of * private tasks and calculating indentation. * * @param integer $indent The indention level of the tasks. */ public function process($indent = null) { global $conf; /* Link cache. */ static $view_url_list, $task_url_list; /* Set indention. */ if (is_null($indent)) { $indent = 0; if ($parent = $this->getParent()) { $indent = $parent->indent + 1; } } $this->indent = $indent; if ($this->id) { $indent++; } /* Process children. */ for ($i = 0, $c = count($this->children); $i < $c; ++$i) { $this->children[$i]->process($indent); } /* Mark last child. */ if (count($this->children)) { $this->children[count($this->children) - 1]->lastChild = true; } /* Only process further if this is really a (parent) task, not only a * task list container. */ if (!$this->id) { return; } if (!isset($view_url_list[$this->tasklist])) { $view_url_list[$this->tasklist] = Horde::url('view.php')->add('tasklist', $this->tasklist); $task_url_list[$this->tasklist] = Horde::url('task.php')->add('tasklist', $this->tasklist); } /* Obscure private tasks. */ if ($this->private && $this->owner != $GLOBALS['registry']->getAuth()) { $this->name = _("Private Task"); $this->desc = ''; } /* Create task links. */ $this->view_link = $view_url_list[$this->tasklist]->copy()->add('task', $this->id); $task_url_task = $task_url_list[$this->tasklist]->copy()->add('task', $this->id); $this->complete_link = Horde::url( $conf['urls']['pretty'] == 'rewrite' ? 't/complete' : 'task/complete.php' )->add(array( 'url' => Horde::signUrl(Horde::url('list.php')), 'task' => $this->id, 'tasklist' => $this->tasklist )); $this->edit_link = $task_url_task->copy()->add('actionID', 'modify_task'); $this->delete_link = $task_url_task->copy()->add('actionID', 'delete_task'); } /** * Returns the HTML code for any tree icons, when displaying this task in * a tree view. * * @return string The HTML code for necessary tree icons. */ public function treeIcons() { $html = ''; $parent = $this->parent; for ($i = 1; $i < $this->indent; ++$i) { if ($parent && $parent->lastChild) { $html = Horde::img('tree/blank.png') . $html; } else { $html = Horde::img('tree/line.png', '|') . $html; } $parent = $parent->parent; } if ($this->indent) { if ($this->lastChild) { $html .= Horde::img($GLOBALS['registry']->nlsconfig->curr_rtl ? 'tree/rev-joinbottom.png' : 'tree/joinbottom.png', '\\'); } else { $html .= Horde::img($GLOBALS['registry']->nlsconfig->curr_rtl ? 'tree/rev-join.png' : 'tree/join.png', '+'); } } return $html; } /** * Recursively loads tags for all tasks contained in this object. */ public function loadTags() { $ids = array(); if (!isset($this->_tags)) { $ids[] = $this->uid; } foreach ($this->children as $task) { $ids[] = $task->uid; } if (!$ids) { return; } $results = $GLOBALS['injector']->getInstance('Nag_Tagger')->getTags($ids); if (isset($results[$this->uid])) { $this->synchronizeTags($results[$this->uid]); } foreach ($this->children as $task) { if (isset($results[$task->uid])) { $task->synchronizeTags($results[$task->uid]); $task->loadTags(); } } } /** * Syncronizes tags from the tagging backend with the task storage backend, * if necessary. * * @param array $tags Tags from the tagging backend. */ public function synchronizeTags(array $tags) { if (isset($this->internaltags)) { usort($tags, 'strcoll'); if (array_diff($this->internaltags, $tags)) { $GLOBALS['injector']->getInstance('Nag_Tagger')->replaceTags( $this->uid, $this->internaltags, $this->owner, 'task' ); } $this->_tags = implode(',', $this->internaltags); } else { $this->_tags = $tags; } } /** * Sorts sub tasks by the given criteria. * * @param string $sortby The field by which to sort * (Nag::SORT_PRIORITY, Nag::SORT_NAME * Nag::SORT_DUE, Nag::SORT_COMPLETION). * @param integer $sortdir The direction by which to sort * (Nag::SORT_ASCEND, Nag::SORT_DESCEND). * @param string $altsortby The secondary sort field. */ public function sort($sortby, $sortdir, $altsortby) { /* Sorting criteria for the task list. */ $sort_functions = array( Nag::SORT_PRIORITY => 'ByPriority', Nag::SORT_NAME => 'ByName', Nag::SORT_DUE => 'ByDue', Nag::SORT_START => 'ByStart', Nag::SORT_COMPLETION => 'ByCompletion', Nag::SORT_ASSIGNEE => 'ByAssignee', Nag::SORT_ESTIMATE => 'ByEstimate', Nag::SORT_OWNER => 'ByOwner' ); /* Sort the array if we have a sort function defined for this * field. */ if (isset($sort_functions[$sortby])) { $prefix = ($sortdir == Nag::SORT_DESCEND) ? '_rsort' : '_sort'; usort($this->children, array('Nag', $prefix . $sort_functions[$sortby])); if (isset($sort_functions[$altsortby]) && $altsortby !== $sortby) { $task_buckets = array(); for ($i = 0, $c = count($this->children); $i < $c; ++$i) { if (!isset($task_buckets[$this->children[$i]->$sortby])) { $task_buckets[$this->children[$i]->$sortby] = array(); } $task_buckets[$this->children[$i]->$sortby][] = $this->children[$i]; } $tasks = array(); foreach ($task_buckets as $task_bucket) { usort($task_bucket, array('Nag', $prefix . $sort_functions[$altsortby])); $tasks = array_merge($tasks, $task_bucket); } $this->children = $tasks; } /* Mark last child. */ for ($i = 0, $c = count($this->children); $i < $c; ++$i) { $this->children[$i]->lastChild = false; } if (count($this->children)) { $this->children[count($this->children) - 1]->lastChild = true; } for ($i = 0, $c = count($this->children); $i < $c; ++$i) { $this->_dict[$this->children[$i]->id] = $i; $this->children[$i]->sort($sortby, $sortdir, $altsortby); } } } /** * Returns a hash representation for this task. * * @return array A task hash. */ public function toHash() { $hash = array( 'tasklist_id' => $this->tasklist, 'task_id' => $this->id, 'uid' => $this->uid, 'parent' => $this->parent_id, 'owner' => $this->owner, 'assignee' => $this->assignee, 'name' => $this->name, 'desc' => $this->desc, 'start' => $this->start, 'due' => $this->due, 'priority' => $this->priority, 'estimate' => $this->estimate, 'completed' => $this->completed, 'completed_date' => $this->completed_date, 'alarm' => $this->alarm, 'methods' => $this->methods, 'private' => $this->private, 'recurrence' => $this->recurrence, 'tags' => $this->tags); return $hash; } /** * Returns a simple object suitable for json transport representing this * task. * * @param boolean $full Whether to return all task details. * @param string $time_format The date() format to use for time formatting. * * @return object A simple object. */ public function toJson($full = false, $time_format = 'H:i') { $json = new stdClass; $json->l = $this->tasklist; $json->p = $this->parent_id; $json->i = $this->indent; $json->n = $this->name; if ($this->desc) { //TODO: Get the proper amount of characters, and cut by last //whitespace $json->sd = Horde_String::substr($this->desc, 0, 80); } $json->cp = (boolean)$this->completed; if ($this->due && ($due = $this->getNextDue())) { $json->du = $due->toJson(); } if ($this->start && ($start = $this->getNextStart())) { $json->s = $start->toJson(); } $json->pr = (int)$this->priority; if ($this->recurs()) { $json->r = $this->recurrence->getRecurType(); } $json->t = array_values($this->tags); if ($full) { // @todo: do we really need all this? $json->id = $this->id; $json->de = $this->desc; if ($this->due) { $date = new Horde_Date($this->due); $json->dd = $date->strftime('%x'); $json->dt = $date->format($time_format); } $json->as = $this->assignee; if ($this->estimate) { $json->e = $this->estimate; } /* $json->o = $this->owner; if ($this->completed_date) { $date = new Horde_Date($this->completed_date); $json->cd = $date->toJson(); } */ $json->a = (int)$this->alarm; $json->m = $this->methods; //$json->pv = (boolean)$this->private; if ($this->recurs()) { $json->r = $this->recurrence->toJson(); } if ($this->tasklist == '**EXTERNAL**') { $json->vl = (string)$this->view_link; $json->cl = (string)$this->complete_link; $json->pe = $json->pd = false; } else { try { $share = $GLOBALS['nag_shares']->getShare($this->tasklist); } catch (Horde_Share_Exception $e) { Horde::log($e->getMessage(), 'ERR'); throw new Nag_Exception($e); } $json->pe = $share->hasPermission( $GLOBALS['registry']->getAuth(), Horde_Perms::EDIT ); $json->pd = $share->hasPermission( $GLOBALS['registry']->getAuth(), Horde_Perms::DELETE ); } } return $json; } /** * Returns an alarm hash of this task suitable for Horde_Alarm. * * @param string $user The user to return alarms for. * @param Prefs $prefs A Prefs instance. * * @return array Alarm hash or null. */ public function toAlarm($user = null, $prefs = null) { if (empty($this->alarm) || $this->completed) { return; } if (empty($user)) { $user = $GLOBALS['registry']->getAuth(); } if (empty($prefs)) { $prefs = $GLOBALS['prefs']; } $methods = !empty($this->methods) ? $this->methods : @unserialize($prefs->getValue('task_alarms')); if (!$methods) { $methods = array(); } if (isset($methods['notify'])) { $methods['notify']['show'] = array( '__app' => $GLOBALS['registry']->getApp(), 'task' => $this->id, 'tasklist' => $this->tasklist); $methods['notify']['ajax'] = 'task:' . $this->tasklist . ':' . $this->id; if (!empty($methods['notify']['sound'])) { if ($methods['notify']['sound'] == 'on') { // Handle boolean sound preferences; $methods['notify']['sound'] = (string)Horde_Themes::sound('theetone.wav'); } else { // Else we know we have a sound name that can be // served from Horde. $methods['notify']['sound'] = (string)Horde_Themes::sound($methods['notify']['sound']); } } } if (isset($methods['mail'])) { $image = Nag::getImagePart('big_alarm.png'); $view = new Horde_View(array('templatePath' => NAG_TEMPLATES . '/alarm', 'encoding' => 'UTF-8')); new Horde_View_Helper_Text($view); $view->task = $this; $view->imageId = $image->getContentId(); $view->due = new Horde_Date($this->due); $view->dateFormat = $prefs->getValue('date_format'); $view->timeFormat = $prefs->getValue('twentyFour') ? 'H:i' : 'h:ia'; if (!$prefs->isLocked('task_alarms')) { $view->prefsUrl = Horde::url($GLOBALS['registry']->getServiceLink('prefs', 'nag'), true)->remove(session_name()); } $methods['mail']['mimepart'] = Nag::buildMimeMessage($view, 'mail', $image); } if (isset($methods['desktop'])) { $methods['desktop']['url'] = Horde::url('view.php', true)->add('tasklist', $this->tasklist)->add('task', $this->id)->toString(true, true); } return array( 'id' => $this->uid, 'user' => $user, 'start' => new Horde_Date($this->due - $this->alarm * 60), 'methods' => array_keys($methods), 'params' => $methods, 'title' => $this->name, 'text' => $this->desc); } /** * Exports this task in iCalendar format. * * @param Horde_Icalendar $calendar A Horde_Icalendar object that acts as * the container. * * @return Horde_Icalendar_Vtodo A vtodo component of this task. */ public function toiCalendar(Horde_Icalendar $calendar) { $vTodo = Horde_Icalendar::newComponent('vtodo', $calendar); $v1 = $calendar->getAttribute('VERSION') == '1.0'; $vTodo->setAttribute('UID', $this->uid); if (!empty($this->assignee)) { $vTodo->setAttribute('ORGANIZER', $this->assignee); } if (!empty($this->name)) { $vTodo->setAttribute('SUMMARY', $this->name); } if (!empty($this->desc)) { $vTodo->setAttribute('DESCRIPTION', $this->desc); } if (isset($this->priority)) { $priorityMap = array( 0 => 5, 1 => 1, 2 => 3, 3 => 5, 4 => 7, 5 => 9, ); $vTodo->setAttribute('PRIORITY', $priorityMap[$this->priority]); } if (!empty($this->parent_id) && !empty($this->parent)) { $vTodo->setAttribute('RELATED-TO', $this->parent->uid); } if ($this->private) { $vTodo->setAttribute('CLASS', 'PRIVATE'); } if (!empty($this->start)) { $vTodo->setAttribute('DTSTART', $this->start); } if ($this->due) { $vTodo->setAttribute('DUE', $this->due); if ($this->alarm) { if ($v1) { $vTodo->setAttribute('AALARM', $this->due - $this->alarm * 60); } else { $vAlarm = Horde_Icalendar::newComponent('valarm', $vTodo); $vAlarm->setAttribute('ACTION', 'DISPLAY'); $vAlarm->setAttribute('DESCRIPTION', $this->name); $vAlarm->setAttribute('TRIGGER;VALUE=DURATION', '-PT' . $this->alarm . 'M'); $vTodo->addComponent($vAlarm); } $hordeAlarm = $GLOBALS['injector']->getInstance('Horde_Alarm'); if ($hordeAlarm->exists($this->uid, $GLOBALS['registry']->getAuth()) && $hordeAlarm->isSnoozed($this->uid, $GLOBALS['registry']->getAuth())) { $vTodo->setAttribute('X-MOZ-LASTACK', new Horde_Date($_SERVER['REQUEST_TIME'])); $alarm = $hordeAlarm->get($this->uid, $GLOBALS['registry']->getAuth()); if (!empty($alarm['snooze'])) { $alarm['snooze']->setTimezone(date_default_timezone_get()); $vTodo->setAttribute('X-MOZ-SNOOZE-TIME', $alarm['snooze']); } } } } if ($this->completed) { $vTodo->setAttribute('STATUS', 'COMPLETED'); $vTodo->setAttribute('COMPLETED', $this->completed_date ? $this->completed_date : $_SERVER['REQUEST_TIME']); } else { if ($v1) { $vTodo->setAttribute('STATUS', 'NEEDS ACTION'); } else { $vTodo->setAttribute('STATUS', 'NEEDS-ACTION'); } } if (!empty($this->estimate)) { $vTodo->setAttribute('X-HORDE-ESTIMATE', $this->estimate); } if ($this->tags) { $vTodo->setAttribute('CATEGORIES', '', array(), true, array_values($this->tags)); } /* Get the task's history. */ $created = $modified = null; try { $log = $GLOBALS['injector']->getInstance('Horde_History')->getHistory('nag:' . $this->tasklist . ':' . $this->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($created)) { $vTodo->setAttribute($v1 ? 'DCREATED' : 'CREATED', $created); if (empty($modified)) { $modified = $created; } } if (!empty($modified)) { $vTodo->setAttribute('LAST-MODIFIED', $modified); } return $vTodo; } /** * Create an AS message from this task * * @param array $options Options: * - protocolversion: (float) The EAS version to support * DEFAULT: 2.5 * - bodyprefs: (array) A BODYPREFERENCE array. * DEFAULT: none (No body prefs enforced). * - truncation: (integer) Truncate event body to this length * DEFAULT: none (No truncation). * * @return Horde_ActiveSync_Message_Task */ public function toASTask(array $options = array()) { $message = new Horde_ActiveSync_Message_Task(array( 'protocolversion' => $options['protocolversion']) ); /* Notes and Title */ if ($options['protocolversion'] >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($this->desc)) { $bp = $options['bodyprefs']; $body = new Horde_ActiveSync_Message_AirSyncBaseBody(); $body->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) { $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']; } elseif (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_HTML])) { $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize']; $this->desc = Horde_Text_Filter::filter($this->desc, 'Text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)); } else { $truncation = false; } if ($truncation && Horde_String::length($this->desc) > $truncation) { $body->data = Horde_String::substr($this->desc, 0, $truncation); $body->truncated = 1; } else { $body->data = $this->desc; } $body->estimateddatasize = Horde_String::length($this->desc); $message->airsyncbasebody = $body; } } else { $message->body = $this->desc; } $message->subject = $this->name; /* Completion */ if ($this->completed) { if ($this->completed_date) { $message->datecompleted = new Horde_Date($this->completed_date); } $message->complete = Horde_ActiveSync_Message_Task::TASK_COMPLETE_TRUE; } else { $message->complete = Horde_ActiveSync_Message_Task::TASK_COMPLETE_FALSE; } /* Due Date */ if (!empty($this->due)) { if ($this->due) { $message->utcduedate = new Horde_Date($this->getNextDue()); } $message->duedate = clone($message->utcduedate); } /* Start Date */ if (!empty($this->start)) { if ($this->start) { $message->utcstartdate = new Horde_Date($this->start); } $message->startdate = clone($message->utcstartdate); } /* Priority */ switch ($this->priority) { case 5: $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_LOW; break; case 4: case 3: case 2: $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL; break; case 1: $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_HIGH; break; default: $priority = Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL; } $message->setImportance($priority); /* Reminders */ if ($this->due && $this->alarm) { $message->setReminder(new Horde_Date($this->due - $this->alarm * 60)); } /* Recurrence */ if ($this->recurs()) { $message->setRecurrence($this->recurrence); } /* Categories */ $message->categories = $this->tags; return $message; } /** * Creates a task from a Horde_Icalendar_Vtodo object. * * @param Horde_Icalendar_Vtodo $vTodo The iCalendar data to update from. */ public function fromiCalendar(Horde_Icalendar_Vtodo $vTodo) { /* Owner is always current user. */ $this->owner = $GLOBALS['registry']->getAuth(); try { $name = $vTodo->getAttribute('SUMMARY'); if (!is_array($name)) { $this->name = $name; } } catch (Horde_Icalendar_Exception $e) { } try { $assignee = $vTodo->getAttribute('ORGANIZER'); if (!is_array($assignee)) { $this->assignee = $assignee; } } catch (Horde_Icalendar_Exception $e) { } try { $uid = $vTodo->getAttribute('UID'); if (!is_array($uid)) { $this->uid = $uid; } } catch (Horde_Icalendar_Exception $e) { } try { $relations = $vTodo->getAttribute('RELATED-TO'); if (!is_array($relations)) { $relations = array($relations); } $params = $vTodo->getAttribute('RELATED-TO', true); foreach ($relations as $id => $relation) { if (empty($params[$id]['RELTYPE']) || Horde_String::upper($params[$id]['RELTYPE']) == 'PARENT') { try { $parent = $this->_storage->getByUID($relation); $this->parent_id = $parent->id; } catch (Horde_Exception_NotFound $e) { } break; } } } catch (Horde_Icalendar_Exception $e) { } try { $start = $vTodo->getAttribute('DTSTART'); if (!is_array($start)) { // Date-Time field $this->start = $start; } else { // Date field $this->start = mktime(0, 0, 0, (int)$start['month'], (int)$start['mday'], (int)$start['year']); } } catch (Horde_Icalendar_Exception $e) { } try { $due = $vTodo->getAttribute('DUE'); if (is_array($due)) { $this->due = mktime(0, 0, 0, (int)$due['month'], (int)$due['mday'], (int)$due['year']); } elseif (!empty($due)) { $this->due = $due; } } catch (Horde_Icalendar_Exception $e) { } // vCalendar 1.0 alarms try { $alarm = $vTodo->getAttribute('AALARM'); if (!is_array($alarm) && !empty($alarm) && !empty($this->due)) { $this->alarm = intval(($this->due - $alarm) / 60); if ($this->alarm === 0) { // We don't support alarms exactly at due date. $this->alarm = 1; } } } catch (Horde_Icalendar_Exception $e) { } // @TODO: vCalendar 2.0 alarms try { $desc = $vTodo->getAttribute('DESCRIPTION'); if (!is_array($desc)) { $this->desc = $desc; } } catch (Horde_Icalendar_Exception $e) { } try { $priority = $vTodo->getAttribute('PRIORITY'); if (!is_array($priority)) { $priorityMap = array( 0 => 3, 1 => 1, 2 => 1, 3 => 2, 4 => 2, 5 => 3, 6 => 4, 7 => 4, 8 => 5, 9 => 5, ); $this->priority = isset($priorityMap[$priority]) ? $priorityMap[$priority] : 3; } } catch (Horde_Icalendar_Exception $e) { } try { $cat = $vTodo->getAttribute('CATEGORIES'); if (!is_array($cat)) { $this->tags = $cat; } } catch (Horde_Icalendar_Exception $e) { } try { $status = $vTodo->getAttribute('STATUS'); if (!is_array($status)) { $this->completed = !strcasecmp($status, 'COMPLETED'); } } catch (Horde_Icalendar_Exception $e) { } try { $class = $vTodo->getAttribute('CLASS'); if (!is_array($class)) { $class = Horde_String::upper($class); $this->private = $class == 'PRIVATE' || $class == 'CONFIDENTIAL'; } } catch (Horde_Icalendar_Exception $e) { } try { $estimate = $vTodo->getAttribute('X-HORDE-ESTIMATE'); if (!is_array($estimate)) { $this->estimate = $estimate; } } catch (Horde_Icalendar_Exception $e) { } } /** * Create a nag Task object from an activesync message * * @param Horde_ActiveSync_Message_Task $message The task object */ public function fromASTask(Horde_ActiveSync_Message_Task $message) { /* Owner is always current user. */ $this->owner = $GLOBALS['registry']->getAuth(); /* Must set _tags so we don't lazy load tags from the backend in the * case that this is an edit. For edits, all current tags will be passed * from the client. */ $this->_tags = array(); /* Notes and Title */ if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_TWELVE) { if ($message->airsyncbasebody->type == Horde_ActiveSync::BODYPREF_TYPE_HTML) { $this->desc = Horde_Text_Filter::filter($message->airsyncbasebody->data, 'Html2text'); } else { $this->desc = $message->airsyncbasebody->data; } } else { $this->desc = $message->body; } $this->name = $message->subject; $tz = date_default_timezone_get(); /* Completion: Note we don't use self::toggleCompletion() becuase of * the way that EAS hanldes recurring tasks (see below). */ if ($this->completed = $message->complete) { if ($message->datecompleted) { $message->datecompleted->setTimezone($tz); $this->completed_date = $message->datecompleted->timestamp(); } else { $this->completed_date = null; } } /* Due Date */ if ($due = $message->utcduedate) { $due->setTimezone($tz); $this->due = $due->timestamp(); } elseif ($due = $message->duedate) { // "Local" date, sent as a UTC datetime string, // but must be interpreted as a local time. Since // we have no timezone information we have to assume it's the // same as $tz. $due = new Horde_Date( array( 'year' => $due->year, 'month' => $due->month, 'mday' => $due->mday, 'hour' => $due->hour, 'min' => $due->min ), $tz ); $this->due = $due->timestamp(); } /* Start Date */ if ($start = $message->utcstartdate) { $start->setTimezone($tz); $this->start = $start->timestamp(); } elseif ($start = $message->startdate) { // See note above regarding utc vs local times. $start = new Horde_Date( array( 'year' => $start->year, 'month' => $start->month, 'mday' => $start->mday, 'hour' => $start->hour, 'min' => $start->min ), $tz ); $this->start = $start->timestamp(); } /* Priority */ switch ($message->getImportance()) { case Horde_ActiveSync_Message_Task::IMPORTANCE_LOW: $this->priority = 5; break; case Horde_ActiveSync_Message_Task::IMPORTANCE_NORMAL: $this->priority = 3; break; case Horde_ActiveSync_Message_Task::IMPORTANCE_HIGH: $this->priority = 1; break; default: $this->priority = 3; } if (($alarm = $message->getReminder()) && $this->due) { $alarm->setTimezone($tz); $this->alarm = ($this->due - $alarm->timestamp()) / 60; } $this->tasklist = $GLOBALS['prefs']->getValue('default_tasklist'); /* Categories */ if (is_array($message->categories) && count($message->categories)) { $this->tags = implode(',', $message->categories); } // Recurrence is handled by the client deleting the original event // and recreating a "dead" completed event and an active recurring // event with the first due date being the next due date in the // series. So, if deadoccur is set, we have to ignore the recurrence // properties. Otherwise, editing the "dead" occurance will recreate // a completely new recurring series on the client. if (!($message->recurrence && $message->recurrence->deadoccur) && !$message->deadoccur) { if ($rrule = $message->getRecurrence()) { $this->recurrence = $rrule; } } } }
Simpan