'date_ical')); } /** * Implements hook_views_api(). */ function date_ical_views_api() { return array( 'api' => 3, 'path' => drupal_get_path('module', 'date_ical') . '/includes', ); } /** * Implements hook_feeds_plugins(). */ function date_ical_feeds_plugins() { $path = drupal_get_path('module', 'date_ical') . '/includes'; $info = array(); $info['DateiCalFeedsParser'] = array( 'name' => 'iCal parser', 'description' => t('Parse iCal feeds.'), 'handler' => array( 'parent' => 'FeedsParser', 'class' => 'DateiCalFeedsParser', 'file' => 'DateiCalFeedsParser.inc', 'path' => $path, ), ); return $info; } /** * Implements hook_theme(). */ function date_ical_theme($existing, $type, $theme, $path) { return array( 'date_ical_icon' => array( 'variables' => array('url' => NULL, 'tooltip' => NULL), ), ); } /** * Theme function for the ical icon used by attached iCal feed. * * Available variables are: * $variables['tooltip'] - The tooltip to be used for the ican feed icon. * $variables['url'] - The url to the actual iCal feed. * $variables['view'] - The view object from which the iCal feed is being * built (useful for contextual information). */ function theme_date_ical_icon($variables) { if (empty($variables['tooltip'])) { $variables['tooltip'] = t('Add this event to my calendar'); } $variables['path'] = drupal_get_path('module', 'date_ical') . '/images/ical-feed-icon-34x14.png'; $variables['alt'] = $variables['title'] = $variables['tooltip']; if ($image = theme('image', $variables)) { return "$image"; } else { return "{$variables['tooltip']}"; } } /** * Implements hook_preprocess_HOOK(). * * Hides the page elements which don't belong in an iCal DESCRIPTION element. * The display of the body and other fields is controlled by the Manage * Display settings for the nodes' iCal view mode. */ function date_ical_preprocess_node(&$variables) { if (isset($variables['view_mode']) && $variables['view_mode'] == 'ical') { // Trick the default node template into not displaying the page title, by // telling it that this *is* a page. $variables['page'] = TRUE; $variables['title_prefix'] = ''; $variables['title_suffix'] = ''; // We don't want to see the author information in our feed. $variables['display_submitted'] = FALSE; // Comments and links don't belong in an iCal feed. if (isset($variables['content']['comments'])) { unset($variables['content']['comments']); } if (isset($variables['content']['links'])) { unset($variables['content']['links']); } } } /** * Implements hook_entity_info_alter(). * * Adds an 'iCal' view mode for entities, which is used by the iCal Entity * View plugin. */ function date_ical_entity_info_alter(&$entity_info) { foreach ($entity_info as $entity_type => $info) { if (!isset($entity_info[$entity_type]['view modes'])) { $entity_info[$entity_type]['view modes'] = array(); } $entity_info[$entity_type]['view modes'] += array( 'ical' => array( 'label' => t('iCal'), // Set the iCal view mode to default to same settings as the "default" // view mode, so it won't pollute Features. 'custom settings' => FALSE, ), ); } } /** * Implements hook_libraries_info(). */ function date_ical_libraries_info() { $libraries['iCalcreator'] = array( 'name' => 'iCalcreator', 'vendor url' => 'http://github.com/iCalcreator/iCalcreator', 'download url' => 'http://github.com/iCalcreator/iCalcreator', 'version arguments' => array( 'file' => 'iCalcreator.class.php', 'pattern' => "/define\( 'ICALCREATOR_VERSION', 'iCalcreator ([\d\.]+)' \);/", 'lines' => 100, ), 'files' => array( 'php' => array('iCalcreator.class.php'), ), ); return $libraries; } /** * Implements hook_ctools_plugin_api(). */ function date_ical_ctools_plugin_api($owner, $api) { if ($owner == 'feeds' && $api == 'plugins') { return array('version' => 2); } } /** * Implements hook_feeds_processor_targets_alter(). * * Adds the "Field Name: Repeat Rule" target to Date Repeat fields. * * @see FeedsNodeProcessor::getMappingTargets() */ function date_ical_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) { $info = field_info_field($name); if (in_array($info['type'], array('date', 'datestamp', 'datetime')) && isset($info['settings']['repeat']) && $info['settings']['repeat']) { $targets[$name . ':rrule'] = array( 'name' => t('@name: Repeat Rule', array('@name' => $instance['label'])), 'callback' => 'date_ical_feeds_set_rrule', 'description' => t('The repeat rule for the @name field.', array('@name' => $instance['label'])), 'real_target' => $name, ); } } } /** * Reformats the provided text to be compliant with the iCal spec. * * If the text contains HTML tags, those tags will be stripped (with

tags * converted to "\n\n" and link tags converted to footnotes), and uneeded * whitespace will be cleaned up. * * @param string $text * The text to be sanitized. */ function date_ical_sanitize_text($text = '') { // Use Drupal's built-in HTML to Text converter, which does a mostly // adequate job of making the text iCal-compliant. $text = trim(drupal_html_to_text($text)); // Replace instances of more than one space with exactly one space. This // cleans up the whitespace mess that drupal_html_to_text() leaves behind. $text = preg_replace("/ +/", " ", $text); // The call to drupal_html_to_text() above converted

to \n\n, and also // shoved a \n into the string every 80 characters. We don't want those // single \n's lying around, because iCalcreator will properly "fold" long // text fields for us. So, we need to remove all instances of \n which // are neither immediately preceeded, nor followed, by another \n. // However, \n's which are followed immediately by a > character should // remain, because of how drupal_html_to_text() converts

. $text = preg_replace("/(?])/", " ", $text); return $text; } /** * Callback specified in date_ical_feeds_processor_targets_alter() for RRULEs. * * @param object $source * The FeedsSource object. * @param object $entity * The node that's being built from the iCal element that's being parsed. * @param string $target * The machine name of the field into which this RRULE shall be parsed, * with ":rrule" appended to the end. * @param string $repeat_rule * The repeat rule string, formatted like "$rrule|$rdate|$exrule|$exdate". */ function date_ical_feeds_set_rrule($source, $entity, $target, $repeat_rule) { if (empty($repeat_rule)) { // Don't alter the entity if there's no repeat rule. return; } // $target looks like :rrule, but we only need . $field_name = current(explode(':', $target, 2)); // Parse the repeat rule into RRULE, RDATE, EXRULE, and EXDATE strings. $repeat_data = array_combine( array('RRULE', 'RDATE', 'EXRULE', 'EXDATE'), explode('|', $repeat_rule) ); module_load_include('inc', 'date_ical', 'date_ical.utils'); // This "loop" is really just to make sure we get the right array keys. It // souldn't ever execute more than once. foreach ($entity->{$field_name} as $lang => $date_values) { $values = _date_ical_get_repeat_dates($field_name, $repeat_data, $date_values[0], $source); foreach ($values as $key => $value) { $entity->{$field_name}[$lang][$key] = $value; } } } /** * Identify all potential fields which could be used as an iCal LOCATION. */ 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; } /** * Internal helper function for date_ical_get_location_fields(). * * This is a cut down version of _date_views_fields() from * date_views_fields.inc in date_views module. * * @return array * array with fieldname, type, and table. * * @see date_views_date_views_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); $supported_location_fields = array( 'text', 'text_long', 'text_with_summary', 'node_reference', 'addressfield', 'location' ); if (!$info || !in_array($info['type'], $supported_location_fields)) { 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; } /** * Identify all potential fields which could be used as an iCal SUMMARY. */ function date_ical_get_summary_fields($base = 'node', $reset = FALSE) { static $fields = array(); $empty = array('name' => array(), 'alias' => array()); if (empty($fields[$base]) || $reset) { $cid = 'date_ical_summary_fields_' . $base; if (!$reset && $cached = cache_get($cid, 'cache_views')) { $fields[$base] = $cached->data; } else { $fields[$base] = _date_ical_get_summary_fields($base); } } // Make sure that empty values will be arrays in the expected format. return !empty($fields) && !empty($fields[$base]) ? $fields[$base] : $empty; } /** * Internal helper function for date_ical_get_summary_fields(). * * This is a cut down version of _date_views_fields() from * date_views_fields.inc in date_views module. * * @return array * Array with fieldname, type, and table. * * @see date_views_date_views_fields() */ function _date_ical_get_summary_fields($base = 'node') { // Make sure $base is never empty. if (empty($base)) { $base = 'node'; } $cid = 'date_ical_summary_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); $supported_summary_fields = array( 'text', 'text_long', 'text_with_summary', 'node_reference', 'taxonomy_term_reference', ); if (!$info || !in_array($info['type'], $supported_summary_fields)) { 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; } /** * Convert an rrule array to the iCalcreator format. * * iCalcreator expects the BYDAY element to be an array like this: * (array) ( [([plus] ordwk / minus ordwk)], "DAY" => weekday ) * * But the way that the Date API gives it to us is like this: * (array) ( [([plus] ordwk / minus ordwk)]weekday ) */ function _date_ical_convert_rrule_for_icalcreator($rrule) { $new_rrule = array(); foreach ($rrule as $key => $value) { if (strtoupper($key) == 'DATA') { // iCalcreator doesn't expect the 'DATA' key that the Date API gives us. continue; } if (strtoupper($key) == 'UNTIL') { // iCalcreator expects the 'timestamp' to be array key for UNTIL. $value['timestamp'] = strtotime($value['datetime']); } if (strtoupper($key) == 'BYDAY') { $new_byday = array(); foreach ($value as $day) { // Fortunately, the weekday values are always 2 characters. $weekday = substr($day, -2); $ordwk = substr($day, 0, -2); $new_byday[] = array($ordwk, 'DAY' => $weekday); } $value = $new_byday; } $new_rrule[$key] = $value; } return $new_rrule; }