Commit 709fdda7 authored by Robert Rollins's avatar Robert Rollins

Added LOCATION field option for iCal export.

parent cba726e2
......@@ -92,50 +92,6 @@ function date_ical_entity_info_alter(&$entity_info) {
}
}
// TODO: I'm pretty sure the following two functions are no longer relevant, since we've moved to the iCalcreator method.
// They don't really hurt anything, though, so I've left them in for now.
/**
* Implements hook_theme_registry_alter().
*
* Technique borrowed from Display Suite module.
* Add a custom preprocess hook that will work for all types of entities
*/
function date_ical_theme_registry_alter(&$theme_registry) {
$entity_info = entity_get_info();
foreach ($entity_info as $entity => $info) {
// User uses user_profile for theming.
if ($entity == 'user') {
$entity = 'user_profile';
}
// Only add preprocess functions if entity exposes theme function.
if (isset($theme_registry[$entity])) {
$theme_registry[$entity]['preprocess functions'][] = 'date_ical_preprocess_date_ical';
}
}
// Support for File Entity.
if (isset($theme_registry['file_entity'])) {
$theme_registry['file_entity']['preprocess functions'][] = 'date_ical_preprocess_date_ical';
}
// Support for Entity API.
if (isset($theme_registry['entity'])) {
$theme_registry['entity']['preprocess functions'][] = 'date_ical_preprocess_date_ical';
}
}
/**
* Technique borrowed from Display Suite module.
* Add ical template suggestions to all types of entities.
*/
function date_ical_preprocess_date_ical(&$vars) {
if (isset($vars['elements']) && isset($vars['elements']['#entity_type']) && isset($vars['elements']['#bundle']) && isset($vars['view_mode']) && $vars['view_mode'] == 'ical') {
$vars['theme_hook_suggestions'][] = $vars['elements']['#entity_type'] . '__ical';
$vars['theme_hook_suggestions'][] = $vars['elements']['#entity_type'] . '__' . $vars['elements']['#bundle'] . '__ical';
}
}
/**
* Implements hook_libraries_info().
*/
......@@ -182,9 +138,9 @@ function date_ical_feeds_plugins() {
),
);
$info['DateIcalIcalcreatorParser'] = array(
'name' => 'iCalcreator parser',
'description' => 'Use iCalcreator to parse iCal feeds.',
'help' => 'Parse feeds in the iCal format using the iCalcreator library.',
'name' => 'iCal parser',
'description' => 'Use the iCalcreator library to parse iCal feeds.',
'help' => 'Parse iCal feeds.',
'handler' => array(
'parent' => 'DateIcalFeedsParser',
'class' => 'DateIcalIcalcreatorParser',
......@@ -289,7 +245,6 @@ function date_ical_build_repeating_dates($rrule = NULL, $rrule_values = NULL, $f
if (!empty($rrule_values['UNTIL']['datetime'])) {
$end = date_ical_date($rrule_values['UNTIL'], $timezone);
// TODO: BUG HERE. Read import.pdf, probably something to do with RRULEs and DTENDs together.
$end_datetime = date_format($end, DATE_FORMAT_DATETIME);
}
elseif (!empty($rrule_values['COUNT'])) {
......@@ -300,6 +255,7 @@ function date_ical_build_repeating_dates($rrule = NULL, $rrule_values = NULL, $f
// The best we can do is pretend it has a repeat count of 52 (52 weeks in a year, most repeats are weekly)
// by inserting a COUNT=52 param into the string, right after 'RRULE:'.
$rrule = substr_replace($rrule, 'COUNT=52;', 6, 0);
$end_datetime = NULL;
}
// Split the RRULE into RRULE, EXDATE, and RDATE parts.
......@@ -340,3 +296,78 @@ function date_ical_build_repeating_dates($rrule = NULL, $rrule_values = NULL, $f
}
return $value;
}
/**
* Identify all potential fields that could populate the optional LOCATION component of iCal output.
*/
function date_ical_get_location_fields($base = 'node', $reset = FALSE) {
static $fields = array();
$empty = array('name' => array(), 'alias' => array());
if (empty($fields[$base]) || $reset) {
$cid = 'date_ical_location_fields_' . $base;
if (!$reset && $cached = cache_get($cid, 'cache_views')) {
$fields[$base] = $cached->data;
}
else {
$fields[$base] = _date_ical_get_location_fields($base);
}
}
// Make sure that empty values will be arrays in the expected format.
return !empty($fields) && !empty($fields[$base]) ? $fields[$base] : $empty;
}
/**
* Identify all potential LOCATION fields.
* This is a cut down version of _date_views_fields() from date_views_fields.inc in date_views module.
*
* @return
* array with fieldname, type, and table.
* @see
* date_views_date_views_fields() which implements
* the hook_date_views_fields() for the core date fields.
*/
function _date_ical_get_location_fields($base = 'node') {
// Make sure $base is never empty.
if (empty($base)) {
$base = 'node';
}
$cid = 'date_ical_location_fields_' . $base;
cache_clear_all($cid, 'cache_views');
// Iterate over all the fields that Views knows about.
$all_fields = date_views_views_fetch_fields($base, 'field');
$fields = array();
foreach ($all_fields as $alias => $val) {
$name = $alias;
$tmp = explode('.', $name);
$field_name = $tmp[1];
$table_name = $tmp[0];
// Skip unsupported field types and fields that weren't defined through
// the Field module.
$info = field_info_field($field_name);
if (!$info || !in_array($info['type'], array('text', 'node_reference'))) {
continue;
}
// Build an array of the field info that we'll need.
$alias = str_replace('.', '_', $alias);
$fields['name'][$name] = array(
'label' => "{$val['group']}: {$val['title']} ($field_name)",
'table_name' => $table_name,
'field_name' => $field_name,
'type' => $info['type'],
);
// These are here only to make this $field array conform to the same format
// as the one returned by _date_views_fields(). They're probably not needed,
// but I thought that consistency would be a good idea.
$fields['name'][$name]['real_field_name'] = $field_name;
$fields['alias'][$alias] = $fields['name'][$name];
}
cache_set($cid, $fields, 'cache_views');
return $fields;
}
......@@ -15,7 +15,7 @@ function date_ical_views_plugins() {
'module' => 'date_ical', // This just tells our themes are elsewhere.
'style' => array(
'date_ical' => array(
'title' => t('Date iCal Feed'),
'title' => t('iCal Feed'),
'help' => t('Generates an iCal VCALENDAR feed from a view.'),
'handler' => 'date_ical_plugin_style_ical_feed',
'path' => $includes_path,
......@@ -29,7 +29,7 @@ function date_ical_views_plugins() {
),
'row' => array(
'date_ical' => array(
'title' => t('Date iCal Entity'),
'title' => t('iCal Entity'),
'help' => t('Render each entity as an iCal VEVENT item.'),
'handler' => 'date_ical_plugin_row_ical_entity',
'path' => $includes_path,
......
......@@ -27,6 +27,7 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
function option_definition() {
$options = parent::option_definition();
$options['date_field'] = array('default' => array());
$options['location_field'] = array('default' => array());
return $options;
}
......@@ -35,6 +36,9 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// Build the select dropdown for the date field that the user wants to use
// to populate the date fields in VEVENTs.
$data = date_views_fields($this->base_table);
$options = array();
foreach ($data['name'] as $item => $value) {
......@@ -60,6 +64,23 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
<br>To change the iCal view mode, configure it on the 'Manage Display' page for each Content Type.
Please note that HTML will be stripped from the output (link URLs will become footnotes), to comply with iCal standards."),
);
// Build the select dropdown for the text/node_reference field that the user
// wants to use to (optionally) populate the LOCATION.
$location_fields = date_ical_get_location_fields($this->base_table);
$location_options = array('none' => '- None -');
foreach ($location_fields['name'] as $item => $value) {
$location_options[$item] = $value['label'];
}
$form['location_field'] = array(
'#type' => 'select',
'#title' => t('LOCATION field'),
'#options' => $location_options,
'#default_value' => $this->options['location_field'],
'#description' => t('You may optionally include a LOCATION component for each event in the iCal output.
Choose which text or Node Reference field you would like to be output as the LOCATION.
If using a Node Reference, the Title of the referenced node will be used.'),
);
}
function pre_render($values) {
......@@ -114,12 +135,12 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
return;
}
$data = date_views_fields($this->base_table);
$info = $data['name'][$this->options['date_field']];
$field_name = str_replace(array('_value', '_value2'), '', $info['real_field_name']);
$table_name = $info['table_name'];
$delta_field = $info['delta_field'];
$is_field = $info['is_field'];
$date_fields = date_views_fields($this->base_table);
$date_info = $date_fields['name'][$this->options['date_field']];
$field_name = str_replace(array('_value', '_value2'), '', $date_info['real_field_name']);
$table_name = $date_info['table_name'];
$delta_field = $date_info['delta_field'];
$is_field = $date_info['is_field'];
// This is ugly and hacky but I can't figure out any generic way to
// recognize that the node module is going to give some the revision timestamp
......@@ -129,30 +150,30 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
}
// Identify the field value that matched our query.
$item = $entity->$field_name;
$date_field = $entity->$field_name;
$entity->date_id = array();
$start = NULL;
$end = NULL;
if ($is_field) {
$lang = $entity->language;
$delta = isset($row->$delta_field) ? $row->$delta_field : 0;
$item = array_key_exists($lang, $item) ? $item[$lang][$delta] : $item['und'][$delta];
if ($is_field) {
$date_field = array_key_exists($lang, $date_field) ? $date_field[$lang][$delta] : $date_field['und'][$delta];
$entity->date_id[] = 'calendar.' . $entity->nid . '.' . $field_name . '.' . $delta;
if (!empty($item['value'])) {
$start = new DateObject($item['value'], $item['timezone_db']);
if (array_key_exists('value2', $item)) {
$end = new DateObject($item['value2'], $item['timezone_db']);
if (!empty($date_field['value'])) {
$start = new DateObject($date_field['value'], $date_field['timezone_db']);
if (array_key_exists('value2', $date_field)) {
$end = new DateObject($date_field['value2'], $date_field['timezone_db']);
}
else {
$end = clone $start;
}
}
}
elseif (!$is_field && !empty($item)) {
$start = new DateObject($item, $item['timezone_db']);
$end = new DateObject($item, $item['timezone_db']);
elseif (!$is_field && !empty($date_field)) {
$start = new DateObject($date_field, $date_field['timezone_db']);
$end = new DateObject($date_field, $date_field['timezone_db']);
}
// If we don't have an iCal date value, go no further.
......@@ -161,13 +182,13 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
}
// Set the item date to the proper display timezone;
$start->setTimezone(new dateTimezone($item['timezone']));
$end->setTimezone(new dateTimezone($item['timezone']));
$start->setTimezone(new dateTimezone($date_field['timezone']));
$end->setTimezone(new dateTimezone($date_field['timezone']));
$start_formatted = $start->format(DATE_FORMAT_DATETIME);
$end_formatted = $end->format(DATE_FORMAT_DATETIME);
$all_day = date_is_all_day($start_formatted, $end_formatted, date_granularity_precision($info['granularity']));
$all_day = date_is_all_day($start_formatted, $end_formatted, date_granularity_precision($date_info['granularity']));
// According to RFC 2445 (clarified in RFC 5545) the DTEND value is
// non-inclusive. When it is a DATE rather than a DATETIME, this means
......@@ -179,25 +200,53 @@ class date_ical_plugin_row_ical_entity extends views_plugin_row {
module_load_include('inc', 'date_api', 'date_api_ical');
$item_text = '';
// If the user specified a LOCATION field, pull that data from the entity.
$location = '';
if (!empty($this->options['location_field']) && $this->options['location_field'] != 'none') {
$location_fields = date_ical_get_location_fields();
$location_info = $location_fields['name'][$this->options['location_field']];
$location_field_name = $location_info['real_field_name'];
// Only attempt this is the entity actually has this field.
if (isset($entity->$location_field_name)) {
$location_field = $entity->$location_field_name;
$location_lang = array_key_exists($lang, $location_field) ? $lang : 'und';
if ($location_info['type'] == 'node_reference') {
$location_nid = $location_field[$location_lang][$delta]['nid'];
if ($location_nid) {
$node = node_load($location_nid);
$location = $node->title;
}
}
else {
$location = $location_field[$location_lang][$delta]['value'];
}
}
}
// Create the rendered display using the display settings from the 'iCal' view mode.
$rendered_array = entity_view($this->entity_type, array($entity), 'ical', $this->language, TRUE);
$item_text = drupal_render($rendered_array);
$event = array();
$event['summary'] = trim(drupal_html_to_text(entity_label($this->entity_type, $entity)));
$event['description'] = trim(drupal_html_to_text($item_text));
$event['all_day'] = $all_day;
$event['start'] = $start;
$event['end'] = $end;
$event['description'] = trim(drupal_html_to_text($item_text));
$uri = entity_uri($this->entity_type, $entity);
$uri['options']['absolute'] = TRUE;
$event['url'] = url($uri['path'], $uri['options']);
$event['uid'] = !empty($entity->date_id) ? $entity->date_id[0] : $event['url'];
$event['rrule'] = $is_field && array_key_exists('rrule', $item) ? $item['rrule'] : '';
if ($location) {
$event['location'] = $location;
}
// Pull the 'changed' date from the entity, so that subscription clients can tell if the event has been updated.
// According to the iCal standard, LAST-MODIFIED must be UTC. Fortunately, Drupal stores timestamps in the DB
// DB as UTC, so we just need to specify that UTC be used rather than the server's local timezone.
// as UTC, so we just need to specify that UTC be used rather than the server's local timezone.
$event['last-modified'] = new DateObject($entity->changed, new DateTimeZone('UTC'));
// Allow other modules to alter the structured event object, before it gets converted to an iCal VEVENT.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment