private function applyICalendarTimes($start, $end, $timezoneID = null) { $utc = new DateTimezone('UTC'); $timezone = null; $userTimeZone = Phprojekt_User_User::getUserDateTimeZone(); if ('Z' === substr($start, -1)) { $timezone = $utc; } else { if (!is_null($timezoneID)) { $timezone = new DateTimeZone($timezoneID); } else { $timezone = $userTimeZone; } } // We can't use ->setTimezone with the timezones returned by getUserDateTimeZone, as these are non-standard // timezones. Unless we start storing correct timezones, we can't directly set the user timezone, so we go to // UTC and convert to usertime from there. Because utcToUser returns a unix timestamp, but ActiveRecords expects // a "Y-m-d H:i:s" timestamp, we have to go through Datetime again. $start = new Datetime($start, $timezone); $start->setTimezone($utc); $startTs = Phprojekt_Converter_Time::utcToUser($start->format('Y-m-d H:i:s')); $start = new Datetime('@' . $startTs); $end = new Datetime($end, $timezone); $end->setTimezone($utc); $endTs = Phprojekt_Converter_Time::utcToUser($end->format('Y-m-d H:i:s')); $end = new Datetime('@' . $endTs); if ($start->diff($end)->invert) { throw new Sabre_DAV_Exception_BadRequest('Start must be before End'); } $this->_timecard->startDatetime = $start->format('Y-m-d H:i:s'); if ($start->format('z') == $end->format('z')) { // Same day $this->_timecard->endTime = $end->format('H:i:s'); } else { $this->_timecard->endTime = '23:59:00'; } }
/** * Updates this Calendar2 object with data from the given VEVENT * * The returned object must be save()d before it is persistent. * This also means that additional changes can be made before any database calls are made. * * @param Sabre_VObject_Component $vevent The vevent component * * @throws Exception If the provided component is not a vevent * * @return void */ public function fromVObject(Sabre_VObject_Component $vevent) { if (strtolower($vevent->name) !== 'vevent') { throw new Exception("Invalid type of vobject_component passed to Calendar2_Models_Calendar2::fromVobject ({$vevent->name})"); } // Workarounds for missing features. We currently don't support locale-time (we just assume it's the user's // usual timzeone) or date values without time (we just assume 0800 - 2000 there). if (!is_null($vevent->dtstart['VALUE']) && $vevent->dtstart['VALUE']->value === 'DATE') { // No T means it's only a date. iCalendar dicates that dtend must be a date, too. $vevent->dtstart->value .= 'T080000'; unset($vevent->dtstart['VALUE']); // Caldav end dates are not inclusive $end = new Datetime($vevent->dtend->value); $end->sub(new DateInterval('P1D')); $vevent->dtend->value = $end->format('Ymd') . 'T200000'; unset($vevent->dtend['VALUE']); } $utc = new DateTimezone('UTC'); $timezone = null; if ('Z' === substr($vevent->dtstart->value, -1)) { $timezone = $utc; } else { if (!is_null($vevent->dtstart['tzid'])) { $timezone = new DateTimeZone($vevent->dtstart['tzid']->value); } else { $timezone = Phprojekt_User_User::getUserDateTimeZone(); } } // 0-1 // handled: // last-mod, description, dtstart, location, summary, uid // not handled // class, created, geo, organizer, priority, dtstamp, seq, status, transp, url, recurid // // none or one of these two // dtend, duration (only assumes dtend case for now) // // 0 - n // TODO: Check how we can handle these. Maybe just concat them? // handling: (only one is handled atm, though) // comment, rrule // not handling: // attach, attendee, categories, contact, exdate, exrule, rstatus, related, resources, rdate, x-prop $mappable = array(array('veventkey' => 'SUMMARY', 'ourkey' => 'summary', 'default' => '_'), array('veventkey' => 'LOCATION', 'ourkey' => 'location', 'default' => ''), array('veventkey' => 'DESCRIPTION', 'ourkey' => 'description', 'default' => ''), array('veventkey' => 'COMMENT', 'ourkey' => 'comments'), array('veventkey' => 'UID', 'ourkey' => 'uid'), array('veventkey' => 'LAST-MODIFIED', 'ourkey' => 'lastModified'), array('veventkey' => 'RRULE', 'ourkey' => 'rrule', 'default' => '')); foreach ($mappable as $m) { if (isset($vevent->{$m}['veventkey'])) { $this->{$m}['ourkey'] = $vevent->{$m}['veventkey']; } else { if (array_key_exists('default', $m)) { $this->{$m}['ourkey'] = $m['default']; } } } $start = new Datetime($vevent->dtstart->value, $timezone); $start->setTimezone($utc); $this->start = Phprojekt_Converter_Time::utcToUser($start->format('Y-m-d H:i:s')); if ($vevent->dtend) { $end = new Datetime($vevent->dtend->value, $timezone); } else { if ($vevent->duration) { $duration = new DateInterval($vevent->duration->value); $end = clone $start; $end->add($duration); } } $end->setTimezone($utc); $this->end = Phprojekt_Converter_Time::utcToUser($end->format('Y-m-d H:i:s')); }
/** * Return ical busy times for the given time period and user. If no * user is given, default to the currently logged in user. * * Request parameters: * int user => The id of the user. May be null. * datetime start => The start of the period to check. * datetime end => The end of the period to check. * * Response * Array of { * datetime start => The start of the busy period. * datetime end => The end of the busy period. * } */ public function jsonBusyTimesAction() { $user = $this->getRequest()->getParam('user', Phprojekt_Auth_Proxy::getEffectiveUserId()); $start = $this->getRequest()->getParam('start'); $end = $this->getRequest()->getParam('end'); if (!Cleaner::validate('int', $user)) { throw new Zend_Controller_Action_Exception("Invalid id '{$id}'", 400); } $user = (int) $user; if (!self::_validateTimestamp($start)) { throw new Zend_Controller_Action_Exception("Invalid start timestamp '{$start}'", 400); } if (!self::_validateTimestamp($end)) { throw new Zend_Controller_Action_Exception("Invalid end timestamp '{$start}'", 400); } $start = new Datetime($start, Phprojekt_User_User::getUserDateTimeZone()); $end = new Datetime($end, Phprojekt_User_User::getUserDateTimeZone()); $model = new Calendar2_Models_Calendar2(); $events = $model->fetchAllForPeriod($start, $end); $busyPeriods = array(); foreach ($events as $event) { $busyPeriods[] = array('start' => new Datetime($event->start, new DateTimeZone('UTC')), 'end' => new Datetime($event->end, new DateTimeZone('UTC'))); } Phprojekt_Converter_Json::echoConvert(Calendar2_Helper_Time::compactPeriods($busyPeriods)); }