/** * 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) { } // Not sure why we were mapping the ORGANIZER to the person the // task is assigned to? If anything, this needs to be mapped to // any ATTENDEE fields from the vTodo. // try { // $assignee = $vTodo->getAttribute('ORGANIZER'); // if (!is_array($assignee)) { $this->assignee = $assignee; } // } catch (Horde_Icalendar_Exception $e) {} try { $organizer = $vTodo->getAttribute('ORGANIZER'); if (!is_array($organizer)) { $this->organizer = $organizer; } } catch (Horde_Icalendar_Exception $e) { } // If an attendee matches our from_addr, add current user as assignee. try { $atnames = $vTodo->getAttribute('ATTENDEE'); if (!is_array($atnames)) { $atnames = array($atnames); } $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create(); $all_addrs = $identity->getAll('from_addr'); foreach ($atnames as $index => $attendee) { if ($vTodo->getAttribute('VERSION') < 2) { $addr_ob = new Horde_Mail_Rfc822_Address($attendee); if (!$addr_ob->valid) { continue; } $attendee = $addr_ob->bare_address; $name = $addr_ob->personal; } else { $attendee = str_ireplace('mailto:', '', $attendee); $addr_ob = new Horde_Mail_Rfc822_Address($attendee); if (!$addr_ob->valid) { continue; } $attendee = $addr_ob->bare_address; $name = isset($atparms[$index]['CN']) ? $atparms[$index]['CN'] : null; } if (in_array($attendee, $all_addrs) !== false) { $this->assignee = $GLOBALS['conf']['assignees']['allow_external'] ? $attendee : $GLOBALS['registry']->getAuth(); $this->status = Nag::RESPONSE_ACCEPTED; break; } elseif ($GLOBALS['conf']['assignees']['allow_external']) { $this->assignee = $attendee; } } } catch (Horde_Icalendar_Exception $e) { } // Default to current user as organizer if (empty($this->organizer) && !empty($this->assignee)) { $this->organizer = $identity->getValue('from_addr'); } 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->tasklist); $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) { } // Recurrence. try { $rrule = $vTodo->getAttribute('RRULE'); if (!is_array($rrule)) { $this->recurrence = new Horde_Date_Recurrence($this->due); if (strpos($rrule, '=') !== false) { $this->recurrence->fromRRule20($rrule); } else { $this->recurrence->fromRRule10($rrule); } // Completions. EXDATE represents completed tasks, just add the // exception. $exdates = $vTodo->getAttributeValues('EXDATE'); if (is_array($exdates)) { foreach ($exdates as $exdate) { if (is_array($exdate)) { $this->recurrence->addCompletion((int) $exdate['year'], (int) $exdate['month'], (int) $exdate['mday']); } } } } } 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) { } // vCalendar 2.0 alarms foreach ($vTodo->getComponents() as $alarm) { if (!$alarm instanceof Horde_Icalendar_Valarm) { continue; } try { if ($alarm->getAttribute('ACTION') == 'NONE') { continue; } } catch (Horde_Icalendar_Exception $e) { } try { // @todo consider implementing different ACTION types. // $action = $alarm->getAttribute('ACTION'); $trigger = $alarm->getAttribute('TRIGGER'); $triggerParams = $alarm->getAttribute('TRIGGER', true); } catch (Horde_Icalendar_Exception $e) { continue; } if (!is_array($triggerParams)) { $triggerParams = array($triggerParams); } $haveTrigger = false; foreach ($triggerParams as $tp) { if (isset($tp['VALUE']) && $tp['VALUE'] == 'DATE-TIME') { if (isset($tp['RELATED']) && $tp['RELATED'] == 'END') { if ($this->due) { $this->alarm = intval(($this->due - $trigger) / 60); $haveTrigger = true; break; } } else { if ($this->start) { $this->alarm = intval(($this->start - $trigger) / 60); $haveTrigger = true; break; } } } elseif (isset($tp['RELATED']) && $tp['RELATED'] == 'END' && $this->due && $this->start) { $this->alarm = -intval($trigger / 60); $this->alarm -= $this->due - $this->start; $haveTrigger = true; break; } } if (!$haveTrigger) { $this->alarm = -intval($trigger / 60); } break; } // Alarm snoozing/dismissal if ($this->alarm) { try { // If X-MOZ-LASTACK is set, this task is either dismissed or // snoozed. $vTodo->getAttribute('X-MOZ-LASTACK'); try { // If X-MOZ-SNOOZE-TIME is set, this task is snoozed. $snooze = $vTodo->getAttribute('X-MOZ-SNOOZE-TIME'); $this->snooze = intval(($snooze - time()) / 60); } catch (Horde_Icalendar_Exception $e) { // If X-MOZ-SNOOZE-TIME is not set, this event is dismissed. $this->snooze = -1; } } catch (Horde_Icalendar_Exception $e) { } } 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)) { $this->priority = $priority; } } 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) { } }
/** * Build a task based a data array * * @todo Use 'organizer' but migrate old format. * * @param array $task The data for the task * * @return array The converted data array representing the task */ protected function _buildTask($task) { $result = array('task_id' => Horde_Url::uriB64Encode($task['uid']), 'uid' => $task['uid'], 'name' => $task['summary'], 'desc' => $task['body'], 'priority' => $task['priority'], 'parent' => $task['parent'], 'alarm' => $task['alarm'], 'completed' => !empty($task['completed']), 'completed_date' => $task['completed_date'], 'private' => $task['sensitivity'] != 'public', 'owner' => $GLOBALS['nag_shares']->getShare($this->_tasklist)->get('owner')); if (isset($task['categories'])) { $result['internaltags'] = $task['categories']; } if (!empty($task['start-date'])) { $result['start'] = $task['start-date']->format('U'); } if (!empty($task['due-date'])) { $result['due'] = $task['due-date']->format('U'); } if (!empty($task['creation-date'])) { $result['created'] = new Horde_Date($task['creation-date']); } if (!empty($task['last-modification-date'])) { $result['modified'] = new Horde_Date($task['last-modification-date']); } if (isset($task['recurrence']) && isset($task['due-date'])) { $recurrence = new Horde_Date_Recurrence($task['due-date']); $recurrence->fromKolab($task['recurrence']); $result['recurrence'] = $recurrence; } if (isset($task['organizer'])) { $result['assignee'] = $task['organizer']['smtp-address']; } if (isset($task['horde-estimate'])) { $result['estimate'] = $task['horde-estimate']; } if (isset($task['horde-alarm-methods'])) { $result['methods'] = @unserialize($task['horde-alarm-methods']); } return $result; }
/** * Generate the HTML for a vEvent. */ protected function _vEvent($vevent, $id, $method = 'PUBLISH', $components = array()) { global $injector, $prefs, $registry, $notification; $attendees = null; $desc = ''; $sender = $vevent->organizerName(); $options = array(); try { if (($attendees = $vevent->getAttribute('ATTENDEE')) && !is_array($attendees)) { $attendees = array($attendees); } } catch (Horde_Icalendar_Exception $e) { } switch ($method) { case 'PUBLISH': $desc = _("%s wishes to make you aware of \"%s\"."); if ($registry->hasMethod('calendar/import')) { $options['import'] = _("Add this to my calendar"); } break; case 'REQUEST': // Check if this is an update. try { $calendars = $registry->calendar->listCalendars(true); $registry->call('calendar/export', array($vevent->getAttributeSingle('UID'), 'text/calendar', array(), $calendars)); $desc = _("%s wants to notify you about changes in \"%s\"."); $is_update = true; } catch (Horde_Exception $e) { $desc = _("%s wishes to make you aware of \"%s\"."); $is_update = false; // Check that you are one of the attendees here. if (!empty($attendees)) { $identity = $injector->getInstance('IMP_Identity'); for ($i = 0, $c = count($attendees); $i < $c; ++$i) { $attendee = parse_url($attendees[$i]); if (!empty($attendee['path']) && $identity->hasAddress($attendee['path'])) { $desc = _("%s requests your presence at \"%s\"."); break; } } } } if ($is_update && $registry->hasMethod('calendar/replace')) { $options['accept-import'] = _("Accept and update in my calendar"); $options['import'] = _("Update in my calendar"); } elseif ($registry->hasMethod('calendar/import')) { $options['accept-import'] = _("Accept and add to my calendar"); $options['import'] = _("Add to my calendar"); } $options['accept'] = _("Accept request"); $options['tentative'] = _("Tentatively Accept request"); $options['deny'] = _("Deny request"); // $options['delegate'] = _("Delegate position"); break; case 'ADD': $desc = _("%s wishes to amend \"%s\"."); if ($registry->hasMethod('calendar/import')) { $options['import'] = _("Update this event on my calendar"); } break; case 'REFRESH': $desc = _("%s wishes to receive the latest information about \"%s\"."); $options['send'] = _("Send Latest Information"); break; case 'REPLY': $desc = _("%s has replied to the invitation to \"%s\"."); $from = $this->getConfigParam('imp_contents')->getHeader()->getHeader('from'); $sender = $from ? $from->getAddressList(true)->first()->bare_address : null; if ($registry->hasMethod('calendar/updateAttendee') && $this->_autoUpdateReply(self::AUTO_UPDATE_EVENT_REPLY, $sender)) { try { $registry->call('calendar/updateAttendee', array($vevent, $sender)); $notification->push(_("Respondent Status Updated."), 'horde.success'); } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error'); } } else { $options['update'] = _("Update respondent status"); } break; case 'CANCEL': try { $vevent->getAttributeSingle('RECURRENCE-ID'); $params = $vevent->getAttribute('RECURRENCE-ID', true); foreach ($params as $param) { if (array_key_exists('RANGE', $param)) { $desc = _("%s has cancelled multiple instances of the recurring \"%s\"."); } break; } if (empty($desc)) { $desc = _("%s has cancelled an instance of the recurring \"%s\"."); } if ($registry->hasMethod('calendar/replace')) { $options['delete'] = _("Update in my calendar"); } } catch (Horde_Icalendar_Exception $e) { $desc = _("%s has cancelled \"%s\"."); if ($registry->hasMethod('calendar/delete')) { $options['delete'] = _("Delete from my calendar"); } } break; } $view = $this->_getViewOb(); try { $start = $vevent->getAttribute('DTSTART'); $view->start = is_array($start) ? strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) : strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start); } catch (Horde_Icalendar_Exception $e) { $start = null; } try { $end = $vevent->getAttribute('DTEND'); $view->end = is_array($end) ? strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) : strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end); } catch (Horde_Icalendar_Exception $e) { $end = null; } try { $summary = $vevent->getAttributeSingle('SUMMARY'); $view->summary = $summary; } catch (Horde_Icalendar_Exception $e) { $summary = _("Unknown Meeting"); $view->summary_error = _("None"); } $view->desc = sprintf($desc, $sender, $summary); try { $view->desc2 = $vevent->getAttributeSingle('DESCRIPTION'); } catch (Horde_Icalendar_Exception $e) { } try { $view->loc = $vevent->getAttributeSingle('LOCATION'); } catch (Horde_Icalendar_Exception $e) { } try { $rrule = $vevent->getAttribute('RRULE'); } catch (Horde_Icalendar_Exception $e) { $rrule = array(); } if (!is_array($rrule)) { $recurrence = new Horde_Date_Recurrence(new Horde_Date($view->start)); if (strpos($rrule, '=') !== false) { $recurrence->fromRRule20($rrule); } else { $recurrence->fromRRule10($rrule); } // Add exceptions try { $exdates = $vevent->getAttributeValues('EXDATE'); if (is_array($exdates)) { foreach ($exdates as $exdate) { if (is_array($exdate)) { $recurrence->addException((int) $exdate['year'], (int) $exdate['month'], (int) $exdate['mday']); } } } } catch (Horde_ICalendar_Exception $e) { } $view->recurrence = $recurrence->toString($prefs->getValue('date_format')); $view->exceptions = array(); foreach ($components as $key => $component) { try { if ($component->getAttribute('RECURRENCE-ID') && $component->getAttributeSingle('UID') == $vevent->getAttributeSingle('UID')) { if ($ex = $this->_vEventException($component, $key, $method)) { $view->exceptions[] = $ex; } } } catch (Horde_Icalendar_Exception $e) { } } } if (!empty($attendees)) { $view->attendees = $this->_parseAttendees($vevent, $attendees); } if (!is_null($start) && !is_null($end) && in_array($method, array('PUBLISH', 'REQUEST', 'ADD')) && $registry->hasMethod('calendar/getFbCalendars') && $registry->hasMethod('calendar/listEvents')) { try { $calendars = $registry->call('calendar/getFbCalendars'); $vevent_start = new Horde_Date($start); $vevent_end = new Horde_Date($end); // Check if it's an all-day event. if (is_array($start)) { $vevent_allDay = true; $vevent_end = $vevent_end->sub(1); } else { $vevent_allDay = false; $time_span_start = $vevent_start->sub($prefs->getValue('conflict_interval') * 60); $time_span_end = $vevent_end->add($prefs->getValue('conflict_interval') * 60); } $events = $registry->call('calendar/listEvents', array($start, $vevent_end, $calendars, false)); // TODO: Check if there are too many events to show. $conflicts = array(); foreach ($events as $calendar) { foreach ($calendar as $event) { // TODO: WTF? Why are we using Kronolith constants // here? if (in_array($event->status, array(Kronolith::STATUS_CANCELLED, Kronolith::STATUS_FREE))) { continue; } if ($vevent_allDay || $event->isAllDay()) { $type = 'collision'; } elseif ($event->end->compareDateTime($time_span_start) <= -1 || $event->start->compareDateTime($time_span_end) >= 1) { continue; } elseif ($event->end->compareDateTime($vevent_start) <= -1 || $event->start->compareDateTime($vevent_end) >= 1) { $type = 'nearcollision'; } else { $type = 'collision'; } $conflicts[] = array('collision' => $type == 'collision', 'range' => $event->getTimeRange(), 'title' => $event->getTitle()); } } if (!empty($conflicts)) { $view->conflicts = $conflicts; } } catch (Horde_Exception $e) { } } if (!empty($options)) { reset($options); $view->options = $options; $view->options_id = $id; } return $view->render('action'); }
/** * 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 = (bool) $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->p = $this->parent_id; $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(); } 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; }
/** * Parse recurrence properties. * * @param string $value MAPI stream * * @return Horde_Date_Recurrence * @throws Horde_Compress_Exception */ protected function _parseRecurrence($value) { $deleted = $modified = array(); // both are 0x3004 (version strings); $this->_geti($value, 16); $this->_geti($value, 16); $freq = $this->_geti($value, 16); $pattern = $this->_geti($value, 16); $calendarType = $this->_geti($value, 16); $firstDt = $this->_geti($value, 32); $period = $this->_geti($value, 32); // Only used for tasks, otherwise value must be zero. $flag = $this->_geti($value, 32); // TypeSpecific field switch ($pattern) { case Horde_Compress_Tnef::PATTERN_DAY: // Nothing here to see, move along. break; case Horde_Compress_Tnef::PATTERN_WEEK: // Bits: 0/unused, 1/Saturday, 2/Friday, 3/Thursday, 4/Wednesday, // 5/Tuesday, 6/Monday, 7/Sunday. $day = $this->_geti($value, 8); // ?? $this->_geti($value, 24); break; case Horde_Compress_Tnef::PATTERN_MONTH: case Horde_Compress_Tnef::PATTERN_MONTH_END: // Day of month on which the recurrence falls. $day = $this->_geti($value, 32); break; case Horde_Compress_Tnef::PATTERN_MONTH_NTH: // Bits: 0/unused, 1/Saturday, 2/Friday, 3/Thursday, 4/Wednesday, // 5/Tuesday, 6/Monday, 7/Sunday. // For Nth Weekday of month $day = $this->_geti($value, 8); $this->_geti($value, 24); $n = $this->_geti($value, 32); break; } $end = $this->_geti($value, 32); $count = $this->_geti($value, 32); $fdow = $this->_geti($value, 32); $deletedCount = $this->_geti($value, 32); for ($i = 0; $i < $deletedCount; $i++) { $deleted[] = $this->_geti($value, 32); } $modifiedCount = $this->_geti($value, 32); for ($i = 0; $i < $modifiedCount; $i++) { $modified[] = $this->_geti($value, 32); } // What Timezone are these in? try { $startDate = new Horde_Date(Horde_Mapi::filetimeToUnixtime($this->_geti($value, 32))); $endDate = new Horde_Date(Horde_Mapi::filetimeToUnixtime($this->_geti($value, 32))); } catch (Horde_Mapi_Exception $e) { throw new Horde_Compress_Exception($e); } catch (Horde_Date_Exception $e) { throw new Horde_Compress_Exception($e); } $rrule = new Horde_Date_Recurrence($startDate); switch ($pattern) { case Horde_Compress_Tnef::PATTERN_DAY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_DAILY); break; case Horde_Compress_Tnef::PATTERN_WEEK: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY); break; case Horde_Compress_Tnef::PATTERN_MONTH: case Horde_Compress_Tnef::PATTERN_MONTH_END: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_DATE); break; case Horde_Compress_Tnef::PATTERN_MONTH_NTH: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY); break; default: if ($freq == Horde_Compress_Tnef::RECUR_YEARLY) { $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY); } } switch ($end) { case Horde_Compress_Tnef::RECUR_END_N: $rrule->setRecurCount($count); break; case Horde_Compress_Tnef::RECUR_END_DATE: $rrule->setRecurEnd($endDate); break; } return $rrule; }
/** * Obtain a recurrence object. Note this returns a Horde_Date_Recurrence * object, not Horde_ActiveSync_Message_Recurrence. * * @return Horde_Date_Recurrence */ public function getRecurrence() { if (!($recurrence = $this->_getAttribute('recurrence'))) { return false; } $d = clone $this->_getAttribute('starttime'); $d->setTimezone($this->getTimezone()); $rrule = new Horde_Date_Recurrence($d); /* Map MS AS type field to Horde_Date_Recurrence types */ switch ($recurrence->type) { case Horde_ActiveSync_Message_Recurrence::TYPE_DAILY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_DAILY); break; case Horde_ActiveSync_Message_Recurrence::TYPE_WEEKLY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY); $rrule->setRecurOnDay($recurrence->dayofweek); break; case Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_DATE); break; case Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY_NTH: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY); $rrule->setRecurOnDay($recurrence->dayofweek); break; case Horde_ActiveSync_Message_Recurrence::TYPE_YEARLY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY_DATE); break; case Horde_ActiveSync_Message_Recurrence::TYPE_YEARLYNTH: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY); $rrule->setRecurOnDay($recurrence->dayofweek); break; } if ($rcnt = $recurrence->occurrences) { $rrule->setRecurCount($rcnt); } if ($runtil = $recurrence->until) { $rrule->setRecurEnd(new Horde_Date($runtil)); } if ($interval = $recurrence->interval) { $rrule->setRecurInterval($interval); } return $rrule; }
public function testRecurringTasks() { $due = time() - 1; $recurrence = new Horde_Date_Recurrence($due); $recurrence->setRecurType(Horde_Date_Recurrence::RECUR_DAILY); $id = $this->_add(array('name' => 'TEST', 'desc' => 'Some test task.', 'due' => $due, 'recurrence' => $recurrence)); $due = new Horde_Date($due); $result = self::$driver->get($id[0]); $next = $result->getNextDue(); $this->assertInstanceOf('Horde_Date', $next); $this->assertEquals($due->timestamp(), $next->timestamp()); $result->toggleComplete(); $result->save(); $result2 = self::$driver->get($id[0]); $due->mday++; $next = $result2->getNextDue(); $this->assertInstanceOf('Horde_Date', $next); $this->assertEquals($due->timestamp(), $next->timestamp()); $result2->toggleComplete(); $result2->save(); $result3 = self::$driver->get($id[0]); $due->mday++; $next = $result3->getNextDue(); $this->assertInstanceOf('Horde_Date', $next); $this->assertEquals($due->timestamp(), $next->timestamp()); $this->assertFalse($result3->recurrence->hasCompletion($due->year, $due->month, $due->mday)); $due->mday--; $this->assertTrue($result3->recurrence->hasCompletion($due->year, $due->month, $due->mday)); $due->mday--; $this->assertTrue($result3->recurrence->hasCompletion($due->year, $due->month, $due->mday)); }
/** * Return whether or not this object is equal to another recurrence object. * The objects are considered equal if the recurrence rules are the same. * This does not take any exceptions into account. * * @param Horde_Date_Recurrence $recurrence The recurrence object to check * equality to. * * @return boolean True if the recurrence rules are the same. * @since 2.2.0 */ public function isEqual(Horde_Date_Recurrence $recurrence) { return $this->getRecurType() == $recurrence->getRecurType() && $this->getRecurInterval() == $recurrence->getRecurInterval() && $this->getRecurCount() == $recurrence->getRecurCount() && $this->getRecurEnd() == $recurrence->getRecurEnd() && $this->getRecurStart() == $recurrence->getRecurStart() && $this->getRecurOnDays() == $recurrence->getRecurOnDays(); }
/** * Build a task based a data array * * @todo Use 'organizer' but migrate old format. * * @param array $task The data for the task * * @return array The converted data array representing the task */ protected function _buildTask($task) { // This decoding of parent ID hashes is required because of a // previous bug in Nag_Driver_Kolab. // See Bug: 14197 $parent_uid = $task['parent']; if (!empty($parent_uid) && $this->_isBase64EncodedUid($parent_uid)) { $parent_uid = Horde_Url::uriB64Decode($parent_uid); } $result = array('task_id' => Horde_Url::uriB64Encode($task['uid']), 'uid' => $task['uid'], 'name' => $task['summary'], 'desc' => $task['body'], 'priority' => $task['priority'], 'parent' => Horde_Url::uriB64Encode($parent_uid), 'alarm' => $task['alarm'], 'completed' => !empty($task['completed']), 'completed_date' => $task['completed_date'], 'private' => $task['sensitivity'] != 'public', 'owner' => $GLOBALS['nag_shares']->getShare($this->_tasklist)->get('owner')); if (isset($task['categories'])) { $result['internaltags'] = $task['categories']; } if (!empty($task['start-date'])) { $result['start'] = $task['start-date']->format('U'); } if (!empty($task['due-date'])) { $result['due'] = $task['due-date']->format('U'); } if (!empty($task['creation-date'])) { $result['created'] = new Horde_Date($task['creation-date']); } if (!empty($task['last-modification-date'])) { $result['modified'] = new Horde_Date($task['last-modification-date']); } if (isset($task['recurrence']) && isset($task['due-date'])) { $recurrence = new Horde_Date_Recurrence($task['due-date']); $recurrence->fromKolab($task['recurrence']); $result['recurrence'] = $recurrence; } if (isset($task['organizer'])) { $result['assignee'] = $task['organizer']['smtp-address']; } if (isset($task['horde-estimate'])) { $result['estimate'] = $task['horde-estimate']; } if (isset($task['horde-alarm-methods'])) { $result['methods'] = @unserialize($task['horde-alarm-methods']); } return $result; }
public function getInfo(&$vars, &$var, &$info) { $recur = $vars->recurrence; if (!$recur) { return; } $recurrence = new Horde_Date_Recurrence($this->_getDue($var, $vars)); if ($vars->recur_end_type == 'date') { $recurEnd = Nag::parseDate($vars->recur_end, false); $recurEnd->hour = 23; $recurEnd->min = $recurEnd->sec = 59; $recurrence->setRecurEnd($recurEnd); } elseif ($vars->recur_end_type == 'count') { $recurrence->setRecurCount($vars->recur_count); } elseif ($vars->recur_end_type == 'none') { $recurrence->setRecurCount(0); $recurrence->setRecurEnd(null); } $recurrence->setRecurType($recur); switch ($recur) { case Horde_Date_Recurrence::RECUR_DAILY: $recurrence->setRecurInterval($vars->get('recur_daily_interval', 1)); break; case Horde_Date_Recurrence::RECUR_WEEKLY: $weekly = $vars->weekly; $weekdays = 0; if (is_array($weekly)) { foreach ($weekly as $day) { $weekdays |= $day; } } if ($weekdays == 0) { // Sunday starts at 0. switch ($recurrence->start->dayOfWeek()) { case 0: $weekdays |= Horde_Date::MASK_SUNDAY; break; case 1: $weekdays |= Horde_Date::MASK_MONDAY; break; case 2: $weekdays |= Horde_Date::MASK_TUESDAY; break; case 3: $weekdays |= Horde_Date::MASK_WEDNESDAY; break; case 4: $weekdays |= Horde_Date::MASK_THURSDAY; break; case 5: $weekdays |= Horde_Date::MASK_FRIDAY; break; case 6: $weekdays |= Horde_Date::MASK_SATURDAY; break; } } $recurrence->setRecurInterval($vars->get('recur_weekly_interval', 1)); $recurrence->setRecurOnDay($weekdays); break; case Horde_Date_Recurrence::RECUR_MONTHLY_DATE: switch ($vars->recur_monthly_scheme) { case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY: $recurrence->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY); case Horde_Date_Recurrence::RECUR_MONTHLY_DATE: $recurrence->setRecurInterval($vars->recur_monthly ? 1 : $vars->get('recur_monthly_interval', 1)); break; default: $recurrence->setRecurInterval($vars->get('recur_day_of_month_interval', 1)); break; } break; case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY: $recurrence->setRecurInterval($vars->get('recur_week_of_month_interval', 1)); break; case Horde_Date_Recurrence::RECUR_YEARLY_DATE: switch ($vars->recur_yearly_scheme) { case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY: case Horde_Date_Recurrence::RECUR_YEARLY_DAY: $recurrence->setRecurType($vars->recur_yearly_scheme); case Horde_Date_Recurrence::RECUR_YEARLY_DATE: $recurrence->setRecurInterval($vars->recur_yearly ? 1 : $vars->get('recur_yearly_interval', 1)); break; default: $recurrence->setRecurInterval($vars->get('recur_yearly_interval', 1)); break; } break; case Horde_Date_Recurrence::RECUR_YEARLY_DAY: $recurrence->setRecurInterval($vars->get('recur_yearly_day_interval', $yearly_interval)); break; case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY: $recurrence->setRecurInterval($vars->get('recur_yearly_weekday_interval', $yearly_interval)); break; } if ($vars->exceptions) { foreach ($vars->exceptions as $exception) { $recurrence->addException((int) substr($exception, 0, 4), (int) substr($exception, 4, 2), (int) substr($exception, 6, 2)); } } $info = $recurrence; }
/** * Return an array describing this task from the provided backend data. * * @param array $row The backend data * @param boolean $include_history Include history data. * * @return array The task data. */ protected function _buildTask($row, $include_history = true) { // Make sure tasks always have a UID. if (empty($row['task_uid'])) { $row['task_uid'] = strval(new Horde_Support_Guid()); $query = 'UPDATE ' . $this->_params['table'] . ' SET task_uid = ?' . ' WHERE task_owner = ? AND task_id = ?'; $values = array($row['task_uid'], $row['task_owner'], $row['task_id']); try { $this->_db->update($query, $values); } catch (Horde_Db_Exception $e) { } } if (!$row['task_due'] || !$row['task_recurtype']) { $recurrence = null; } else { $recurrence = new Horde_Date_Recurrence($row['task_due']); $recurrence->setRecurType((int) $row['task_recurtype']); $recurrence->setRecurInterval((int) $row['task_recurinterval']); if (isset($row['task_recurenddate']) && $row['task_recurenddate'] != '9999-12-31 23:59:59') { $recur_end = new Horde_Date($row['task_recurenddate'], 'UTC'); $recur_end->setTimezone(date_default_timezone_get()); $recurrence->setRecurEnd($recur_end); } if (isset($row['task_recurcount'])) { $recurrence->setRecurCount((int) $row['task_recurcount']); } if (isset($row['task_recurdays'])) { $recurrence->recurData = (int) $row['task_recurdays']; } if (!empty($row['task_exceptions'])) { $recurrence->exceptions = explode(',', $row['task_exceptions']); } if (!empty($row['task_completions'])) { $recurrence->completions = explode(',', $row['task_completions']); } } /* Create a new task based on $row's values. */ $task = array('tasklist_id' => $row['task_owner'], 'task_id' => $row['task_id'], 'uid' => Horde_String::convertCharset($row['task_uid'], $this->_params['charset'], 'UTF-8'), 'parent' => $row['task_parent'], 'owner' => $row['task_creator'], 'assignee' => $row['task_assignee'], 'name' => Horde_String::convertCharset($row['task_name'], $this->_params['charset'], 'UTF-8'), 'desc' => Horde_String::convertCharset($row['task_desc'], $this->_params['charset'], 'UTF-8'), 'start' => $row['task_start'], 'due' => $row['task_due'], 'priority' => $row['task_priority'], 'estimate' => (double) $row['task_estimate'], 'completed' => $row['task_completed'], 'completed_date' => isset($row['task_completed_date']) ? $row['task_completed_date'] : null, 'alarm' => $row['task_alarm'], 'methods' => Horde_String::convertCharset(@unserialize($row['task_alarm_methods']), $this->_params['charset'], 'UTF-8'), 'private' => $row['task_private'], 'recurrence' => $recurrence); if ($include_history) { try { $userId = $GLOBALS['registry']->getAuth(); $log = $GLOBALS['injector']->getInstance('Horde_History')->getHistory('nag:' . $row['task_owner'] . ':' . $row['task_uid']); foreach ($log as $entry) { switch ($entry['action']) { case 'add': $task['created'] = new Horde_Date($entry['ts']); if ($userId != $entry['who']) { $task['createdby'] = sprintf(_("by %s"), Nag::getUserName($entry['who'])); } else { $task['createdby'] = _("by me"); } break; case 'modify': $task['modified'] = new Horde_Date($entry['ts']); if ($userId != $entry['who']) { $task['modifiedby'] = sprintf(_("by %s"), Nag::getUserName($entry['who'])); } else { $task['modifiedby'] = _("by me"); } break; } } } catch (Horde_Exception $e) { } } return $task; }
public function testEncodingSimpleExceptions() { $this->markTestSkipped('Needs updated fixture.'); $l = new Horde_Test_Log(); $logger = $l->getLogger(); //$logger = new Horde_Log_Logger(new Horde_Log_Handler_Stream(fopen('/tmp/test.log', 'a'))); // Every other week recurrence, on thursday, no end. $r = new Horde_Date_Recurrence('2011-12-01T15:00:00'); $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY); $r->setRecurInterval(2); $r->setRecurOnDay(Horde_Date::MASK_THURSDAY); $r->addException(2011, 12, 29); $e = new Horde_ActiveSync_Message_Exception(); $d = new Horde_Date('2011-12-29T15:00:00'); $e->setExceptionStartTime($d); $e->deleted = true; $appt = new Horde_ActiveSync_Message_Appointment(array('logger' => $logger)); $appt->setSubject('Event Title'); $appt->setBody('Event Description'); $appt->setLocation('Philadelphia, PA'); $start = new Horde_Date('2011-12-01T15:00:00'); $appt->setDatetime(array('start' => $start, 'end' => new Horde_Date('2011-12-01T16:00:00'), 'allday' => false)); $appt->setTimezone($start); $appt->setSensitivity(Horde_ActiveSync_Message_Appointment::SENSITIVITY_PERSONAL); $appt->setBusyStatus(Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY); $appt->setDTStamp($start->timestamp()); $appt->setRecurrence($r); $appt->addException($e); $stream = fopen('php://memory', 'w+'); $encoder = new Horde_ActiveSync_Wbxml_Encoder($stream); $encoder->setLogger($logger); $encoder->startTag(Horde_ActiveSync::SYNC_DATA); $appt->encodeStream($encoder); $encoder->endTag(); $fixture = file_get_contents(__DIR__ . '/fixtures/simpleexception.wbxml'); rewind($stream); $results = stream_get_contents($stream); fclose($stream); $this->assertEquals($fixture, $results); }
function handleMessage($fqhostname, $sender, $resource, $tmpfname) { global $conf; $rdata = $this->_getResourceData($sender, $resource); if (is_a($rdata, 'PEAR_Error')) { return $rdata; } else { if ($rdata === false) { /* No data, probably not a local user */ return true; } else { if ($rdata['homeserver'] && $rdata['homeserver'] != $fqhostname) { /* Not the users homeserver, ignore */ return true; } } } $cn = $rdata['cn']; $id = $rdata['id']; if (isset($rdata['action'])) { $action = $rdata['action']; } else { // Manual is the only safe default! $action = RM_ACT_MANUAL; } Horde::log(sprintf('Action for %s is %s', $sender, $action), 'DEBUG'); // Get out as early as possible if manual if ($action == RM_ACT_MANUAL) { Horde::log(sprintf('Passing through message to %s', $id), 'INFO'); return true; } /* Get the iCalendar data (i.e. the iTip request) */ $iCalendar =& $this->_getICal($tmpfname); if ($iCalendar === false) { // No iCal in mail Horde::log(sprintf('Could not parse iCalendar data, passing through to %s', $id), 'INFO'); return true; } // Get the event details out of the iTip request $itip =& $iCalendar->findComponent('VEVENT'); if ($itip === false) { Horde::log(sprintf('No VEVENT found in iCalendar data, passing through to %s', $id), 'INFO'); return true; } $itip = new Horde_Kolab_Resource_Itip($itip); // What is the request's method? i.e. should we create a new event/cancel an // existing event, etc. $method = Horde_String::upper($iCalendar->getAttributeDefault('METHOD', $itip->getMethod())); // What resource are we managing? Horde::log(sprintf('Processing %s method for %s', $method, $id), 'DEBUG'); // This is assumed to be constant across event creation/modification/deletipn $uid = $itip->getUid(); Horde::log(sprintf('Event has UID %s', $uid), 'DEBUG'); // Who is the organiser? $organiser = $itip->getOrganizer(); Horde::log(sprintf('Request made by %s', $organiser), 'DEBUG'); // What is the events summary? $summary = $itip->getSummary(); $estart = new Horde_Kolab_Resource_Epoch($itip->getStart()); $dtstart = $estart->getEpoch(); $eend = new Horde_Kolab_Resource_Epoch($itip->getEnd()); $dtend = $eend->getEpoch(); Horde::log(sprintf('Event starts on <%s> %s and ends on <%s> %s.', $dtstart, $this->iCalDate2Kolab($dtstart), $dtend, $this->iCalDate2Kolab($dtend)), 'DEBUG'); if ($action == RM_ACT_ALWAYS_REJECT) { if ($method == 'REQUEST') { Horde::log(sprintf('Rejecting %s method', $method), 'INFO'); return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update); } else { Horde::log(sprintf('Passing through %s method for ACT_ALWAYS_REJECT policy', $method), 'INFO'); return true; } } $is_update = false; $imap_error = false; $ignore = array(); $folder = $this->_imapConnect($id); if (is_a($folder, 'PEAR_Error')) { $imap_error =& $folder; } if (!is_a($imap_error, 'PEAR_Error') && !$folder->exists()) { $imap_error =& PEAR::raiseError('Error, could not open calendar folder!', OUT_LOG | EX_TEMPFAIL); } if (!is_a($imap_error, 'PEAR_Error')) { $data = $folder->getData(); if (is_a($data, 'PEAR_Error')) { $imap_error =& $data; } } if (is_a($imap_error, 'PEAR_Error')) { Horde::log(sprintf('Failed accessing IMAP calendar: %s', $folder->getMessage()), 'ERR'); if ($action == RM_ACT_MANUAL_IF_CONFLICTS) { return true; } } switch ($method) { case 'REQUEST': if ($action == RM_ACT_MANUAL) { Horde::log(sprintf('Passing through %s method', $method), 'INFO'); break; } if (is_a($imap_error, 'PEAR_Error') || !$data->objectUidExists($uid)) { $old_uid = null; } else { $old_uid = $uid; $ignore[] = $uid; $is_update = true; } /** Generate the Kolab object */ $object = $itip->getKolabObject(); $outofperiod = 0; // Don't even bother checking free/busy info if RM_ACT_ALWAYS_ACCEPT // is specified if ($action != RM_ACT_ALWAYS_ACCEPT) { try { require_once 'Horde/Kolab/Resource/Freebusy.php'; $fb = Horde_Kolab_Resource_Freebusy::singleton(); $vfb = $fb->get($resource); } catch (Exception $e) { return PEAR::raiseError($e->getMessage(), OUT_LOG | EX_UNAVAILABLE); } $vfbstart = $vfb->getAttributeDefault('DTSTART', 0); $vfbend = $vfb->getAttributeDefault('DTEND', 0); Horde::log(sprintf('Free/busy info starts on <%s> %s and ends on <%s> %s', $vfbstart, $this->iCalDate2Kolab($vfbstart), $vfbend, $this->iCalDate2Kolab($vfbend)), 'DEBUG'); $evfbend = new Horde_Kolab_Resource_Epoch($vfbend); if ($vfbstart && $dtstart > $evfbend->getEpoch()) { $outofperiod = 1; } else { // Check whether we are busy or not $busyperiods = $vfb->getBusyPeriods(); Horde::log(sprintf('Busyperiods: %s', print_r($busyperiods, true)), 'DEBUG'); $extraparams = $vfb->getExtraParams(); Horde::log(sprintf('Extraparams: %s', print_r($extraparams, true)), 'DEBUG'); $conflict = false; if (!empty($object['recurrence'])) { $recurrence = new Horde_Date_Recurrence($dtstart); $recurrence->fromHash($object['recurrence']); $duration = $dtend - $dtstart; $events = array(); $next_start = $vfbstart; $next = $recurrence->nextActiveRecurrence($vfbstart); while ($next !== false && $next->compareDate($vfbend) <= 0) { $next_ts = $next->timestamp(); $events[$next_ts] = $next_ts + $duration; $next = $recurrence->nextActiveRecurrence(array('year' => $next->year, 'month' => $next->month, 'mday' => $next->mday + 1, 'hour' => $next->hour, 'min' => $next->min, 'sec' => $next->sec)); } } else { $events = array($dtstart => $dtend); } foreach ($events as $dtstart => $dtend) { Horde::log(sprintf('Requested event from %s to %s', strftime('%a, %d %b %Y %H:%M:%S %z', $dtstart), strftime('%a, %d %b %Y %H:%M:%S %z', $dtend)), 'DEBUG'); foreach ($busyperiods as $busyfrom => $busyto) { if (empty($busyfrom) && empty($busyto)) { continue; } Horde::log(sprintf('Busy period from %s to %s', strftime('%a, %d %b %Y %H:%M:%S %z', $busyfrom), strftime('%a, %d %b %Y %H:%M:%S %z', $busyto)), 'DEBUG'); if (isset($extraparams[$busyfrom]['X-UID']) && in_array(base64_decode($extraparams[$busyfrom]['X-UID']), $ignore) || isset($extraparams[$busyfrom]['X-SID']) && in_array(base64_decode($extraparams[$busyfrom]['X-SID']), $ignore)) { // Ignore continue; } if ($busyfrom >= $dtstart && $busyfrom < $dtend || $dtstart >= $busyfrom && $dtstart < $busyto) { Horde::log('Request overlaps', 'DEBUG'); $conflict = true; break; } } if ($conflict) { break; } } if ($conflict) { if ($action == RM_ACT_MANUAL_IF_CONFLICTS) { //sendITipReply(RM_ITIP_TENTATIVE); Horde::log('Conflict detected; Passing mail through', 'INFO'); return true; } else { if ($action == RM_ACT_REJECT_IF_CONFLICTS) { Horde::log('Conflict detected; rejecting', 'INFO'); return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update); } } } } } if (is_a($imap_error, 'PEAR_Error')) { Horde::log('Could not access users calendar; rejecting', 'INFO'); return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update); } // At this point there was either no conflict or RM_ACT_ALWAYS_ACCEPT // was specified; either way we add the new event & send an 'ACCEPT' // iTip reply Horde::log(sprintf('Adding event %s', $uid), 'INFO'); if (!empty($conf['kolab']['filter']['simple_locks'])) { if (!empty($conf['kolab']['filter']['simple_locks_timeout'])) { $timeout = $conf['kolab']['filter']['simple_locks_timeout']; } else { $timeout = 60; } if (!empty($conf['kolab']['filter']['simple_locks_dir'])) { $lockdir = $conf['kolab']['filter']['simple_locks_dir']; } else { $lockdir = Horde::getTempDir() . '/Kolab_Filter_locks'; if (!is_dir($lockdir)) { mkdir($lockdir, 0700); } } if (is_dir($lockdir)) { $lockfile = $lockdir . '/' . $resource . '.lock'; $counter = 0; while ($counter < $timeout && file_exists($lockfile)) { sleep(1); $counter++; } if ($counter == $timeout) { Horde::log(sprintf('Lock timeout of %s seconds exceeded. Rejecting invitation.', $timeout), 'ERR'); return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update); } $result = file_put_contents($lockfile, 'LOCKED'); if ($result === false) { Horde::log(sprintf('Failed creating lock file %s.', $lockfile), 'ERR'); } else { $this->lockfile = $lockfile; } } else { Horde::log(sprintf('The lock directory %s is missing. Disabled locking.', $lockdir), 'ERR'); } } $itip->setAccepted($resource); $result = $data->save($itip->getKolabObject(), $old_uid); if (is_a($result, 'PEAR_Error')) { $result->code = OUT_LOG | EX_UNAVAILABLE; return $result; } if ($outofperiod) { Horde::log('No freebusy information available', 'NOTICE'); return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_TENTATIVE, $organiser, $uid, $is_update); } else { return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_ACCEPT, $organiser, $uid, $is_update); } case 'CANCEL': Horde::log(sprintf('Removing event %s', $uid), 'INFO'); if (is_a($imap_error, 'PEAR_Error')) { $body = sprintf(Horde_Kolab_Resource_Translation::t("Unable to access %s's calendar:"), $resource) . "\n\n" . $summary; $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary); } else { if (!$data->objectUidExists($uid)) { Horde::log(sprintf('Canceled event %s is not present in %s\'s calendar', $uid, $resource), 'WARNING'); $body = sprintf(Horde_Kolab_Resource_Translation::t("The following event that was canceled is not present in %s's calendar:"), $resource) . "\n\n" . $summary; $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary); } else { /** * Delete the messages from IMAP * Delete any old events that we updated */ Horde::log(sprintf('Deleting %s because of cancel', $uid), 'DEBUG'); $result = $data->delete($uid); if (is_a($result, 'PEAR_Error')) { Horde::log(sprintf('Deleting %s failed with %s', $uid, $result->getMessage()), 'DEBUG'); } $body = Horde_Kolab_Resource_Translation::t("The following event has been successfully removed:") . "\n\n" . $summary; $subject = sprintf(Horde_Kolab_Resource_Translation::t("%s has been cancelled"), $summary); } } Horde::log(sprintf('Sending confirmation of cancelation to %s', $organiser), 'WARNING'); $body = new MIME_Part('text/plain', Horde_String::wrap($body, 76)); $mime =& MIME_Message::convertMimePart($body); $mime->setTransferEncoding('quoted-printable'); $mime->transferEncodeContents(); // Build the reply headers. $msg_headers = new MIME_Headers(); $msg_headers->addHeader('Date', date('r')); $msg_headers->addHeader('From', $resource); $msg_headers->addHeader('To', $organiser); $msg_headers->addHeader('Subject', $subject); $msg_headers->addMIMEHeaders($mime); $reply = new Horde_Kolab_Resource_Reply($resource, $organiser, $msg_headers, $mime); Horde::log('Successfully prepared cancellation reply', 'INFO'); return $reply; default: // We either don't currently handle these iTip methods, or they do not // apply to what we're trying to accomplish here Horde::log(sprintf('Ignoring %s method and passing message through to %s', $method, $resource), 'INFO'); return true; } }
public function getLink($datetime = null, $icons = true, $from_url = null, $full = false, $encoded = true) { global $prefs; if (is_null($datetime)) { $datetime = $this->start; } if (is_null($from_url)) { $from_url = Horde::selfUrl(true, false, true); } $event_title = $this->getTitle(); $view_url = $this->getViewUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'), 'url' => $from_url), $full, $encoded); $read_permission = $this->hasPermission(Horde_Perms::READ); $link = '<span' . $this->getCSSColors() . '>'; if ($read_permission && $view_url) { $link .= Horde::linkTooltip($view_url, $event_title, $this->getStatusClass(), '', '', $this->getTooltip(), '', array('style' => $this->getCSSColors(false))); } $link .= htmlspecialchars($event_title); if ($read_permission && $view_url) { $link .= '</a>'; } if ($icons && $prefs->getValue('show_icons')) { $icon_color = $this->_foregroundColor == '#000' ? '000' : 'fff'; $status = ''; if ($this->alarm) { if ($this->alarm % 10080 == 0) { $alarm_value = $this->alarm / 10080; $title = sprintf(ngettext("Alarm %d week before", "Alarm %d weeks before", $alarm_value), $alarm_value); } elseif ($this->alarm % 1440 == 0) { $alarm_value = $this->alarm / 1440; $title = sprintf(ngettext("Alarm %d day before", "Alarm %d days before", $alarm_value), $alarm_value); } elseif ($this->alarm % 60 == 0) { $alarm_value = $this->alarm / 60; $title = sprintf(ngettext("Alarm %d hour before", "Alarm %d hours before", $alarm_value), $alarm_value); } else { $alarm_value = $this->alarm; $title = sprintf(ngettext("Alarm %d minute before", "Alarm %d minutes before", $alarm_value), $alarm_value); } $status .= Horde::fullSrcImg('alarm-' . $icon_color . '.png', array('attr' => array('alt' => $title, 'title' => $title, 'class' => 'iconAlarm'))); } if ($this->recurs()) { $title = Kronolith::recurToString($this->recurrence->getRecurType()); $status .= Horde::fullSrcImg('recur-' . $icon_color . '.png', array('attr' => array('alt' => $title, 'title' => $title, 'class' => 'iconRecur'))); } elseif ($this->baseid) { $title = _("Exception"); $status .= Horde::fullSrcImg('exception-' . $icon_color . '.png', array('attr' => array('alt' => $title, 'title' => $title, 'class' => 'iconRecur'))); } if ($this->private) { $title = _("Private event"); $status .= Horde::fullSrcImg('private-' . $icon_color . '.png', array('attr' => array('alt' => $title, 'title' => $title, 'class' => 'iconPrivate'))); } if (!empty($this->attendees)) { $status .= Horde::fullSrcImg('attendees-' . $icon_color . '.png', array('attr' => array('alt' => _("Meeting"), 'title' => _("Meeting"), 'class' => 'iconPeople'))); } $space = ' '; if (!empty($this->icon)) { $link = $status . '<img class="kronolithEventIcon" src="' . $this->icon . '" /> ' . $link; } elseif (!empty($status)) { $link .= ' ' . $status; $space = ''; } if ((!$this->private || $this->creator == $GLOBALS['registry']->getAuth()) && Kronolith::getDefaultCalendar(Horde_Perms::EDIT)) { $url = $this->getEditUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'), 'url' => $from_url), $full); if ($url) { $link .= $space . $url->link(array('title' => sprintf(_("Edit %s"), $event_title), 'class' => 'iconEdit')) . Horde::fullSrcImg('edit-' . $icon_color . '.png', array('attr' => array('alt' => _("Edit")))) . '</a>'; $space = ''; } } if ($this->hasPermission(Horde_Perms::DELETE)) { $url = $this->getDeleteUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'), 'url' => $from_url), $full); if ($url) { $link .= $space . $url->link(array('title' => sprintf(_("Delete %s"), $event_title), 'class' => 'iconDelete')) . Horde::fullSrcImg('delete-' . $icon_color . '.png', array('attr' => array('alt' => _("Delete")))) . '</a>'; } } } return $link . '</span>'; }
/** * Imports a backend specific event object. * * @param mixed $event Backend specific event object that this object * will represent. */ public function fromDriver($event) { $this->id = '_' . $this->_api . $event['id']; $this->icon = !empty($event['icon']) ? $event['icon'] : null; $this->title = $event['title']; $this->description = isset($event['description']) ? $event['description'] : ''; if (isset($event['location'])) { $this->location = $event['location']; } try { $this->start = new Horde_Date($event['start']); $this->end = new Horde_Date($event['end']); } catch (Horde_Date_Exception $e) { throw new Kronolith_Exception($e); } if (isset($event['status'])) { switch ($event['status']) { case 'confirmed': $this->status = Kronolith::STATUS_CONFIRMED; break; case 'tentative': $this->status = Kronolith::STATUS_TENTATIVE; break; default: $this->status = Kronolith::STATUS_FREE; } } else { $this->status = Kronolith::STATUS_FREE; } if (isset($event['private'])) { $this->private = $event['private']; } $this->_params = $event['params']; $this->_link = !empty($event['link']) ? $event['link'] : null; $this->url = !empty($event['url']) ? (string) $event['url'] : null; $this->_editLink = !empty($event['edit_link']) ? $event['edit_link'] : null; $this->_deleteLink = !empty($event['delete_link']) ? $event['delete_link'] : null; $this->_ajaxLink = !empty($event['ajax_link']) ? $event['ajax_link'] : null; $this->_backgroundColor = Kronolith::backgroundColor($event); $this->_foregroundColor = Kronolith::foregroundColor($event); if (isset($event['recurrence'])) { $recurrence = new Horde_Date_Recurrence($this->start); $recurrence->setRecurType($event['recurrence']['type']); if (isset($event['recurrence']['end'])) { $recurrence->setRecurEnd(new Horde_Date($event['recurrence']['end'])); } if (isset($event['recurrence']['interval'])) { $recurrence->setRecurInterval($event['recurrence']['interval']); } if (isset($event['recurrence']['count'])) { $recurrence->setRecurCount($event['recurrence']['count']); } if (isset($event['recurrence']['days'])) { $recurrence->setRecurOnDay($event['recurrence']['days']); } if (isset($event['recurrence']['exceptions'])) { foreach ($event['recurrence']['exceptions'] as $exception) { $recurrence->addException(substr($exception, 0, 4), substr($exception, 4, 2), substr($exception, 6, 2)); } } if (isset($event['recurrence']['completions'])) { foreach ($event['recurrence']['completions'] as $completion) { $recurrence->addCompletion(substr($completion, 0, 4), substr($completion, 4, 2), substr($completion, 6, 2)); } } $this->recurrence = $recurrence; } if (isset($event['owner'])) { $this->_owner = $event['owner']; } if (isset($event['permissions'])) { $this->_permissions = $event['permissions']; } if (isset($event['variable_length'])) { $this->_variableLength = $event['variable_length']; } $this->initialized = true; $this->stored = true; }
/** * Retrieve the busy times from this event within the given timeframe. This * is trivial for non-recurring events but recurring events need to be * expanded. * * @param Horde_Date $startDate The start point. * @param Horde_Date $endDate The end point. * * @return array The list of busy times (only the start times of the event). */ public function getBusyTimes(Horde_Date $startDate, Horde_Date $endDate) { if (!$this->recurs()) { if ($startDate->compareDateTime($this->_start) > 0 || $endDate->compareDateTime($this->_start) < 0) { return array(); } return array($this->_start->timestamp()); } else { $result = array(); $next = $this->_recurrence->nextRecurrence($startDate); while ($next) { if ($endDate->compareDateTime($next) < 0) { break; } if (!$this->_recurrence->hasException($next->year, $next->month, $next->mday)) { $result[] = $next->timestamp(); } $next->mday++; $next = $this->_recurrence->nextRecurrence($next); } return $result; } }
public function testBug12869RecurrenceEndFromIcalendar() { date_default_timezone_set('Europe/Amsterdam'); $iCal = new Horde_Icalendar(); $iCal->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/bug12869.ics')); $components = $iCal->getComponents(); foreach ($components as $content) { if ($content instanceof Horde_Icalendar_Vevent) { $start = new Horde_Date($content->getAttribute('DTSTART')); $end = new Horde_Date($content->getAttribute('DTEND')); $rrule = $content->getAttribute('RRULE'); $recurrence = new Horde_Date_Recurrence($start, $end); $recurrence->fromRRule20($rrule); break; } } $after = array('year' => 2013, 'month' => 12); $after['mday'] = 11; $this->assertEquals('2013-12-12 13:45:00', (string) $recurrence->nextRecurrence($after)); $after['mday'] = 18; $this->assertEquals('2013-12-19 13:45:00', (string) $recurrence->nextRecurrence($after)); $after['mday'] = 20; $this->assertEquals('', (string) $recurrence->nextRecurrence($after)); date_default_timezone_set('Europe/Berlin'); }