calendar_ical.module in Calendar 5.2
Same filename and directory in other branches
Adds ical functionality to Calendar.
File
calendar_ical.moduleView source
<?php
/**
* @file
* Adds ical functionality to Calendar.
*/
/**
* Implementation of hook_menu().
*/
function calendar_ical_menu($may_cache) {
$items = array();
if (!$may_cache) {
drupal_add_css(drupal_get_path('module', 'calendar') . '/calendar.css');
require_once './' . drupal_get_path('module', 'calendar') . '/calendar.module';
foreach (calendar_info() as $view_name => $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 '<div style="text-align:right"><a href="' . check_url($url) . '" class="ical-icon" title="ical">' . $image . '</a></div>';
}
}
/**
* 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 = '<div class="node">';
$output .= '<h2>' . l($node->title, $node->url) . '</h2>';
$output .= '<div>' . theme('calendar_date_combo', $field, $node, $node->format, t('Dates'), $view) . '</div>';
$output .= '<div class="content">' . $node->teaser . '</div>';
$output .= '</div>';
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;
}
Functions
Name | Description |
---|---|
calendar_ical_add_feeds | Bring an ical feed into the calendar. |
calendar_ical_add_ical | |
calendar_ical_cache | Identify the cache where the ical feeds are stored. |
calendar_ical_calendar_add_items | Implementation of hook_calendar_add_items(). |
calendar_ical_calendar_add_types | Implementation of hook_calendar_add_types(). |
calendar_ical_cron | Implementation of hook_cron(). |
calendar_ical_menu | Implementation of hook_menu(). |
calendar_ical_post_view_make_args | 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. |
calendar_ical_setup_form | |
calendar_ical_views_arguments | While we support the global selector, some might want to allow ONLY ical feeds so we support a stingy selector too |
calendar_ical_views_feed_argument | 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 |
calendar_ical_views_post_view | post view for our own op -- mimics the feed selector |
calendar_ical_views_style_plugins | Provide views plugins for the feed types we support. |
theme_calendar_ical_feed | plugin that actually displays an ical feed |
theme_calendar_ical_field | Views field theme for an ical field. |
theme_calendar_ical_node | Theme for an entire ical node. |
theme_ical_icon | |
views_handler_arg_ical | handler for our own ical argument; mimics the feed selector |