$view) { $items[] = array( 'path' => 'admin/settings/calendar/'. $view_name .'/ical', 'title' => t('iCal'), 'description' => t('iCal setup.'), 'access' => user_access('administer views'), 'callback' => 'drupal_get_form', 'callback arguments' => array('calendar_ical_setup_form', $view_name), 'type' => MENU_LOCAL_TASK, 'weight' => 6, ); } } return $items; } function calendar_ical_setup_form($view_name) { require_once('./'. drupal_get_path('module', 'calendar') .'/calendar_ical_admin.inc'); return _calendar_ical_setup_form($view_name); } /** * Identify the cache where the ical feeds are stored. * * @return unknown */ function calendar_ical_cache() { return 'cache_views'; } /** * Implementation of hook_cron(). */ function calendar_ical_cron() { cache_clear_all('calendar_feeds_', calendar_ical_cache(), TRUE); } /** * Implementation of hook_calendar_add_items(). */ function calendar_ical_calendar_add_items($view) { require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_ical.inc'); $items = array(); $feeds = calendar_ical_add_feeds($view); $min = date_format($view->min_date, DATE_FORMAT_DATETIME); $max = date_format($view->max_date, DATE_FORMAT_DATETIME); foreach ($feeds as $feed) { if (!empty($feed->calendar_start) && !empty($feed->calendar_end)) { if ($feed->calendar_start >= date_format($view->min_date, DATE_FORMAT_DATETIME) && $feed->calendar_end <= date_format($view->max_date, DATE_FORMAT_DATETIME)) { if (($feed->calendar_start >= $min && $feed->calendar_end <= $max) || ($feed->calendar_start >= $min && $feed->all_day == 1)){ // We have to add a calendar start and end object to the feed, // the cached date objects will not work correctly. $feed->calendar_start_date = date_make_date($feed->calendar_start, $feed->timezone); $feed->calendar_end_date = date_make_date($feed->calendar_end, $feed->timezone); $items[] = $feed; } } } } return $items; } /** * Implementation of hook_calendar_add_types(). */ function calendar_ical_calendar_add_types($view) { return array('calendar_ical' => t('Calendar: iCal Feed')); } /** * Bring an ical feed into the calendar. * * @param $view - the view being manipulated * @param $refresh - whether or not to force a refresh of the feed * @return $nodes to add to calendar * @todo probably need to add more validation of the results in case the url doesn't work. */ function calendar_ical_add_feeds($view, $refresh = FALSE) { $nodes = array(); $feeds = variable_get('calendar_feeds_'. $view->name, array()); $expire = intval(variable_get('calendar_ical_expire_'. $view->name, 9676800) + time()); foreach ($feeds as $delta => $feed) { if ($view->build_type == 'page') { $GLOBALS['calendar_stripe_labels'][$feed['stripe']] = $feed['name']; } if (!$refresh && $cached = cache_get('calendar_feeds_'. $view->name .':'. $feed['url'], calendar_ical_cache())) { $nodes += (array) unserialize($cached->data); } else { $expire = intval(variable_get('calendar_ical_expire_'. $view->name, 9676800) + time()); require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_ical.inc'); switch ($feed['type']) { case 'ical': $filename = $feed['url']; $default = $feed['link']; $new_nodes = array(); $calendars = date_ical_import($filename); if (empty($calendars)) { continue; } foreach ($calendars as $calendar) { if (empty($calendar) || empty($calendar['VEVENT'])) { continue; } foreach ($calendar['VEVENT'] as $key => $value) { if (empty($value['DTSTART'])) continue; $node = new stdClass(); $node->nid = $value['UID']; $node->title = $value['SUMMARY']; $node->label = $feed['name']; $node->teaser = $value['DESCRIPTION']; $node->timezone = $value['DTSTART']['tz']; $node->calendar_start_date = date_ical_date($value['DTSTART'], $value['DTSTART']['tz']); $node->calendar_start = date_format_date($node->calendar_start_date, 'custom', DATE_FORMAT_DATETIME); $node->calendar_end_date = date_ical_date($value['DTEND'], $value['DTEND']['tz']); $node->calendar_end = date_format_date($node->calendar_end_date, 'custom', DATE_FORMAT_DATETIME); $show_tz = ' e'; $granularity = $value['DTSTART']['type'] == 'DATE' ? array('year', 'month', 'day') : array('year', 'month', 'day', 'hour', 'minute', 'second'); $node->format_time = variable_get('calendar_time_format_'. $view->name, 'H:i'); $node->format = date_limit_format(variable_get('date_format_short', 'm/d/Y - H:i'), $granularity) . $show_tz; $node->all_day = $value['all_day']; $node->location = $value['LOCATION']; $node->stripe = $feed['stripe']; $node->stripe_label = $feed['name']; $node->remote = TRUE; $node->uid = $value['uid'] ? $value['UID'] : calendar_get_node_link($node, $default); $node->url = $value['URL'] ? $value['URL'] : calendar_get_node_link($node, $default); $node->calendar_node_theme = 'calendar_ical_node'; $node->calendar_field_theme = 'calendar_ical_field'; $new_nodes[$value['UID']] = $node; } } if (!empty($new_nodes)) { cache_set('calendar_feeds_'. $view->name .':'. $feed['url'], calendar_ical_cache(), serialize($new_nodes), $expire); $nodes += (array) $new_nodes; } break; } } } return $nodes; } /** * Provide views plugins for the feed types we support. */ function calendar_ical_views_style_plugins() { return array( 'calendar_ical' => array( 'name' => t('Calendar: iCal Feed'), 'theme' => 'calendar_ical_feed', 'needs_table_header' => TRUE, 'needs_fields' => TRUE, 'even_empty' => TRUE, ), ); } /** * While we support the global selector, some might want to allow * ONLY ical feeds so we support a stingy selector too */ function calendar_ical_views_arguments() { $arguments = array( 'calendar_ical' => array( 'name' => t('Calendar: iCal Feed'), 'handler' => 'views_handler_arg_ical', 'help' => t('Add this as the last argument to a calendar view to provide an iCal feed of the view.'), ), ); return $arguments; } /** * handler for our own ical argument; mimics the feed selector */ function views_handler_arg_ical($op, &$query, $argtype, $arg = '') { switch ($op) { case 'summary': case 'sort': case 'link': case 'title': break; case 'filter': // This is a clone of the default selector, but it just invokes ours // rather than calling all of them. calendar_ical_views_feed_argument('argument', $GLOBALS['current_view'], $arg, $argtype); } } /** * post view for our own op -- mimics the feed selector */ function calendar_ical_views_post_view($view, $items, $output) { foreach ($view->argument as $id => $argument) { if ($argument['type'] == 'calendar_ical') { $feed = $id; break; } } if ($feed !== NULL) { $query = NULL; return calendar_ical_views_feed_argument('post_view', $view, 'ical', $query); } } /** * feed argument hook that will convert us to ical or display an icon. * the 4th argument isn't part of the hook, but we use it to differentiate * when called as a hook or when called manually from calendar_ical_views_post_view */ function calendar_ical_views_feed_argument($op, &$view, $arg, $argtype = NULL) { if ($op == 'argument' && $arg == 'ical') { // Keep devel module from appending queries to ical export. $GLOBALS['devel_shutdown'] = FALSE; $view->page_type = 'calendar_ical'; // reset the 'real url' to the URL without the feed argument. $view_args = array(); $max = count($view->args); foreach ($view->args as $id => $view_arg) { ++$count; if ($view_arg == $arg && $view->argument[$id]['id'] == $argtype['id']) { if ($count != $max) { $view_args[] = $argtype['wildcard']; } } else { $view_args[] = $view_arg; } } $url = calendar_get_url($view, $args, FALSE); $view->feed_url = $url; } else if ($op == 'post_view') { $args = calendar_ical_post_view_make_args($view, $arg, 'ical'); $url = calendar_get_url($view, $args, FALSE); $title = views_get_title($view, 'page', $args); if ($view->used_filters) { $filters = drupal_query_string_encode($view->used_filters); } return implode(calendar_ical_add_ical(url($url, $filters), $title)); } } /** * helper function -- this function builds a URL for a given feed. * It defaults to the built in feed selector, but the 3rd arg can * be used to set it up for custom selectors too. */ function calendar_ical_post_view_make_args($view, $feed_id, $arg) { // assemble the URL $args = array(); foreach ($view->argument as $id => $argdata) { if (isset($view->args[$id])) { $args[] = $view->args[$id]; } else { if ($argdata['id'] == $feed_id && !in_array($arg, $args)) { $args[] = $arg; } else if ($argdata['argdefault'] != 1 && !in_array($arg, $args)) { $args[] = $arg; } } } return $args; } function calendar_ical_add_ical($url = NULL, $title = '') { if (!is_null($url)) { $stored_feed_links[$url] = theme('ical_icon', $url); drupal_add_link(array('rel' => 'alternate', 'type' => 'application/calendar', 'title' => $title, 'href' => $url)); } return $stored_feed_links; } function theme_ical_icon($url) { if ($image = theme('image', drupal_get_path('module', 'date_api') .'/images/ical16x16.gif', t('Add to calendar'), t('Add to calendar'))) { return '
'. $image .'
'; } } /** * plugin that actually displays an ical feed */ function theme_calendar_ical_feed($view, $items, $type) { if ($type == 'block') { return; } drupal_set_header('Content-Type: text/calendar; charset=utf-8'); drupal_set_header('Content-Disposition: attachment; filename="calendar.ics"; '); $display_formats = variable_get('calendar_display_format_'. $view->name, array('year' => 'calendar', 'month' => 'calendar', 'week' => 'calendar', 'day' => 'calendar', 'block' => 'calendar')); $events = array(); include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_ical.inc'); include_once('./'. drupal_get_path('module', 'calendar') .'/calendar.inc'); $items = calendar_build_nodes($view, $items, $display_formats[$view->calendar_type]); foreach ($items as $time) { foreach ($time as $node) { $event = array(); // Allow modules to affect item fields node_invoke_nodeapi($node, 'ical item'); if (!$node->remote) { $real_node = node_load($node->nid); $node->teaser = $real_node->teaser; } $event['start'] = $node->calendar_start_date; $event['end'] = $node->calendar_end_date; $event['location'] = $node->calendar_location; $event['summary'] = $node->title; $event['description'] = $node->teaser; $event['url'] = $node->url ? $node->url : calendar_get_node_link($node); $event['uid'] = !empty($node->date_id) ? $node->date_id : $event['url']; $events[] = $event; } } $headertitle = filter_xss_admin(views_get_title($view, 'page')); $title = variable_get('site_name', 'Drupal'); $description = $headertitle . ($title ? ' | '. $title : ''); print date_ical_export($events, $description); module_invoke_all('exit'); exit; } /** * Theme for an entire ical node. */ function theme_calendar_ical_node($node, $view) { // Check plain may leave html entities in the title. $node->title = html_entity_decode(check_plain($node->title), ENT-QUOTES); $node->teaser = check_markup($node->teaser); if ($view->calendar_display == 'calendar') { // Remote nodes may come in with lengthy descriptions that won't fit // in small boxes of year, month, and week calendars. if ($view->calendar_type != 'day') { $node->teaser = ''; } $theme = 'calendar_node_'. $view->calendar_type; return theme($theme, $node, $view); } else { // Create a pseudo node view for this item. $field = array(); $output = '
'; $output .= '

'. l($node->title, $node->url) .'

'; $output .= '
'. theme('calendar_date_combo', $field, $node, $node->format, t('Dates'), $view) .'
'; $output .= '
'. $node->teaser .'
'; $output .= '
'; return $output; } } /** * Views field theme for an ical field. * * Used for table and list views. * * For non-calendar views that use fields, the field needs a value from the * imported node that matches the kind of field in the view. Most possible * Views fields make no sense here and will return nothing. */ function theme_calendar_ical_field($fieldname, $fields, $field, $node, $view, $type) { static $datefield; // Find the first date field in the view to attach the ical date to. // There may be more than one date field in the view and we only // want to put the ical field in one place. if (empty($datefield)) { $calendar_fields = calendar_fields(); foreach ($view->field as $viewfield) { if (in_array($viewfield['field'], array_keys($calendar_fields))) { $datefield = $viewfield['queryname']; break; } } } // Check plain may leave html entities in the title. $node->title = html_entity_decode(check_plain($node->title), ENT-QUOTES); $node->teaser = check_markup($node->teaser); // Fix some common html entities that may not get decoded correctly. // You can add more of them in this array. $replace = array( ''' => "'", ); foreach (array('title', 'teaser') as $key) { $node->$key = strtr($node->$key, $replace); } if ($fieldname == $datefield) { return theme('calendar_date_combo', $field, $node, '', $view); } switch ($field['fullname']) { case 'node.title': return l($node->title, $node->url); case 'node.teaser': case 'node.body': return $node->teaser; case 'node.type': return $node->label; } return; }