/** * Updates the properties of this event from a Horde_Icalendar_Vevent * object. * * @param Horde_Icalendar_Vevent $vEvent The iCalendar data to update * from. * @param boolean $parseAttendees Parse attendees too? * @since Kronolith 4.2 */ public function fromiCalendar($vEvent, $parseAttendees = false) { // Unique ID. try { $uid = $vEvent->getAttribute('UID'); if (!empty($uid)) { $this->uid = $uid; } } catch (Horde_Icalendar_Exception $e) { } // Sequence. try { $seq = $vEvent->getAttribute('SEQUENCE'); if (is_int($seq)) { $this->sequence = $seq; } } catch (Horde_Icalendar_Exception $e) { } // Title, tags and description. try { $title = $vEvent->getAttribute('SUMMARY'); if (!is_array($title)) { $this->title = $title; } } catch (Horde_Icalendar_Exception $e) { } // Tags try { $this->_tags = $vEvent->getAttributeValues('CATEGORIES'); } catch (Horde_Icalendar_Exception $e) { } // Description try { $desc = $vEvent->getAttribute('DESCRIPTION'); if (!is_array($desc)) { $this->description = $desc; } } catch (Horde_Icalendar_Exception $e) { } // Remote Url try { $url = $vEvent->getAttribute('URL'); if (!is_array($url)) { $this->url = $url; } } catch (Horde_Icalendar_Exception $e) { } // Location try { $location = $vEvent->getAttribute('LOCATION'); if (!is_array($location)) { $this->location = $location; } } catch (Horde_Icalendar_Exception $e) { } try { $geolocation = $vEvent->getAttribute('GEO'); $this->geoLocation = array('lat' => $geolocation['latitude'], 'lon' => $geolocation['longitude']); } catch (Horde_Icalendar_Exception $e) { } // Class try { $class = $vEvent->getAttribute('CLASS'); if (!is_array($class)) { $class = Horde_String::upper($class); $this->private = $class == 'PRIVATE' || $class == 'CONFIDENTIAL'; } } catch (Horde_Icalendar_Exception $e) { } // Status. try { $status = $vEvent->getAttribute('STATUS'); if (!is_array($status)) { $status = Horde_String::upper($status); if ($status == 'DECLINED') { $status = 'CANCELLED'; } if (defined('Kronolith::STATUS_' . $status)) { $this->status = constant('Kronolith::STATUS_' . $status); } } } catch (Horde_Icalendar_Exception $e) { } // Reset allday flag in case this has changed. Will be recalculated // next time isAllDay() is called. $this->allday = false; // Start and end date. $tzid = null; try { $start = $vEvent->getAttribute('DTSTART'); $startParams = $vEvent->getAttribute('DTSTART', true); // We don't support different timezones for different attributes, // so use the DTSTART timezone for the complete event. if (isset($startParams[0]['TZID'])) { // Horde_Date supports timezone aliases, so try that first. $tz = $startParams[0]['TZID']; try { // Check if the timezone name is supported by PHP natively. new DateTimeZone($tz); $this->timezone = $tzid = $tz; } catch (Exception $e) { } } if (!is_array($start)) { // Date-Time field $this->start = new Horde_Date($start, $tzid); } else { // Date field $this->start = new Horde_Date(array('year' => (int) $start['year'], 'month' => (int) $start['month'], 'mday' => (int) $start['mday']), $tzid); } } catch (Horde_Icalendar_Exception $e) { throw new Kronolith_Exception($e); } catch (Horde_Date_Exception $e) { throw new Kronolith_Exception($e); } try { $end = $vEvent->getAttribute('DTEND'); if (!is_array($end)) { // Date-Time field $this->end = new Horde_Date($end, $tzid); // All day events are transferred by many device as // DSTART: YYYYMMDDT000000 DTEND: YYYYMMDDT2359(59|00) // Convert accordingly if (is_object($this->start) && $this->start->hour == 0 && $this->start->min == 0 && $this->start->sec == 0 && $this->end->hour == 23 && $this->end->min == 59) { $this->end = new Horde_Date(array('year' => (int) $this->end->year, 'month' => (int) $this->end->month, 'mday' => (int) $this->end->mday + 1), $tzid); } } else { // Date field $this->end = new Horde_Date(array('year' => (int) $end['year'], 'month' => (int) $end['month'], 'mday' => (int) $end['mday']), $tzid); } } catch (Horde_Icalendar_Exception $e) { $end = null; } if (is_null($end)) { try { $duration = $vEvent->getAttribute('DURATION'); if (!is_array($duration)) { $this->end = new Horde_Date($this->start); $this->end->sec += $duration; $end = 1; } } catch (Horde_Icalendar_Exception $e) { } if (is_null($end)) { // End date equal to start date as per RFC 2445. $this->end = new Horde_Date($this->start); if (is_array($start)) { // Date field $this->end->mday++; } } } // vCalendar 1.0 alarms try { $alarm = $vEvent->getAttribute('AALARM'); if (!is_array($alarm) && intval($alarm)) { $this->alarm = intval(($this->start->timestamp() - $alarm) / 60); } } catch (Horde_Icalendar_Exception $e) { } // vCalendar 2.0 alarms foreach ($vEvent->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') { $this->alarm = intval(($this->end->timestamp() - $trigger) / 60); } else { $this->alarm = intval(($this->start->timestamp() - $trigger) / 60); } $haveTrigger = true; break; } elseif (isset($tp['RELATED']) && $tp['RELATED'] == 'END') { $this->alarm = -intval($trigger / 60); $this->alarm -= $this->durMin; $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 event is either dismissed or // snoozed. $vEvent->getAttribute('X-MOZ-LASTACK'); try { // If X-MOZ-SNOOZE-TIME is set, this event is snoozed. $snooze = $vEvent->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) { } } // Attendance. // Importing attendance may result in confusion: editing an imported // copy of an event can cause invitation updates to be sent from // people other than the original organizer. So we don't import by // default. However to allow updates by synchronization, this behavior // can be overriden. // X-ATTENDEE is there for historical reasons. @todo remove in // Kronolith 5. $attendee = null; if ($parseAttendees) { try { $attendee = $vEvent->getAttribute('ATTENDEE'); $params = $vEvent->getAttribute('ATTENDEE', true); } catch (Horde_Icalendar_Exception $e) { try { $attendee = $vEvent->getAttribute('X-ATTENDEE'); $params = $vEvent->getAttribute('X-ATTENDEE', true); } catch (Horde_Icalendar_Exception $e) { } } } if ($attendee) { if (!is_array($attendee)) { $attendee = array($attendee); } if (!is_array($params)) { $params = array($params); } for ($i = 0; $i < count($attendee); ++$i) { $attendee[$i] = str_replace(array('MAILTO:', 'mailto:'), '', $attendee[$i]); $tmp = new Horde_Mail_Rfc822_Address($attendee[$i]); $email = $tmp->bare_address; // Default according to rfc2445: $attendance = Kronolith::PART_REQUIRED; // vCalendar 2.0 style: if (!empty($params[$i]['ROLE'])) { switch ($params[$i]['ROLE']) { case 'OPT-PARTICIPANT': $attendance = Kronolith::PART_OPTIONAL; break; case 'NON-PARTICIPANT': $attendance = Kronolith::PART_NONE; break; } } // vCalendar 1.0 style; if (!empty($params[$i]['EXPECT'])) { switch ($params[$i]['EXPECT']) { case 'REQUEST': $attendance = Kronolith::PART_OPTIONAL; break; case 'FYI': $attendance = Kronolith::PART_NONE; break; } } $response = Kronolith::RESPONSE_NONE; if (empty($params[$i]['PARTSTAT']) && !empty($params[$i]['STATUS'])) { $params[$i]['PARTSTAT'] = $params[$i]['STATUS']; } if (!empty($params[$i]['PARTSTAT'])) { switch ($params[$i]['PARTSTAT']) { case 'ACCEPTED': $response = Kronolith::RESPONSE_ACCEPTED; break; case 'DECLINED': $response = Kronolith::RESPONSE_DECLINED; break; case 'TENTATIVE': $response = Kronolith::RESPONSE_TENTATIVE; break; } } $name = isset($params[$i]['CN']) ? $params[$i]['CN'] : null; $this->addAttendee($email, $attendance, $response, $name); } } $this->_handlevEventRecurrence($vEvent); $this->initialized = true; }