protected function _exportvData($base = 'VCALENDAR')
{
$result = 'BEGIN:' . Horde_String::upper($base) . $this->_newline;
// VERSION is not allowed for entries enclosed in VCALENDAR/ICALENDAR,
// as it is part of the enclosing VCALENDAR/ICALENDAR. See rfc2445
if ($base !== 'VEVENT' && $base !== 'VTODO' && $base !== 'VALARM' && $base !== 'VJOURNAL' && $base !== 'VFREEBUSY' && $base != 'VTIMEZONE' && $base != 'STANDARD' && $base != 'DAYLIGHT') {
// Ensure that version is the first attribute.
$result .= 'VERSION:' . $this->_version . $this->_newline;
}
foreach ($this->_attributes as $attribute) {
$name = $attribute['name'];
if ($name == 'VERSION') {
// Already done.
continue;
}
$params_str = '';
$params = $attribute['params'];
if ($params) {
foreach ($params as $param_name => $param_value) {
/* Skip CHARSET for iCalendar 2.0 data, not allowed. */
if ($param_name == 'CHARSET' && !$this->_oldFormat) {
continue;
}
/* Skip VALUE=DATE for vCalendar 1.0 data, not allowed. */
if ($this->_oldFormat && $param_name == 'VALUE' && $param_value == 'DATE') {
continue;
}
if ($param_value === null) {
$params_str .= ";{$param_name}";
} else {
if (!is_array($param_value)) {
$param_value = array($param_value);
}
foreach ($param_value as &$one_param_value) {
$len = strlen($one_param_value);
$safe_value = '';
$quote = false;
for ($i = 0; $i < $len; ++$i) {
$ord = ord($one_param_value[$i]);
// Accept only valid characters.
if ($ord == 9 || $ord == 32 || $ord == 33 || $ord >= 35 && $ord <= 126 || $ord >= 128) {
$safe_value .= $one_param_value[$i];
// Characters above 128 do not need to be
// quoted as per RFC2445 but Outlook requires
// this.
if ($ord == 44 || $ord == 58 || $ord == 59 || $ord >= 128) {
$quote = true;
}
}
}
if ($quote) {
$safe_value = '"' . $safe_value . '"';
}
$one_param_value = $safe_value;
}
$params_str .= ";{$param_name}=" . implode(',', $param_value);
}
}
}
$value = $attribute['value'];
switch ($name) {
// Date fields.
case 'COMPLETED':
case 'CREATED':
case 'DCREATED':
case 'LAST-MODIFIED':
case 'X-MOZ-LASTACK':
case 'X-MOZ-SNOOZE-TIME':
$value = $this->_exportDateTime($value);
break;
case 'DTEND':
case 'DTSTART':
case 'DTSTAMP':
case 'DUE':
case 'AALARM':
case 'RECURRENCE-ID':
$floating = $base == 'STANDARD' || $base == 'DAYLIGHT' || isset($params['TZID']);
if (isset($params['VALUE'])) {
if ($params['VALUE'] == 'DATE') {
// VCALENDAR 1.0 uses T000000 - T235959 for all day events:
if ($this->_oldFormat && $name == 'DTEND') {
$d = new Horde_Date($value);
$value = new Horde_Date(array('year' => $d->year, 'month' => $d->month, 'mday' => $d->mday - 1));
$value = $this->_exportDate($value, '235959');
} else {
$value = $this->_exportDate($value, '000000');
}
} else {
$value = $this->_exportDateTime($value, $floating);
}
} else {
$value = $this->_exportDateTime($value, $floating);
}
break;
// Comma seperated dates.
// Comma seperated dates.
case 'EXDATE':
case 'RDATE':
$floating = $base == 'STANDARD' || $base == 'DAYLIGHT';
$dates = array();
foreach ($value as $date) {
if (isset($params['VALUE'])) {
if ($params['VALUE'] == 'DATE') {
$dates[] = $this->_exportDate($date, '000000');
} elseif ($params['VALUE'] == 'PERIOD') {
$dates[] = $this->_exportPeriod($date);
} else {
$dates[] = $this->_exportDateTime($date, $floating);
}
} else {
$dates[] = $this->_exportDateTime($date, $floating);
}
}
$value = implode($this->_oldFormat ? ';' : ',', $dates);
break;
case 'TRIGGER':
if (isset($params['VALUE'])) {
if ($params['VALUE'] == 'DATE-TIME') {
$value = $this->_exportDateTime($value);
} elseif ($params['VALUE'] == 'DURATION') {
$value = $this->_exportDuration($value);
}
} else {
$value = $this->_exportDuration($value);
}
break;
// Duration fields.
// Duration fields.
case 'DURATION':
$value = $this->_exportDuration($value);
break;
// Period of time fields.
// Period of time fields.
case 'FREEBUSY':
$value_str = '';
foreach ($value as $period) {
$value_str .= empty($value_str) ? '' : ',';
$value_str .= $this->_exportPeriod($period);
}
$value = $value_str;
break;
// UTC offset fields.
// UTC offset fields.
case 'TZOFFSETFROM':
case 'TZOFFSETTO':
$value = $this->_exportUtcOffset($value);
break;
// Integer fields.
// Integer fields.
case 'PERCENT-COMPLETE':
case 'PRIORITY':
case 'REPEAT':
case 'SEQUENCE':
$value = "{$value}";
break;
// Geo fields.
// Geo fields.
case 'GEO':
if ($this->_oldFormat) {
$value = $value['longitude'] . ',' . $value['latitude'];
} else {
$value = $value['latitude'] . ';' . $value['longitude'];
}
break;
// Recurrence fields.
// Recurrence fields.
case 'EXRULE':
case 'RRULE':
break;
default:
if ($this->_oldFormat) {
if (is_array($attribute['values']) && count($attribute['values']) > 1) {
$values = $attribute['values'];
if ($name == 'N' || $name == 'ADR' || $name == 'ORG') {
$glue = ';';
} else {
$glue = ',';
}
$values = str_replace(';', '\\;', $values);
$value = implode($glue, $values);
} else {
/* vcard 2.1 and vcalendar 1.0 escape only
* semicolons */
$value = str_replace(';', '\\;', $value);
}
// Text containing newlines or ASCII >= 127 must be BASE64
// or QUOTED-PRINTABLE encoded. Currently we use
// QUOTED-PRINTABLE as default.
if (preg_match("/[^ -]/", $value) && empty($params['ENCODING'])) {
$params['ENCODING'] = 'QUOTED-PRINTABLE';
$params_str .= ';ENCODING=QUOTED-PRINTABLE';
// Add CHARSET as well. At least the synthesis client
// gets confused otherwise
if (empty($params['CHARSET'])) {
$params['CHARSET'] = 'UTF-8';
$params_str .= ';CHARSET=' . $params['CHARSET'];
}
}
} else {
if (is_array($attribute['values']) && count($attribute['values'])) {
$values = $attribute['values'];
if ($name == 'N' || $name == 'ADR' || $name == 'ORG') {
$glue = ';';
} else {
$glue = ',';
}
// As of rfc 2426 2.5 semicolon and comma must be
// escaped.
$values = str_replace(array('\\', ';', ','), array('\\\\', '\\;', '\\,'), $values);
$value = implode($glue, $values);
} else {
// As of rfc 2426 2.5 semicolon and comma must be
// escaped.
$value = str_replace(array('\\', ';', ','), array('\\\\', '\\;', '\\,'), $value);
}
$value = preg_replace('/\\r?\\n/', '\\n', $value);
}
break;
}
$value = str_replace("\r", '', $value);
if (!empty($params['ENCODING']) && $params['ENCODING'] == 'QUOTED-PRINTABLE' && strlen(trim($value))) {
$result .= $name . $params_str . ':' . preg_replace(array('/(?<!\\r)\\n/', '/(?<!=)\\r\\n/'), array("\r\n", "=0D=0A=\r\n "), quoted_printable_encode($value)) . $this->_newline;
} else {
$attr_string = $name . $params_str . ':' . $value;
if (!$this->_oldFormat) {
if (isset($params['ENCODING']) && $params['ENCODING'] == 'b') {
$attr_string = trim(chunk_split($attr_string, 75, $this->_newline . ' '));
} else {
$attr_string = Horde_String::wordwrap($attr_string, 75, $this->_newline . ' ', true, true);
}
}
$result .= $attr_string . $this->_newline;
}
}
$tzs = array();
foreach ($this->_components as $component) {
if (!$component instanceof Horde_Icalendar_Vtimezone || !isset($tzs[$component->getAttribute('TZID')])) {
$result .= $component->exportvCalendar();
if ($component instanceof Horde_Icalendar_Vtimezone) {
$tzs[$component->getAttribute('TZID')] = true;
}
}
}
return $result . 'END:' . $base . $this->_newline;
}