public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response) { $this->_mapper = $GLOBALS['injector']->getInstance('Horde_Routes_Mapper'); $this->_matchDict = new Horde_Support_Array($this->_mapper->match($request->getPath())); $injector = $this->getInjector(); switch ($this->_matchDict->action) { case 'category': $driver = $injector->getInstance('Dolcore_Factory_Driver')->create($injector); $categories = $driver->getCategoriesApi(); $discussionApi = $driver->getDiscussionApi(); $category = $categories->getCategory($this->_matchDict->category); /* Write a category's currently running Umfragen */ $now = new Horde_Date(time()); $template = $this->getInjector()->createInstance('Horde_Template'); $template->set('updated', $now->format(DATE_ATOM)); $template->set('category_caption', $category->getCaption()); $template->set('category_id', $category->id); $discussions = array(); foreach ($discussionApi->listDiscussions(array('category' => $category->id)) as $discussion) { $discussions[$discussion->id]['title'] = $discussion->text; $discussions[$discussion->id]['details'] = $discussion->hintergrund; $discussions[$discussion->id]['modified'] = $discussion->erstelldatum; $discussions[$discussion->id]['id'] = Dolcore::getUrlFor('discussion', array('discussion_id' => $discussion->id), true, true); $discussions[$discussion->id]['url'] = Dolcore::getUrlFor('discussion', array('discussion_id' => $discussion->id), true, true); } $template->set('discussions', $discussions); $response->setBody($template->fetch(DOLCORE_TEMPLATES . '/feeds/atom.xml')); break; case 'categories': break; } }
/** * Constructor. * * @param array $config Configuration key-value pairs. */ public function __construct($config = array()) { global $prefs, $registry; parent::__construct($config); $blank = new Horde_Url(); $this->addNewButton(_("_New Event"), $blank, array('id' => 'kronolithNewEvent')); $this->newExtra = $blank->link(array_merge(array('id' => 'kronolithQuickEvent'), Horde::getAccessKeyAndTitle(_("Quick _insert"), false, true))); $sidebar = $GLOBALS['injector']->createInstance('Horde_View'); /* Minical. */ $today = new Horde_Date($_SERVER['REQUEST_TIME']); $sidebar->today = $today->format('F Y'); $sidebar->weekdays = array(); for ($i = $prefs->getValue('week_start_monday'), $c = $i + 7; $i < $c; $i++) { $weekday = Horde_Nls::getLangInfo(constant('DAY_' . ($i % 7 + 1))); $sidebar->weekdays[$weekday] = Horde_String::substr($weekday, 0, 2); } /* Calendars. */ $sidebar->newShares = $registry->getAuth() && !$prefs->isLocked('default_share'); $sidebar->admin = $registry->isAdmin(); $sidebar->resourceAdmin = $registry->isAdmin() || $GLOBALS['injector']->getInstance('Horde_Core_Perms')->hasAppPermission('resource_management'); $sidebar->resources = $GLOBALS['conf']['resources']['enabled']; $sidebar->addRemote = !$prefs->isLocked('remote_cals'); $remotes = unserialize($prefs->getValue('remote_cals')); $sidebar->showRemote = !($prefs->isLocked('remote_cals') && empty($remotes)); $this->content = $sidebar->render('dynamic/sidebar'); }
/** * * @global Horde_Prefs $prefs * @param Horde_Date $date * * @return Kronolith_View_Month */ public function __construct(Horde_Date $date) { global $prefs; $this->month = $date->month; $this->year = $date->year; // Need to calculate the start and length of the view. $this->date = new Horde_Date($date); $this->date->mday = 1; $this->_startday = $this->date->dayOfWeek(); if (!$prefs->getValue('week_start_monday')) { $this->_startOfView = 1 - $this->_startday; } else { if ($this->_startday == Horde_Date::DATE_SUNDAY) { $this->_startOfView = -5; } else { $this->_startOfView = 2 - $this->_startday; } } $startDate = new Horde_Date($this->year, $this->month, $this->_startOfView); $this->_endDate = new Horde_Date($this->year, $this->month, Horde_Date_Utils::daysInMonth($this->month, $this->year) + 1); $this->_endDate->mday += (7 - ($this->_endDate->format('w') - $prefs->getValue('week_start_monday'))) % 7; if ($prefs->getValue('show_shared_side_by_side')) { $allCalendars = Kronolith::listInternalCalendars(); $this->_currentCalendars = array(); foreach ($GLOBALS['calendar_manager']->get(Kronolith::DISPLAY_CALENDARS) as $id) { $this->_currentCalendars[$id] = $allCalendars[$id]; } } else { $this->_currentCalendars = array('internal_0' => true); } try { $this->_events = Kronolith::listEvents($startDate, $this->_endDate); } catch (Exception $e) { $GLOBALS['notification']->push($e, 'horde.error'); $this->_events = array(); } if (!is_array($this->_events)) { $this->_events = array(); } }
/** * Output a human readable description of the recurrence rule. * * @return string * @since 2.1.0 */ public function toString($date_format) { $string = ''; if ($this->hasRecurType(self::RECUR_DAILY)) { $string = _("Daily: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("day(s)"); } elseif ($this->hasRecurType(self::RECUR_WEEKLY)) { $weekdays = array(); if ($this->recurOnDay(Horde_Date::MASK_MONDAY)) { $weekdays[] = _("Monday"); } if ($this->recurOnDay(Horde_Date::MASK_TUESDAY)) { $weekdays[] = _("Tuesday"); } if ($this->recurOnDay(Horde_Date::MASK_WEDNESDAY)) { $weekdays[] = _("Wednesday"); } if ($this->recurOnDay(Horde_Date::MASK_THURSDAY)) { $weekdays[] = _("Thursday"); } if ($this->recurOnDay(Horde_Date::MASK_FRIDAY)) { $weekdays[] = _("Friday"); } if ($this->recurOnDay(Horde_Date::MASK_SATURDAY)) { $weekdays[] = _("Saturday"); } if ($this->recurOnDay(Horde_Date::MASK_SUNDAY)) { $weekdays[] = _("Sunday"); } $string = _("Weekly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("week(s) on:") . ' ' . implode(', ', $weekdays); } elseif ($this->hasRecurType(self::RECUR_MONTHLY_DATE)) { $string = _("Monthly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("month(s)") . ' ' . _("on the same date"); } elseif ($this->hasRecurType(self::RECUR_MONTHLY_WEEKDAY)) { $string = _("Monthly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("month(s)") . ' ' . _("on the same weekday"); } elseif ($this->hasRecurType(self::RECUR_MONTHLY_LAST_WEEKDAY)) { $string = _("Monthly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("month(s)") . ' ' . _("on the same last weekday"); } elseif ($this->hasRecurType(self::RECUR_YEARLY_DATE)) { $string = _("Yearly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("year(s) on the same date"); } elseif ($this->hasRecurType(self::RECUR_YEARLY_DAY)) { $string = _("Yearly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("year(s) on the same day of the year"); } elseif ($this->hasRecurType(self::RECUR_YEARLY_WEEKDAY)) { $string = _("Yearly: Recurs every") . ' ' . $this->getRecurInterval() . ' ' . _("year(s) on the same weekday and month of the year"); } $string .= "\n" . _("Ends after") . ': ' . ($this->hasRecurEnd() ? $this->recurEnd->strftime($date_format) . ($this->recurEnd->hour == 23 && $this->recurEnd->min == 59 ? '' : ' ' . $this->recurEnd->format($date_format)) : ($this->getRecurCount() ? sprintf(_("%d times"), $this->getRecurCount()) : _("No end date"))); if ($this->getExceptions()) { $string .= "\n" . _("Exceptions on") . ': '; foreach ($this->getExceptions() as $exception_date) { $string .= $this->_formatExceptionDate($exception_date, $date_format) . ' '; } } return $string; }
/** * @dataProvider timezones */ public function testFile($file) { $result = ''; $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents($file)); foreach ($ical->getComponents() as $component) { if ($component->getType() != 'vEvent') { continue; } $date = $component->getAttribute('DTSTART'); if (is_array($date)) { continue; } $result .= str_replace("\r", '', $component->getAttribute('SUMMARY')) . "\n"; $d = new Horde_Date($date); $result .= $d->format('H:i') . "\n"; } $this->assertStringEqualsFile(__DIR__ . '/fixtures/vTimezone/' . basename($file, 'ics') . 'txt', $result, 'Failed parsing file ' . basename($file)); }
/** * Return the number of hours to offset a POOMCONTACTS:BIRTHDAY * or ANNIVERSARY field in an attempt to work around a bug in the * protocol - which doesn't define a standard time for birthdays to occur. * * WP: * Devices seem to send the birthdays at the entered date, with * a time of 00:00:00 UTC during standard time and with 01:00:00 UTC * during DST if the client's configured timezone observes it. No idea * what purpose this serves since no timezone data is transmitted for * birthday values. * * iOS: * Seems different based on version. iOS 5+, at least seems to send * the birthday as midnight at the entered date in the device's timezone * then converted to UTC. Some minor issues with offsets being off an * hour or two for some timezones though. * * iOS < 5 sends the birthday time part as the time the birthday * was entered/edited on the device, converted to UTC, so it can't be * trusted at all. The best we can do here is transform the date to * midnight on date_default_timezone() converted to UTC. * * Android: * For contacts originating on the SERVER, the following is true: * * Stock 4.3 Takes the down-synched bday value which is assumed to be * UTC, does some magic to it (converts to milliseconds, creates a * gregorian calendar object, then converts to YYYY-MM-DD). When * sending the bday value up, it sends it up as-is. No conversion * to/from UTC or local is done. * * Stock 4.4.x does the above, but before sending the bday value, * validates that it's in a correct format for sending to the server. * This really only affects date data originally entered on the device * for non-stock android clients. * * There is some strange bit of code in Android that adds 1 to the * DAY_OF_MONTH when HOUR_OF_DAY >= 12 in an attempt to "fix" * birthday handling for GMT+n users. See: * https://android.googlesource.com/platform/packages/apps/Exchange/+/32daacdd71b9de8fd5e3f59c37934e3e4a9fa972%5E!/exchange2/src/com/android/exchange/adapter/ContactsSyncAdapter.java * Not sure what to make of it, or why it's not just converted to * local tz when displaying but this probably breaks birthday handling * for people in a few timezones. * * For contacts originating on the CLIENT, the datetime is sent as * 08:00:00 UTC, and this seems to be regardless of the timezone set * in the Android system. * * Given all of this, it makes sense to me to ALWAYS send birthday * data as occuring at 08:00:00 UTC for *native* Android clients. * * BB 10+ expects it at 12:00:00 UTC * * @param Horde_Date $date The date. This should normally be in the local * timezone if encoding the date for the client. * If decoding the date from the client, it will * normally be in UTC. * @param boolean $toEas Convert from local to device if true. * DEFAULT: false * * @return Horde_Date The date of the birthday/anniversary, with * any fixes applied for the current device. The * timezone set in the object will depend on the * client detected, and whether the date is being * encoding or decoding. */ public function normalizePoomContactsDates($date, $toEas = false) { switch (Horde_String::lower($this->clientType)) { case self::TYPE_WP: case 'wp8': // Legacy. Remove in H6. // Legacy. Remove in H6. case 'wp': // Legacy. Remove in H6. if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } case self::TYPE_ANDROID: // Need to protect against clients that don't send the actual Android // version in the OS field. if (stripos($this->deviceType, 'samsung') === 0) { // Samsung's native Contacts app works differently than stock // Android, always sending as 00:00:00 if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } if ($this->getMajorVersion() >= 4 && $this->getMajorVersion() <= 10) { if ($toEas) { return new Horde_Date($date->format('Y-m-d 08:00:00'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } } else { // POOMCONTACTS:BIRTHDAY not really supported in early Android // versions. Return as is. return $date; } case self::TYPE_IPAD: case self::TYPE_IPHONE: case self::TYPE_IPOD: if ($this->getMajorVersion() >= 5) { // iOS >= 5 handles it correctly more or less. if ($toEas) { return new Horde_Date($date->format('Y-m-d 00:00:00')); } else { return $date; } } else { if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } } case self::TYPE_NINE: if ($toEas) { return new Horde_Date($date->format('Y-m-d 00:00:00')); } else { return $date; } case self::TYPE_BLACKBERRY: if ($toEas) { return new Horde_Date($date->format('Y-m-d 12:00:00'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } case self::TYPE_TOUCHDOWN: case self::TYPE_UNKNOWN: default: return $date; } }
/** * Calculate the offsets for the specified transition * * @param array $offsets A TZ offset hash * @param array $transition A transition hash * @param string $type Transition type - dst or std * * @return array A populated offset hash */ protected static function _generateOffsetsForTransition(array $offsets, array $transition, $type) { // We can't use Horde_Date directly here, since it is unable to // properly convert to UTC from local ON the exact hour of a std -> dst // transition. This is due to a conversion to DateTime in the localtime // zone internally before the timezone change is applied $transitionDate = new DateTime($transition['time']); $transitionDate->setTimezone(new DateTimeZone('UTC')); $transitionDate = new Horde_Date($transitionDate); $offsets[$type . 'month'] = $transitionDate->format('n'); $offsets[$type . 'day'] = $transitionDate->format('w'); $offsets[$type . 'minute'] = (int) $transitionDate->format('i'); $offsets[$type . 'hour'] = (int) $transitionDate->format('H'); for ($i = 5; $i > 0; $i--) { if (self::_isNthOcurrenceOfWeekdayInMonth($transition['ts'], $i)) { $offsets[$type . 'week'] = $i; break; } } return $offsets; }
private function _getEvents($dh, $startDate, $endDate) { $events = array(); for ($date = new Horde_Date($startDate); $date->compareDate($endDate) <= 0; $date->mday++) { $holidays = $dh->getHolidayForDate($date->format('Y-m-d'), null, true); if (Date_Holidays::isError($holidays)) { Horde::log(sprintf('Unable to retrieve list of holidays from %s to %s', (string) $startDate, (string) $endDate), __FILE__, __LINE__); continue; } if (is_null($holidays)) { continue; } foreach ($holidays as $holiday) { $event = new Kronolith_Event_Holidays($this, $holiday); $events[] = $event; } } return $events; }
/** * 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->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; }
/** * Adds specified EXIF fields to this image's tags. * Called during image upload/creation. * * @param array $fields An array of EXIF fields to import as a tag. * * @return void */ protected function _exifToTags(array $fields = array()) { $tags = array(); foreach ($fields as $field) { if (!empty($this->_exif[$field])) { if (substr($field, 0, 8) == 'DateTime') { $d = new Horde_Date(strtotime($this->_exif[$field])); $tags[] = $d->format("Y-m-d"); } elseif ($field == 'Keywords') { $tags = array_merge($tags, explode(',', $this->_exif[$field])); } else { $tags[] = $this->_exif[$field]; } } } $this->_tags = array_merge($this->_tags, $tags); }
/** * Oh yeah. This is beautiful. Exchange outputs date fields differently in * calendar items and emails. We could just always send one or the other, * but unfortunately nokia's 'Mail for exchange' depends on this quirk. * So we have to send a different date type depending on where it's used. * Used when encoding a date value to send to the client. * * @param Horde_Date $dt The Horde_Date object to format * (should normally be in local tz). * @param integer $type The type to format as: One of * TYPE_DATE or TYPE_DATE_DASHES, TYPE_DATE_LOCAL * * @return string The formatted date * @throws InvalidArgumentException */ protected function _formatDate(Horde_Date $dt, $type) { switch ($type) { case self::TYPE_DATE: return $dt->setTimezone('UTC')->format('Ymd\\THis\\Z'); case self::TYPE_DATE_DASHES: return $dt->setTimezone('UTC')->format('Y-m-d\\TH:i:s\\.000\\Z'); case self::TYPE_DATE_LOCAL: return $dt->format('Y-m-d\\TH:i:s\\.000\\Z'); default: throw new InvalidArgumentException('Unidentified DATE_TYPE'); } }
/** * @return string The time range of the event ("All Day", "1:00pm-3:00pm", * "08:00-22:00"). */ public function getTimeRange() { if ($this->isAllDay()) { return _("All day"); } elseif (($cmp = $this->start->compareDate($this->end)) > 0) { $df = $GLOBALS['prefs']->getValue('date_format'); if ($cmp > 0) { return $this->end->strftime($df) . '-' . $this->start->strftime($df); } else { return $this->start->strftime($df) . '-' . $this->end->strftime($df); } } else { $twentyFour = $GLOBALS['prefs']->getValue('twentyFour'); return $this->start->format($twentyFour ? 'G:i' : 'g:ia') . '-' . $this->end->format($twentyFour ? 'G:i' : 'g:ia'); } }
$desc = $event->isPrivate() ? '' : htmlspecialchars($event->description); if (strlen($desc)) { $desc .= '<br /><br />'; } /* Time. */ $desc .= _("When:") . ' ' . $event->start->strftime($prefs->getValue('date_format')) . ' ' . $event->start->format($twentyFour ? 'H:i' : 'H:ia') . _(" to "); if ($event->start->compareDate($event->end->timestamp()) == 0) { $desc .= $event->end->format($twentyFour ? 'H:i' : 'h:ia'); } else { $desc .= $event->end->strftime($prefs->getValue('date_format')) . ' ' . $event->end->format($twentyFor ? 'H:i' : 'h:ia'); } /* Attendees. */ if (!$event->isPrivate()) { $attendees = Kronolith::getAttendeeEmailList($event->attendees); if (count($attendees)) { $desc .= '<br />' . _("Who:") . ' ' . htmlspecialchars(strval($attendees)); } if (strlen($event->location)) { $desc .= '<br />' . _("Where:") . ' ' . htmlspecialchars($event->location); } } $desc .= '<br />' . _("Event Status:") . ' ' . Kronolith::statusToString($event->status); $entries[$id]['title'] = htmlspecialchars($event->getTitle()); $entries[$id]['desc'] = htmlspecialchars($desc); $entries[$id]['url'] = htmlspecialchars(Horde::url($event->getViewUrl(), true, -1)); $entries[$id]['modified'] = $modified->format(DATE_ATOM); } } $template->set('entries', $entries, true); $browser->downloadHeaders($calendar . '.xml', 'text/xml', true); echo $template->fetch(KRONOLITH_TEMPLATES . '/feeds/' . $feed_type . '.xml');
/** * Finds the next recurrence of this event that's after $afterDate. * * @param Horde_Date|string $after Return events after this date. * * @return Horde_Date|boolean The date of the next recurrence or false * if the event does not recur after * $afterDate. */ public function nextRecurrence($after) { if (!$after instanceof Horde_Date) { $after = new Horde_Date($after); } else { $after = clone $after; } // Make sure $after and $this->start are in the same TZ $after->setTimezone($this->start->timezone); if ($this->start->compareDateTime($after) >= 0) { return clone $this->start; } if ($this->recurInterval == 0 && empty($this->rdates)) { return false; } switch ($this->getRecurType()) { case self::RECUR_DAILY: $diff = $this->start->diff($after); $recur = ceil($diff / $this->recurInterval); if ($this->recurCount && $recur >= $this->recurCount) { return false; } $recur *= $this->recurInterval; $next = $this->start->add(array('day' => $recur)); if ((!$this->hasRecurEnd() || $next->compareDateTime($this->recurEnd) <= 0) && $next->compareDateTime($after) >= 0) { return $next; } break; case self::RECUR_WEEKLY: if (empty($this->recurData)) { return false; } $start_week = Horde_Date_Utils::firstDayOfWeek($this->start->format('W'), $this->start->year); $start_week->timezone = $this->start->timezone; $start_week->hour = $this->start->hour; $start_week->min = $this->start->min; $start_week->sec = $this->start->sec; // Make sure we are not at the ISO-8601 first week of year while // still in month 12...OR in the ISO-8601 last week of year while // in month 1 and adjust the year accordingly. $week = $after->format('W'); if ($week == 1 && $after->month == 12) { $theYear = $after->year + 1; } elseif ($week >= 52 && $after->month == 1) { $theYear = $after->year - 1; } else { $theYear = $after->year; } $after_week = Horde_Date_Utils::firstDayOfWeek($week, $theYear); $after_week->timezone = $this->start->timezone; $after_week_end = clone $after_week; $after_week_end->mday += 7; $diff = $start_week->diff($after_week); $interval = $this->recurInterval * 7; $repeats = floor($diff / $interval); if ($diff % $interval < 7) { $recur = $diff; } else { /** * If the after_week is not in the first week interval the * search needs to skip ahead a complete interval. The way it is * calculated here means that an event that occurs every second * week on Monday and Wednesday with the event actually starting * on Tuesday or Wednesday will only have one incidence in the * first week. */ $recur = $interval * ($repeats + 1); } if ($this->hasRecurCount()) { $recurrences = 0; /** * Correct the number of recurrences by the number of events * that lay between the start of the start week and the * recurrence start. */ $next = clone $start_week; while ($next->compareDateTime($this->start) < 0) { if ($this->recurOnDay((int) pow(2, $next->dayOfWeek()))) { $recurrences--; } ++$next->mday; } if ($repeats > 0) { $weekdays = $this->recurData; $total_recurrences_per_week = 0; while ($weekdays > 0) { if ($weekdays % 2) { $total_recurrences_per_week++; } $weekdays = ($weekdays - $weekdays % 2) / 2; } $recurrences += $total_recurrences_per_week * $repeats; } } $next = clone $start_week; $next->mday += $recur; while ($next->compareDateTime($after) < 0 && $next->compareDateTime($after_week_end) < 0) { if ($this->hasRecurCount() && $next->compareDateTime($after) < 0 && $this->recurOnDay((int) pow(2, $next->dayOfWeek()))) { $recurrences++; } ++$next->mday; } if ($this->hasRecurCount() && $recurrences >= $this->recurCount) { return false; } if (!$this->hasRecurEnd() || $next->compareDateTime($this->recurEnd) <= 0) { if ($next->compareDateTime($after_week_end) >= 0) { return $this->nextRecurrence($after_week_end); } while (!$this->recurOnDay((int) pow(2, $next->dayOfWeek())) && $next->compareDateTime($after_week_end) < 0) { ++$next->mday; } if (!$this->hasRecurEnd() || $next->compareDateTime($this->recurEnd) <= 0) { if ($next->compareDateTime($after_week_end) >= 0) { return $this->nextRecurrence($after_week_end); } else { return $next; } } } break; case self::RECUR_MONTHLY_DATE: $start = clone $this->start; if ($after->compareDateTime($start) < 0) { $after = clone $start; } else { $after = clone $after; } // If we're starting past this month's recurrence of the event, // look in the next month on the day the event recurs. if ($after->mday > $start->mday) { ++$after->month; $after->mday = $start->mday; } // Adjust $start to be the first match. $offset = $after->month - $start->month + ($after->year - $start->year) * 12; $offset = floor(($offset + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval; if ($this->recurCount && $offset / $this->recurInterval >= $this->recurCount) { return false; } $start->month += $offset; $count = $offset / $this->recurInterval; do { if ($this->recurCount && $count++ >= $this->recurCount) { return false; } // Bail if we've gone past the end of recurrence. if ($this->hasRecurEnd() && $this->recurEnd->compareDateTime($start) < 0) { return false; } if ($start->isValid()) { return $start; } // If the interval is 12, and the date isn't valid, then we // need to see if February 29th is an option. If not, then the // event will _never_ recur, and we need to stop checking to // avoid an infinite loop. if ($this->recurInterval == 12 && ($start->month != 2 || $start->mday > 29)) { return false; } // Add the recurrence interval. $start->month += $this->recurInterval; } while (true); break; case self::RECUR_MONTHLY_WEEKDAY: // Start with the start date of the event. $estart = clone $this->start; // What day of the week, and week of the month, do we recur on? if (isset($this->recurNthDay)) { $nth = $this->recurNthDay; $weekday = log($this->recurData, 2); } else { $nth = ceil($this->start->mday / 7); $weekday = $estart->dayOfWeek(); } // Adjust $estart to be the first candidate. $offset = $after->month - $estart->month + ($after->year - $estart->year) * 12; $offset = floor(($offset + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval; // Adjust our working date until it's after $after. $estart->month += $offset - $this->recurInterval; $count = $offset / $this->recurInterval; do { if ($this->recurCount && $count++ >= $this->recurCount) { return false; } $estart->month += $this->recurInterval; $next = clone $estart; $next->setNthWeekday($weekday, $nth); if ($next->compareDateTime($after) < 0) { // We haven't made it past $after yet, try again. continue; } if ($this->hasRecurEnd() && $next->compareDateTime($this->recurEnd) > 0) { // We've gone past the end of recurrence; we can give up // now. return false; } // We have a candidate to return. break; } while (true); return $next; case self::RECUR_YEARLY_DATE: // Start with the start date of the event. $estart = clone $this->start; $after = clone $after; if ($after->month > $estart->month || $after->month == $estart->month && $after->mday > $estart->mday) { ++$after->year; $after->month = $estart->month; $after->mday = $estart->mday; } // Seperate case here for February 29th if ($estart->month == 2 && $estart->mday == 29) { while (!Horde_Date_Utils::isLeapYear($after->year)) { ++$after->year; } } // Adjust $estart to be the first candidate. $offset = $after->year - $estart->year; if ($offset > 0) { $offset = floor(($offset + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval; $estart->year += $offset; } // We've gone past the end of recurrence; give up. if ($this->recurCount && $offset >= $this->recurCount) { return false; } if ($this->hasRecurEnd() && $this->recurEnd->compareDateTime($estart) < 0) { return false; } return $estart; case self::RECUR_YEARLY_DAY: // Check count first. $dayofyear = $this->start->dayOfYear(); $count = ($after->year - $this->start->year) / $this->recurInterval + 1; if ($this->recurCount && ($count > $this->recurCount || $count == $this->recurCount && $after->dayOfYear() > $dayofyear)) { return false; } // Start with a rough interval. $estart = clone $this->start; $estart->year += floor($count - 1) * $this->recurInterval; // Now add the difference to the required day of year. $estart->mday += $dayofyear - $estart->dayOfYear(); // Add an interval if the estimation was wrong. if ($estart->compareDate($after) < 0) { $estart->year += $this->recurInterval; $estart->mday += $dayofyear - $estart->dayOfYear(); } // We've gone past the end of recurrence; give up. if ($this->hasRecurEnd() && $this->recurEnd->compareDateTime($estart) < 0) { return false; } return $estart; case self::RECUR_YEARLY_WEEKDAY: // Start with the start date of the event. $estart = clone $this->start; // What day of the week, and week of the month, do we recur on? if (isset($this->recurNthDay)) { $nth = $this->recurNthDay; $weekday = log($this->recurData, 2); } else { $nth = ceil($this->start->mday / 7); $weekday = $estart->dayOfWeek(); } // Adjust $estart to be the first candidate. $offset = floor(($after->year - $estart->year + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval; // Adjust our working date until it's after $after. $estart->year += $offset - $this->recurInterval; $count = $offset / $this->recurInterval; do { if ($this->recurCount && $count++ >= $this->recurCount) { return false; } $estart->year += $this->recurInterval; $next = clone $estart; $next->setNthWeekday($weekday, $nth); if ($next->compareDateTime($after) < 0) { // We haven't made it past $after yet, try again. continue; } if ($this->hasRecurEnd() && $next->compareDateTime($this->recurEnd) > 0) { // We've gone past the end of recurrence; we can give up // now. return false; } // We have a candidate to return. break; } while (true); return $next; } // fall-back to RDATE properties if (!empty($this->rdates)) { $next = clone $this->start; foreach ($this->rdates as $rdate) { $next->year = $rdate->year; $next->month = $rdate->month; $next->mday = $rdate->mday; if ($next->compareDateTime($after) > 0) { return $next; } } } // We didn't find anything, the recurType was bad, or something else // went wrong - return false. return false; }
/** * @throws Nag_Exception */ public function download(Horde_Variables $vars) { global $display_tasklists, $injector, $registry; switch ($vars->actionID) { case 'export': $tasklists = $vars->get('exportList', $display_tasklists); if (!is_array($tasklists)) { $tasklists = array($tasklists); } /* Get the full, sorted task list. */ $tasks = Nag::listTasks(array('tasklists' => $tasklists, 'completed' => $vars->exportTasks, 'include_tags' => true, 'include_history' => false)); $tasks->reset(); switch ($vars->exportID) { case Horde_Data::EXPORT_CSV: $data = array(); while ($task = $tasks->each()) { $task = $task->toHash(); $task['desc'] = str_replace(',', '', $task['desc']); $task['tags'] = implode(',', $task['tags']); unset($task['complete_link'], $task['delete_link'], $task['edit_link'], $task['parent'], $task['task_id'], $task['tasklist_id'], $task['view_link'], $task['recurrence'], $task['methods']); foreach (array('start', 'due', 'completed_date') as $field) { if (!empty($task[$field])) { $date = new Horde_Date($task[$field]); $task[$field] = $date->format('c'); } } $data[] = $task; } $injector->getInstance('Horde_Core_Factory_Data')->create('Csv', array('cleanup' => array($this, 'cleanupData')))->exportFile(_("tasks.csv"), $data, true); exit; case Horde_Data::EXPORT_ICALENDAR: $iCal = new Horde_Icalendar(); $iCal->setAttribute('PRODID', '-//The Horde Project//Nag ' . $registry->getVersion() . '//EN'); while ($task = $tasks->each()) { $iCal->addComponent($task->toiCalendar($iCal)); } return array('data' => $iCal->exportvCalendar(), 'name' => _("tasks.ics"), 'type' => 'text/calendar'); } } }
/** */ protected function _content() { global $prefs; if (isset($this->_params['calendar']) && $this->_params['calendar'] != '__all') { $calendars = Kronolith::listCalendars(); if (!isset($calendars[$this->_params['calendar']])) { return _("Calendar not found"); } if (!$calendars[$this->_params['calendar']]->hasPermission(Horde_Perms::READ)) { return _("Permission Denied"); } } $year = date('Y'); $month = date('m'); $startday = new Horde_Date(array('mday' => 1, 'month' => $month, 'year' => $year)); $startday = $startday->dayOfWeek(); if (!$prefs->getValue('week_start_monday')) { $startOfView = 1 - $startday; $endday = new Horde_Date(array('mday' => Horde_Date_Utils::daysInMonth($month, $year), 'month' => $month, 'year' => $year)); } else { if ($startday == Horde_Date::DATE_SUNDAY) { $startOfView = -5; } else { $startOfView = 2 - $startday; } } $startDate = new Horde_Date($year, $month, $startOfView); $endDate = new Horde_Date($year, $month, Horde_Date_Utils::daysInMonth($month, $year) + 1); $endDate->mday += (7 - ($endDate->format('w') - $prefs->getValue('week_start_monday'))) % 7; /* Table start. and current month indicator. */ $html = '<table cellspacing="1" class="monthgrid" width="100%"><tr>'; /* Set up the weekdays. */ $weekdays = array(_("Mo"), _("Tu"), _("We"), _("Th"), _("Fr"), _("Sa")); if (!$prefs->getValue('week_start_monday')) { array_unshift($weekdays, _("Su")); } else { $weekdays[] = _("Su"); } foreach ($weekdays as $weekday) { $html .= '<th class="item">' . $weekday . '</th>'; } try { if (isset($this->_params['calendar']) && $this->_params['calendar'] != '__all') { list($type, $calendar) = explode('_', $this->_params['calendar'], 2); $driver = Kronolith::getDriver($type, $calendar); $all_events = $driver->listEvents($startDate, $endDate, array('show_recurrence' => true)); } else { $all_events = Kronolith::listEvents($startDate, $endDate, $GLOBALS['calendar_manager']->get(Kronolith::DISPLAY_CALENDARS)); } } catch (Exception $e) { return '<em>' . $e->getMessage() . '</em>'; } $weekday = 0; $week = -1; $weekStart = $prefs->getValue('week_start_monday'); for ($date_ob = new Kronolith_Day($month, $startOfView, $year); $date_ob->compareDate($endDate) < 0; $date_ob->mday++) { if ($weekday == 7) { $weekday = 0; } if ($weekday == 0) { ++$week; $html .= '</tr><tr>'; } if ($date_ob->isToday()) { $td_class = 'kronolith-today'; } elseif ($date_ob->month != $month) { $td_class = 'kronolith-othermonth'; } elseif ($date_ob->dayOfWeek() == 0 || $date_ob->dayOfWeek() == 6) { $td_class = 'kronolith-weekend'; } else { $td_class = ''; } $html .= '<td align="center" class="' . $td_class . '">'; /* Set up the link to the day view. */ $url = Horde::url('day.php', true)->add('date', $date_ob->dateString()); if (isset($this->_params['calendar']) && $this->_params['calendar'] != '__all') { $url->add('display_cal', $this->_params['calendar']); } $date_stamp = $date_ob->dateString(); if (empty($all_events[$date_stamp])) { /* No events, plain link to the day. */ $cell = Horde::linkTooltip($url, _("View Day")) . $date_ob->mday . '</a>'; } else { /* There are events; create a cell with tooltip to * list them. */ $day_events = ''; foreach ($all_events[$date_stamp] as $event) { if ($event->isAllDay()) { $day_events .= _("All day"); } else { $day_events .= $event->start->strftime($prefs->getValue('twentyFour') ? '%R' : '%I:%M%p') . '-' . $event->end->strftime($prefs->getValue('twentyFour') ? '%R' : '%I:%M%p'); } $location = $event->getLocation(); $day_events .= ':' . ($location ? ' (' . htmlspecialchars($location) . ')' : '') . ' ' . $event->getTitle() . "\n"; } $cell = Horde::linkTooltip($url, _("View Day"), '', '', '', $day_events) . $date_ob->mday . '</a>'; } /* Bold the cell if there are events. */ if (!empty($all_events[$date_stamp])) { $cell = '<strong>' . $cell . '</strong>'; } $html .= $cell . '</td>'; ++$weekday; } return $html . '</tr></table>'; }
/** * Adds recurrence information to the value hash for SQL * INSERT/UPDATE queries. * * @param array $values The fields to update. * @param array $task The task information. */ protected function _addRecurrenceFields(&$values, $task) { if (!$task['recurrence']) { $values[] = 0; for ($i = 0; $i < 6; $i++) { $values[] = null; } } else { $recurrence = $task['recurrence']; $recur = $recurrence->getRecurType(); if ($recurrence->hasRecurEnd()) { $recur_end = clone $recurrence->recurEnd; $recur_end->setTimezone('UTC'); } else { $recur_end = new Horde_Date(array('year' => 9999, 'month' => 12, 'mday' => 31, 'hour' => 23, 'min' => 59, 'sec' => 59)); } $values[] = $recur; $values[] = $recurrence->getRecurInterval(); $values[] = $recur_end->format('Y-m-d H:i:s'); $values[] = $recurrence->getRecurCount(); switch ($recur) { case Horde_Date_Recurrence::RECUR_WEEKLY: $values[] = $recurrence->getRecurOnDays(); break; default: $values[] = null; break; } $values[] = implode(',', $recurrence->getExceptions()); $values[] = implode(',', $recurrence->getCompletions()); } }
/** * Return the number of hours to offset a POOMCONTACTS:BIRTHDAY * or ANNIVERSARY field in an attempt to work around a bug in the * protocol - which doesn't define a standard time for birthdays to occur. * * @param Horde_Date $date The date. * @param boolean $toEas Convert from local to device if true. * * @return Horde_Date The date of the birthday/anniversary, in UTC, with * any fixes applied for the current device. */ public function normalizePoomContactsDates($date, $toEas = false) { // WP devices seem to send the birthdays at the entered date, with // a time of 00:00:00 UTC. // // iOS seems different based on version. iOS 5+, at least seems to send // the birthday as midnight at the entered date in the device's timezone // then converted to UTC. Some minor issues with offsets being off an // hour or two for some timezones though. // // iOS < 5 sends the birthday time part as the time the birthday // was entered/edited on the device, converted to UTC, so it can't be // trusted at all. The best we can do here is transform the date to // midnight on date_default_timezone() converted to UTC. // // Native Android 4 ALWAYS sends it as 08:00:00 UTC // // BB 10+ expects it at 12:00:00 UTC switch (strtolower($this->clientType)) { case self::TYPE_WP: case 'wp8': // Legacy. Remove in H6. // Legacy. Remove in H6. case 'wp': // Legacy. Remove in H6. if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } case self::TYPE_ANDROID: if ($this->getMajorVersion() >= 4) { if ($toEas) { return new Horde_Date($date->format('Y-m-d 08:00:00'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } } else { // POOMCONTACTS:BIRTHDAY not really supported in early Android // versions. Return as is. return $date; } case self::TYPE_IPAD: case self::TYPE_IPHONE: case self::TYPE_IPOD: if ($this->getMajorVersion() >= 5) { // iOS >= 5 handles it correctly more or less. return $date; } else { if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } } case self::TYPE_BLACKBERRY: if ($toEas) { return new Horde_Date($date->format('Y-m-d 12:00:00'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } case self::TYPE_TOUCHDOWN: case self::TYPE_UNKNOWN: default: return $date; } }
/** * Converts an UTC timestamp like "20061222T110000Z" into a local * timestamp like "20061222T130000" using the server timezone. * * @param array $utc Array with a datetime string in UTC. * * @return string The datetime string converted to the local timezone. */ protected function _convertUTC2LocalTime($utc) { $date = new Horde_Date($utc[0]); $date->setTimezone(date_default_timezone_get()); return $date->format("Ymd\\THis"); }
/** * Convert an ActiveSync contact message into a hash suitable for * importing via self::add(). * * @param Horde_ActiveSync_Message_Contact $message The contact message * object. * * @return array A contact hash. */ public function fromASContact(Horde_ActiveSync_Message_Contact $message) { $hash = array(); foreach (self::$_asMap as $turbaField => $asField) { if (!$message->isGhosted($asField)) { try { $hash[$turbaField] = $message->{$asField}; } catch (InvalidArgumentException $e) { } } } /* Requires special handling */ try { if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($message->airsyncbasebody)) { $hash['notes'] = $message->airsyncbasebody->data; } } else { $hash['notes'] = $message->body; } } catch (InvalidArgumentException $e) { } // picture ($message->picture *should* already be base64 encdoed) if (!$message->isGhosted('picture')) { $hash['photo'] = base64_decode($message->picture); } /* Email addresses */ $hash['emails'] = array(); if (!$message->isGhosted('email1address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email1address); $hash['emails'][] = $hash['email'] = $e ? $e : ''; } if (!$message->isGhosted('email2address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email2address); $hash['emails'][] = $hash['homeEmail'] = $e ? $e : ''; } if (!$message->isGhosted('email3address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email3address); $hash['emails'][] = $hash['workEmail'] = $e ? $e : ''; } $hash['emails'] = implode(',', $hash['emails']); /* Categories */ if (is_array($message->categories) && count($message->categories)) { $hash['__tags'] = $message->categories; } /* Children */ if (is_array($message->children) && count($message->children)) { // We use a comma as incoming delimiter as it's the most // common even though it might be used withing a name string. $hash['children'] = implode(', ', $message->children); } elseif (!$message->isGhosted('children')) { $hash['children'] = ''; } /* Birthday and Anniversary */ if (!empty($message->birthday)) { $bday = new Horde_Date($message->birthday); $bday->setTimezone(date_default_timezone_get()); $hash['birthday'] = $bday->format('Y-m-d'); } elseif (!$message->isGhosted('birthday')) { $hash['birthday'] = ''; } if (!empty($message->anniversary)) { $anniversary = new Horde_Date($message->anniversary); $anniversary->setTimezone(date_default_timezone_get()); $hash['anniversary'] = $anniversary->format('Y-m-d'); } elseif (!$message->isGhosted('anniversary')) { $hash['anniversary'] = ''; } /* Countries */ include 'Horde/Nls/Countries.php'; if (!empty($message->homecountry)) { if (!empty($this->map['homeCountryFree'])) { $hash['homeCountryFree'] = $message->homecountry; } else { $country = array_search($message->homecountry, $countries); if ($country === false) { $country = $message->homecountry; } $hash['homeCountry'] = $country; } } elseif (!$message->isGhosted('homecountry')) { $hash['homeCountry'] = ''; } if (!empty($message->businesscountry)) { if (!empty($this->map['workCountryFree'])) { $hash['workCountryFree'] = $message->businesscountry; } else { $country = array_search($message->businesscountry, $countries); if ($country === false) { $country = $message->businesscountry; } $hash['workCountry'] = $country; } } elseif (!$message->isGhosted('businesscountry')) { $hash['workCountry'] = ''; } if (!empty($message->othercountry)) { if (!empty($this->map['otherCountryFree'])) { $hash['otherCountryFree'] = $message->othercountry; } else { $country = array_search($message->othercountry, $countries); if ($country === false) { $country = $message->othercountry; } $hash['otherCountry'] = $country; } } elseif (!$message->isGhosted('othercountry')) { $hash['otherCountry'] = ''; } return $hash; }
public function UTC2LocalDate($s) { $date = new Horde_Date($s); $date->setTimezone(date_default_timezone_get()); return $date->format('Ymd') . 'T000000'; }
/** * Adds rules from this ruleset to a VTIMEZONE component. * * @param Horde_Icalendar_Vtimezone $tz A VTIMEZONE component. * @param string $tzid The timezone ID of the component. * @param string $name A timezone name abbreviation. * May contain a placeholder that is * replaced the Rules' "Letter(s)" * entry. * @param array $startOffset An offset hash describing the * base offset of a timezone. * @param Horde_Date $start Start of the period to add rules * for. * @param Horde_Date $end End of the period to add rules * for. */ public function addRules(Horde_Icalendar_Vtimezone $tz, $tzid, $name, $startOffset, Horde_Date $start, Horde_Date $end = null) { $offset = $startOffset; foreach ($this->_rules as $rule) { $year = $rule[3]; if ($year[0] == 'o') { // TO is "only" $rule[3] = $rule[2]; } if ($rule[3][0] != 'm' && $rule[3] < $start->year) { // TO is not maximum and is before the searched period continue; } if ($end && $rule[2][0] != 'm' && $rule[2] > $end->year) { // FROM is not "minimum" and is after the searched period break; } if ($rule[2][0] != 'm' && $rule[2] < $start->year) { $rule[2] = $start->year; } if ($rule[8] == 0) { $component = new Horde_Icalendar_Standard(); $component->setAttribute('TZOFFSETFROM', $offset); $component->setAttribute('TZOFFSETTO', $startOffset); $offset = $startOffset; } else { $component = new Horde_Icalendar_Daylight(); $component->setAttribute('TZOFFSETFROM', $offset); $offset = $this->_getOffset($startOffset, $rule[8]); $component->setAttribute('TZOFFSETTO', $offset); } $month = Horde_Timezone::getMonth($rule[5]); // Retrieve time of rule start. preg_match('/(\\d+)(?::(\\d+))?(?::(\\d+))?(w|s|u)?/', $rule[7], $match); if (!isset($match[2])) { $match[2] = 0; } if ($rule[2] == $rule[3] && preg_match('/^\\d+$/', $rule[6])) { // Rule lasts only for a single year and starts on a specific // date. $rdate = new Horde_Date(array('year' => $rule[2], 'month' => Horde_Timezone::getMonth($rule[5]), 'mday' => $rule[6], 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); $component->setAttribute('DTSTART', $rdate); } elseif (substr($rule[6], 0, 4) == 'last') { // Rule starts on the last of a certain weekday of the month. $weekday = $this->_weekdays[substr($rule[6], 4, 3)]; $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $component->setAttribute('DTSTART', $last); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYDAY=-1' . Horde_String::upper(substr($rule[6], 4, 2)) . ';BYMONTH=' . $month . $until); } elseif (strpos($rule[6], '>=')) { // Rule starts on a certain weekday after a certain day of // month. list($weekday, $day) = explode('>=', $rule[6]); $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)]; $first = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($first->dayOfWeek() != $weekdayInt) { $first->mday++; } $component->setAttribute('DTSTART', $first); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday++; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } for ($days = array(), $i = $day, $lastDay = min(Horde_Date_Utils::daysInMonth($month, $rule[2]), $i + 6); $day > 1 && $i <= $lastDay; $i++) { $days[] = $i; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ($days ? ';BYMONTHDAY=' . implode(',', $days) : '') . ';BYDAY=1' . Horde_String::upper(substr($weekday, 0, 2)) . $until); } elseif (strpos($rule[6], '<=')) { // Rule starts on a certain weekday before a certain day of // month. list($weekday, $day) = explode('>=', $rule[6]); $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)]; $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($last->dayOfWeek() != $weekdayInt) { $last->mday--; } $component->setAttribute('DTSTART', $last); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } for ($days = array(), $i = 1; $i <= $day; $i++) { $days[] = $i; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ';BYMONTHDAY=' . implode(',', $days) . ';BYDAY=-1' . Horde_String::upper(substr($weekday, 0, 2)) . $until); } $component->setAttribute('TZNAME', sprintf($name, $rule[9])); $tz->addComponent($component); } }
/** * Deletes an event identified by UID. * * @param string|array $uid A single UID or an array identifying the * event(s) to delete. * * @param string $recurrenceId The reccurenceId for the event instance, if * this is a deletion of a recurring event * instance ($uid must not be an array). * @param string $range The range value if deleting a recurring * event instance. Only supported values are * null or Kronolith::RANGE_THISANDFUTURE. * @since 4.1.5 * * @throws Kronolith_Exception */ public function delete($uid, $recurrenceId = null, $range = null) { // Handle an array of UIDs for convenience of deleting multiple events // at once. if (is_array($uid)) { foreach ($uid as $g) { $this->delete($g); } return; } $kronolith_driver = Kronolith::getDriver(); $events = $kronolith_driver->getByUID($uid, null, true); $event = null; // First try the user's own calendars. if (empty($event)) { $ownerCalendars = Kronolith::listInternalCalendars(true, Horde_Perms::DELETE); foreach ($events as $ev) { if (isset($ownerCalendars[$ev->calendar])) { $kronolith_driver->open($ev->calendar); $event = $ev; break; } } } // If not successful, try all calendars the user has access to. if (empty($event)) { $deletableCalendars = Kronolith::listInternalCalendars(false, Horde_Perms::DELETE); foreach ($events as $ev) { if (isset($deletableCalendars[$ev->calendar])) { $kronolith_driver->open($ev->calendar); $event = $ev; break; } } } // Are we an admin cleaing up user data? if (empty($event) && $GLOBALS['registry']->isAdmin()) { $event = $events[0]; } if (empty($event)) { throw new Horde_Exception_PermissionDenied(); } if ($recurrenceId && $event->recurs() && empty($range)) { $deleteDate = new Horde_Date($recurrenceId); $event->recurrence->addException($deleteDate->format('Y'), $deleteDate->format('m'), $deleteDate->format('d')); $event->save(); } elseif ($range == Kronolith::RANGE_THISANDFUTURE) { // Deleting the instance and remaining series. $instance = new Horde_Date($recurrenceId); $recurEnd = clone $instance; $recurEnd->mday--; if ($event->end->compareDate($recurEnd) > 0) { $kronolith_driver->deleteEvent($event->id); } else { $event->recurrence->setRecurEnd($recurEnd); $result = $event->save(); } } elseif ($recurrenceId) { throw new Kronolith_Exception(_("Unable to delete event. An exception date was provided but the event does not seem to be recurring.")); } else { $kronolith_driver->deleteEvent($event->id); } }
public function vevent2sif($vcard) { /* Some special handling for all-day vEvents that are not passed * as TYPE=DATE (TYPE=DATE does not exist for vCalendar 1.0) */ if (preg_match('/(\\r\\n|\\r|\\n)DTSTART:.*T000000(\\r\\n|\\r|\\n)/', $vcard)) { if (preg_match('/(\\r\\n|\\r|\\n)DTEND:(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)T235959(\\r\\n|\\r|\\n)/', $vcard, $m)) { $vcard = preg_replace('/(\\r\\n|\\r|\\n)DTSTART:(.*)T000000(\\r\\n|\\r|\\n)/', "\$1DTSTART;VALUE=DATE:\$2\$3", $vcard); $vcard = preg_replace('/(\\r\\n|\\r|\\n)DTEND:(.*)T235959(\\r\\n|\\r|\\n)/', "\$1DTEND;VALUE=DATE:\$2\$3", $vcard); } // @TODO: else: handle case with DTEND= T240000 } $iCal = new Horde_Icalendar(); if (!$iCal->parsevCalendar($vcard)) { // @TODO: NEVER use die() in a library. die("There was an error importing the data."); } $components = $iCal->getComponents(); switch (count($components)) { case 0: // @TODO: NEVER use die() in a library. die("No data was found."); case 1: $content = $components[0]; break; default: // @TODO: NEVER use die() in a library. die("Multiple components found; only one is supported."); } $hash = array('ReminderSet' => 0, 'IsRecurring' => 0, 'BusyStatus' => 2); $alarm = $end = null; $start = $content->getAttribute('DTSTART'); $start_params = $content->getAttribute('DTSTART', true); if (!empty($start_params[0]['VALUE']) && $start_params[0]['VALUE'] == 'DATE') { $hash['AllDayEvent'] = 1; $hash['Start'] = sprintf('%04d-%02d-%02d', $start['year'], $start['month'], $start['mday']); $start = mktime(0, 0, 0, $start['month'], $start['mday'], $start['year']); } else { $hash['AllDayEvent'] = 0; $hash['Start'] = Horde_Icalendar::_exportDateTime($start); } foreach ($content->getAllAttributes() as $item) { $GLOBALS['backend']->logMessage(sprintf('Sync4j for name %s, value %s', $item['name'], is_string($item['value']) ? $item['value'] : var_export($item['value'], true)), 'DEBUG'); switch (Horde_String::upper($item['name'])) { case 'DTSTART': break; case 'DTEND': if (!empty($item['params']['VALUE']) && $item['params']['VALUE'] == 'DATE') { $hash['AllDayEvent'] = 1; $date = new Horde_Date($item['value']['year'], $item['value']['month'], $item['value']['mday']); $date->mday--; $hash['End'] = $date->format('Y-m-d'); $end = $date->datestamp(); } else { $hash['AllDayEvent'] = 0; $hash['End'] = Horde_Icalendar::_exportDateTime($item['value']); $end = $item['value']; } break; case 'SUMMARY': $hash['Subject'] = $item['value']; break; case 'DESCRIPTION': $hash['Body'] = $item['value']; break; case 'LOCATION': $hash['Location'] = $item['value']; break; case 'CATEGORIES': $hash['Categories'] = $item['value']; break; case 'AALARM': $hash['ReminderSet'] = 1; $alarm = $item['value']; break; case 'STATUS': switch (Horde_String::upper($item['value'])) { case 'FREE': case 'CANCELLED': $hash['BusyStatus'] = 0; break; case 'TENTATIVE': $hash['BusyStatus'] = 1; break; case 'CONFIRMED': $hash['BusyStatus'] = 2; break; } break; case 'CLASS': switch (Horde_String::upper($item['value'])) { case 'PUBLIC': $hash['Sensitivity'] = 0; break; case 'PRIVATE': $hash['Sensitivity'] = 2; break; case 'CONFIDENTIAL': $hash['Sensitivity'] = 3; break; } break; case 'RRULE': // Parse the recurrence rule into keys and values. $rdata = array(); $parts = explode(';', $item['value']); foreach ($parts as $part) { list($key, $value) = explode('=', $part, 2); $rdata[Horde_String::upper($key)] = $value; } if (!isset($rdata['FREQ'])) { break; } $hash['IsRecurring'] = 1; if (isset($rdata['BYDAY'])) { $maskdays = array('SU' => Horde_Date::MASK_SUNDAY, 'MO' => Horde_Date::MASK_MONDAY, 'TU' => Horde_Date::MASK_TUESDAY, 'WE' => Horde_Date::MASK_WEDNESDAY, 'TH' => Horde_Date::MASK_THURSDAY, 'FR' => Horde_Date::MASK_FRIDAY, 'SA' => Horde_Date::MASK_SATURDAY); $days = explode(',', $rdata['BYDAY']); $mask = 0; foreach ($days as $day) { $instance = (int) $day; $mask |= $maskdays[str_replace($instance, '', $day)]; } } $hash['Interval'] = isset($rdata['INTERVAL']) ? $rdata['INTERVAL'] : 1; switch (Horde_String::upper($rdata['FREQ'])) { case 'DAILY': $hash['RecurrenceType'] = 0; break; case 'WEEKLY': $hash['RecurrenceType'] = 1; if (isset($rdata['BYDAY'])) { $hash['DayOfWeekMask'] = $mask; } break; case 'MONTHLY': if (isset($rdata['BYDAY'])) { $hash['RecurrenceType'] = 3; $hash['Instance'] = $instance; $hash['DayOfWeekMask'] = $mask; } else { $hash['RecurrenceType'] = 2; $hash['DayOfMonth'] = date('j', $start); } break; case 'YEARLY': if (isset($rdata['BYDAY'])) { $hash['RecurrenceType'] = 6; $hash['Instance'] = $instance; $hash['DayOfWeekMask'] = $mask; } else { $hash['RecurrenceType'] = 5; $hash['DayOfMonth'] = date('j', $start); } $hash['MonthOfYear'] = date('n', $start); unset($hash['Interval']); break; } if (isset($rdata['UNTIL'])) { $hash['NoEndDate'] = 0; $hash['PatternEndDate'] = $rdata['UNTIL']; } elseif (isset($rdata['COUNT'])) { $hash['NoEndDate'] = 0; $hash['Occurrences'] = $rdata['COUNT']; } else { $hash['NoEndDate'] = 1; } break; case 'EXDATE': if (empty($hash['Exceptions'])) { $hash['Exceptions'] = array(); } foreach ($item['values'] as $date) { if ($hash['AllDayEvent'] == 1) { $d = new Horde_Date(array('year' => $date['year'], 'month' => $date['month'], 'mday' => $date['mday'] + 1)); $hash['Exceptions'][] = array('ExcludeDate' => $d->format('Y-m-d')); } else { $hash['Exceptions'][] = array('ExcludeDate' => Horde_Icalendar::_exportDate($date)); } } break; } } if (!empty($start)) { if ($hash['ReminderSet'] && !empty($alarm) && $start != $alarm) { $hash['ReminderMinutesBeforeStart'] = ($start - $alarm) / 60; } else { // Parse VALARM components. foreach ($content->getComponents() as $component) { if ($component->getType() != 'vAlarm') { continue; } try { $trigger = $component->getAttribute('TRIGGER'); } catch (Horde_Icalendar_Exception $e) { continue; } if (is_array($trigger) || empty($trigger)) { continue; } $hash['ReminderSet'] = 1; $hash['ReminderMinutesBeforeStart'] = -$trigger / 60; } } } if (empty($hash['AllDayEvent']) && !empty($start) && !empty($end) && $start != $end) { $hash['Duration'] = ($end - $start) / 60; $GLOBALS['backend']->logMessage('Duration set to ' . $hash['Duration'], 'DEBUG'); } return Horde_SyncMl_Device_sync4j::array2sif($hash, '<?xml version="1.0"?><appointment>', '</appointment>'); }
/** * Lists all events in the given time range. * * @param Horde_Date $startDate Start of range date object. * @param Horde_Date $endDate End of range data object. * * @return array Events in the given time range. * * @throws Horde_Kolab_FreeBusy_Exception If retrieving the events failed. */ public function listEvents(Horde_Date $startDate, Horde_Date $endDate) { $url = $this->_params['url'] . '/public/?cmd=freebusy' . '&start=' . $startDate->format('c') . '&end=' . $endDate->format('c') . '&interval=' . $this->_params['interval'] . '&u=SMTP:' . $this->_owner->getOwner(); $response = $this->_client->get($url, array('User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)')); if ($response->code !== 200) { throw new Horde_Kolab_FreeBusy_Exception_NotFound(sprintf('Unable to fetch free/busy information from %s', $url)); } $owa = new Horde_Kolab_FreeBusy_Freebusy_Helper_Owa($response->getStream()); $result = $owa->convert($startDate, $endDate, $this->_params['interval']); if (!isset($result[$this->_owner->getOwner()])) { return array(); } $events = array(); foreach ($result[$this->_owner->getOwner()] as $item) { $events[] = new Horde_Kolab_FreeBusy_Object_Event($item); } return $events; }
protected function _fqlToTurba($fields, $result) { //$remove = array(); foreach ($fields as $field) { if (strpos($field, '.') !== false) { $key = substr($field, 0, strpos($field, '.')); $subfield = substr($field, strpos($field, '.') + 1); $result[$field] = $result[$key][$subfield]; } } if (!empty($result['birthday_date'])) { // Make sure the birthdate is in a standard format that // listDateObjects will understand. $bday = new Horde_Date($result['birthday_date']); $result['birthday_date'] = $bday->format('Y-m-d'); } return $result; }
/** * Prepares this event to be saved to the backend. * * @param boolean $full Return full data, including uid and id. * * @return array The event properties. */ public function toProperties($full = false) { $driver = $this->getDriver(); $properties = array(); if ($full) { $properties['event_id'] = $this->id; $properties['event_uid'] = $this->uid; } /* Basic fields. */ $properties['event_creator_id'] = $driver->convertToDriver($this->creator); $properties['event_title'] = $driver->convertToDriver($this->title); $properties['event_description'] = $driver->convertToDriver($this->description); $properties['event_location'] = $driver->convertToDriver($this->location); $properties['event_timezone'] = $this->timezone; $properties['event_url'] = (string) $this->url; $properties['event_private'] = (int) $this->private; $properties['event_status'] = $this->status; $properties['event_attendees'] = serialize($this->attendees); $properties['event_resources'] = serialize($driver->convertToDriver($this->getResources())); $properties['event_modified'] = $_SERVER['REQUEST_TIME']; $properties['event_organizer'] = $this->organizer; if ($this->isAllDay()) { $properties['event_start'] = $this->start->strftime('%Y-%m-%d %H:%M:%S'); $properties['event_end'] = $this->end->strftime('%Y-%m-%d %H:%M:%S'); $properties['event_allday'] = 1; } else { if ($driver->getParam('utc')) { $start = clone $this->start; $end = clone $this->end; $start->setTimezone('UTC'); $end->setTimezone('UTC'); } else { $start = $this->start; $end = $this->end; } $properties['event_start'] = $start->strftime('%Y-%m-%d %H:%M:%S'); $properties['event_end'] = $end->strftime('%Y-%m-%d %H:%M:%S'); $properties['event_allday'] = 0; } /* Alarm. */ $properties['event_alarm'] = (int) $this->alarm; /* Alarm Notification Methods. */ $properties['event_alarm_methods'] = serialize($driver->convertToDriver($this->methods)); /* Recurrence. */ if (!$this->recurs()) { $properties['event_recurtype'] = 0; } else { $recur = $this->recurrence->getRecurType(); if ($this->recurrence->hasRecurEnd()) { if ($driver->getParam('utc')) { $recur_end = clone $this->recurrence->recurEnd; $recur_end->setTimezone('UTC'); } else { $recur_end = $this->recurrence->recurEnd; } } else { $recur_end = new Horde_Date(array('year' => 9999, 'month' => 12, 'mday' => 31, 'hour' => 23, 'min' => 59, 'sec' => 59)); } $properties['event_recurtype'] = $recur; $properties['event_recurinterval'] = $this->recurrence->getRecurInterval(); $properties['event_recurenddate'] = $recur_end->format('Y-m-d H:i:s'); $properties['event_recurcount'] = $this->recurrence->getRecurCount(); switch ($recur) { case Horde_Date_Recurrence::RECUR_WEEKLY: $properties['event_recurdays'] = $this->recurrence->getRecurOnDays(); break; } $properties['event_exceptions'] = implode(',', $this->recurrence->getExceptions()); } /* Exception information */ if (!empty($this->baseid)) { $properties['event_baseid'] = $this->baseid; if ($driver->getParam('utc')) { $eod = clone $this->exceptionoriginaldate; $eod->setTimezone('UTC'); } else { $eod = $this->exceptionoriginaldate; } $properties['event_exceptionoriginaldate'] = $eod->strftime('%Y-%m-%d %H:%M:%S'); } else { /* This must be an empty string. */ $properties['event_baseid'] = ''; $properties['event_exceptionoriginaldate'] = null; } return $properties; }