public function date_ical_plugin_style_ical_feed::render in Date iCal 7.3
Same name and namespace in other branches
- 7 date_ical_plugin_style_ical_feed.inc \date_ical_plugin_style_ical_feed::render()
- 7.2 includes/date_ical_plugin_style_ical_feed.inc \date_ical_plugin_style_ical_feed::render()
Render the event arrays returned by the row plugin into a VCALENDAR.
Overrides views_plugin_style::render
File
- includes/
date_ical_plugin_style_ical_feed.inc, line 143 - Views style plugin for the Date iCal module.
Class
- date_ical_plugin_style_ical_feed
- Defines a Views style plugin that renders iCal feeds.
Code
public function render() {
if (empty($this->row_plugin) || !in_array($this->row_plugin->plugin_name, array(
'date_ical',
'date_ical_fields',
))) {
debug('date_ical_plugin_style_ical_feed: This style plugin supports only the "iCal Entity" and "iCal Fields" row plugins.', NULL, TRUE);
return t('To enable iCal output, the view Format must be configured to Show: iCal Entity or iCal Fields.');
}
if ($this->row_plugin->plugin_name == 'date_ical_fields' && empty($this->row_plugin->options['date_field'])) {
// Because the Date field is required by the form, this error state will
// rarely occur. But I ran across it during testing, and the error that
// resulted was totally non-sensical, so I'm adding this just in case.
return t("When using the iCal Fields row plugin, the Date field is required. Please set it up using the Settings link under 'Format -> Show: iCal Fields'.");
}
$events = array();
foreach ($this->view->result as $row_index => $row) {
$this->view->row_index = $row_index;
$row->index = $row_index;
try {
$events[] = $this->row_plugin
->render($row);
} catch (Exception $e) {
debug($e
->getMessage(), NULL, TRUE);
return $e
->getMessage();
}
}
unset($this->view->row_index);
// Try to load the iCalcreator library.
$library = libraries_load('iCalcreator');
if (!$library['loaded']) {
// The iCalcreator library isn't available, so we can't output anything.
$output = t('Please install the iCalcreator library to enable iCal output.');
}
else {
// Create a vcalendar object using the iCalcreator library.
$config = array(
'unique_id' => 'Date iCal',
);
$vcalendar = new vcalendar($config);
$vcalendar
->setMethod('PUBLISH');
// Only include the X-WR-CALNAME property if the user didn't enable
// the "Exclude Calendar Name" option.
if (!$this
->_get_option('no_calname')) {
$cal_name = $this
->_get_option('cal_name');
if (empty($cal_name)) {
$cal_name = $this->view
->get_title();
if (empty($cal_name)) {
$cal_name = variable_get('site_name', 'Drupal');
}
}
if (!empty($cal_name)) {
$vcalendar
->setProperty('X-WR-CALNAME', $cal_name, array(
'VALUE' => 'TEXT',
));
}
}
// Now add the VEVENTs.
$timezones = array();
foreach ($events as $event) {
if (empty($event)) {
// The row plugin returned NULL for this row, which can happen due to
// either various error conditions, or because an RRULE is involved.
// When this happens, just skip it.
continue;
}
$vevent = $vcalendar
->newComponent('vevent');
$vevent
->setUid($event['uid']);
$vevent
->setSummary($event['summary']);
// Get the start date as an array.
$start = $event['start']
->toArray();
$start_timezone = $event['start']
->getTimezone()
->getName();
$timezones[$start_timezone] = $start_timezone;
if ($event['all_day']) {
// All Day events need to be DATEs, rather than DATE-TIMEs.
$vevent
->setDtstart($start['year'], $start['month'], $start['day'], FALSE, FALSE, FALSE, FALSE, array(
'VALUE' => 'DATE',
));
}
else {
$vevent
->setDtstart($start['year'], $start['month'], $start['day'], $start['hour'], $start['minute'], $start['second'], $start_timezone);
}
// Add the Timezone info to the start date, for use later.
$start['tz'] = $event['start']
->getTimezone();
// Only add the end date if there is one.
if (!empty($event['end'])) {
$end = $event['end']
->toArray();
$end_timezone = $event['end']
->getTimezone()
->getName();
$timezones[$end_timezone] = $end_timezone;
if ($event['all_day']) {
$vevent
->setDtend($end['year'], $end['month'], $end['day'], FALSE, FALSE, FALSE, FALSE, array(
'VALUE' => 'DATE',
));
}
else {
$vevent
->setDtend($end['year'], $end['month'], $end['day'], $end['hour'], $end['minute'], $end['second'], $end_timezone);
}
$end['tz'] = $event['end']
->getTimezone();
}
// Handle repeating dates from the date_repeat module.
if (!empty($event['rrule']) && module_exists('date_repeat')) {
// Split the rrule into an RRULE and any additions and exceptions.
module_load_include('inc', 'date_api', 'date_api_ical');
module_load_include('inc', 'date_repeat', 'date_repeat_calc');
list($rrule, $exceptions, $additions) = date_repeat_split_rrule($event['rrule']);
// Add the RRULE itself. We need to massage the data a bit, since
// iCalcreator expects RRULEs to be in a different format than how
// Date API gives them to us.
$vevent
->setRrule(_date_ical_convert_rrule_for_icalcreator($rrule));
// Convert any exceptions to EXDATE properties.
if (!empty($exceptions)) {
$exdates = array();
foreach ($exceptions as $exception) {
$except = date_ical_date($exception, 'UTC');
$except
->setTimezone($start['tz']);
$exception_array = $except
->toArray();
$exdates[] = array(
'year' => $exception_array['year'],
'month' => $exception_array['month'],
'day' => $exception_array['day'],
// Use the time information from the start date, since Date
// doesn't store time info for EXDATEs.
'hour' => $start['hour'],
'min' => $start['minute'],
'second' => $start['second'],
'tz' => $start['tz']
->getName(),
);
}
// Add each exclusion as a separate EXDATE property.
// The spec supports putting multiple date values into one EXDATE,
// but several popular calendar clients (*cough* Apple *cough*)
// are bugged, and do not recognize multi-value EXDATEs.
$value = $event['all_day'] == 1 ? "DATE" : "DATE-TIME";
foreach ($exdates as $exdate) {
$vevent
->setExdate(array(
$exdate,
), array(
"VALUE" => $value,
));
}
}
// Convert any additions to RDATE properties.
if (!empty($additions)) {
$rdates = array();
foreach ($additions as $addition) {
$add = date_ical_date($addition, 'UTC');
$add
->setTimezone($start['tz']);
$addition_array = $add
->toArray();
$rdate = array(
'year' => $addition_array['year'],
'month' => $addition_array['month'],
'day' => $addition_array['day'],
// If the user's copy of Date has support for time in RDATEs,
// use that. Otherwise use the time from the start date.
'hour' => !empty($addition_array['hour']) ? $addition_array['hour'] : $start['hour'],
'min' => !empty($addition_array['minute']) ? $addition_array['minute'] : $start['minute'],
'second' => !empty($addition_array['second']) ? $addition_array['second'] : $start['second'],
'tz' => $start['tz']
->getName(),
);
// If an end date was was calculated above, use that too.
// iCalcreator expects RDATEs that have end dates to be
// specified as array($start_rdate, $end_rdate).
if (isset($end)) {
$rdate_with_end = array(
$rdate,
);
$rdate_with_end[] = array(
'year' => $addition_array['year'],
'month' => $addition_array['month'],
'day' => $addition_array['day'],
// If the user's copy of Date has support for time in RDATEs,
// use that. Otherwise use the time from the end date.
'hour' => !empty($addition_array['hour']) ? $addition_array['hour'] : $end['hour'],
'min' => !empty($addition_array['minute']) ? $addition_array['minute'] : $end['minute'],
'second' => !empty($addition_array['second']) ? $addition_array['second'] : $end['second'],
'tz' => $end['tz']
->getName(),
);
$rdate = $rdate_with_end;
}
$rdates[] = $rdate;
}
// Add each addition as a separate RDATE property.
// The spec supports putting multiple date values into one RDATE,
// but several popular calendar clients (*cough* Apple *cough*)
// are bugged, and do not recognize multi-value RDATEs.
foreach ($rdates as $rdate) {
$vevent
->setRdate(array(
$rdate,
));
}
}
}
if (!empty($event['url'])) {
$vevent
->setUrl($event['url'], array(
'type' => 'URI',
));
}
if (!empty($event['location'])) {
$vevent
->setLocation($event['location']);
}
if (!empty($event['description'])) {
$vevent
->setDescription($event['description']);
}
if (!empty($event['categories'])) {
$vevent
->setCategories($event['categories']);
}
if (!empty($event['last-modified'])) {
$lm = $event['last-modified']
->toArray();
$vevent
->setLastModified($lm['year'], $lm['month'], $lm['day'], $lm['hour'], $lm['minute'], $lm['second'], $lm['timezone']);
}
if (!empty($event['created'])) {
$created = $event['created']
->toArray();
$vevent
->setCreated($created['year'], $created['month'], $created['day'], $created['hour'], $created['minute'], $created['second'], $created['timezone']);
}
// Allow other modules to alter the vevent before it's exported.
drupal_alter('date_ical_export_vevent', $vevent, $this->view, $event);
}
// Now add to the calendar all the timezones used by the events.
foreach ($timezones as $timezone) {
if (strtoupper($timezone) != 'UTC') {
iCalUtilityFunctions::createTimezone($vcalendar, $timezone);
}
}
// Allow other modules to alter the vcalendar before it's exported.
drupal_alter('date_ical_export_vcalendar', $vcalendar, $this->view);
$output = $vcalendar
->createCalendar();
// iCalcreator escapes all commas and semicolons in string values, as the
// spec demands. However, some calendar clients are buggy and fail to
// unescape these characters. Users may choose to unescape them here to
// sidestep those clients' bugs.
// NOTE: This results in a non-compliant iCal feed, but it seems like a
// LOT of major clients are bugged this way.
if ($this
->_get_option('unescape_punctuation')) {
$output = str_replace('\\,', ',', $output);
$output = str_replace('\\;', ';', $output);
}
// In order to respect the Exclude DTSTAMP option, we unfortunately have
// to parse out the DTSTAMP properties after they get rendered. Simply
// using deleteProperty('DTSTAMP') doesn't work, because iCalcreator
// considers the DTSTAMP to be essential, and will re-create it when
// createCalendar() is called.
if ($this
->_get_option('exclude_dtstamp')) {
$filtered_lines = array();
foreach (explode("\r\n", $output) as $line) {
if (strpos($line, 'DTSTAMP') === 0) {
continue;
}
$filtered_lines[] = $line;
}
$output = implode("\r\n", $filtered_lines);
}
}
// These steps shouldn't be run during Preview on the View page.
if (empty($this->view->live_preview)) {
// Prevent devel module from appending queries to ical export.
$GLOBALS['devel_shutdown'] = FALSE;
drupal_add_http_header('Content-Type', 'text/calendar; charset=UTF-8');
drupal_add_http_header('Cache-Control', 'no-cache, must-revalidate');
drupal_add_http_header('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
// For sites with Clean URLs disabled, the Display's "path" value ends
// up only in the query args, meaning the filename won't be set properly
// when users download the feed. So we need to manually instruct browsers
// to download a .ics file.
if (!variable_get('clean_url', FALSE)) {
$path_array = explode('/', $this->display->display_options['path']);
$filename = end($path_array);
drupal_add_http_header('Content-Disposition', "attachment; filename=\"{$filename}\"");
}
}
// Allow other modules to alter the rendered calendar.
drupal_alter('date_ical_export_post_render', $output, $this->view);
return $output;
}