You are here

calendar.module in Calendar 5

Adds calendar filtering and displays to Views.

File

calendar.module
View source
<?php

/**
 * @file
 * Adds calendar filtering and displays to Views.
 */

/**
 * Implementation of hook_help().
 */
function calendar_help($section) {
  switch ($section) {
    case 'admin/help#calendar':
      return t("<p>View complete documentation at !link.</p>", array(
        '!link' => 'http://drupal.org/node/120710',
      ));
  }
}

/**
 *  Implementation of hook_menu().
 */
function calendar_menu($may_cache) {
  $items = array();
  if (!$may_cache) {
    include_once './' . drupal_get_path('module', 'calendar') . '/calendar.theme';
    drupal_add_css(drupal_get_path('module', 'calendar') . '/calendar.css');
    define('CALENDAR_EMPTY_ARG', variable_get('calendar_empty_arg', 'all'));
    foreach (calendar_info() as $view_name => $view) {
      $parts = explode('/', $view['url']);
      foreach ($parts as $delta => $part) {
        if (in_array($part, array(
          '$arg',
          '$node',
          '$user',
          '$group',
        ))) {
          $parts[$delta] = arg($delta);
        }
      }
      $items[] = array(
        'path' => implode('/', $parts) . '/setup',
        'title' => t('Setup'),
        'description' => t('Calendar setup.'),
        'access' => user_access('administer views'),
        'callback' => 'drupal_get_form',
        'callback arguments' => array(
          'calendar_setup_form',
          $view_name,
        ),
        'type' => MENU_LOCAL_TASK,
        'weight' => 5,
      );
    }
  }
  return $items;
}

/**
 * Implementation of hook_form_alter().
 * Make sure calendar_info() and calendar_fields() get updated.
 */
function calendar_form_alter($form_id, &$form) {
  if ($form_id == 'views_edit_view') {
    $form['#submit'] = array_merge($form['#submit'], array(
      'calendar_clear_all' => array(),
    ));
  }
}

/**
 * Implementation of hook_views_tabs().
 */
function calendar_views_tabs($op) {
  switch ($op) {
    case 'names':
      return array(
        'setup',
      );
  }
}

/**
 * Helper function for loading date_api.
 */
function calendar_load_date_api() {
  include_once drupal_get_path('module', 'date_api') . '/date.inc';
  include_once drupal_get_path('module', 'date_api') . '/date_timezones.inc';
}

/**
 * Helper function for loading calendar_api.
 */
function calendar_load_calendar_api() {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_api.inc';
}

/**
 * Call hooks in other modules to add other items to a calendar view.
 *
 * @param object $view
 * @param string $type - display type, 'table', 'teasers', 'nodes', 'list', 'calendar'
 * @return array of additional items to add.
 */
function calendar_add_items($view, $type) {
  $new_items = array();
  foreach (module_implements('calendar_add_items') as $module) {
    $function = $module . '_calendar_add_items';
    if (function_exists($function)) {
      $feeds = $function($view);

      // Calendar items are just added as objects, themeing will be done by the calendar api.
      // For other types, theme the items and add them.
      foreach ($feeds as $feed) {
        if ($type == 'calendar') {
          $new_items[] = $feed;
        }
        else {
          switch ($type) {
            case 'table':
              $output = array(
                'data' => array(
                  theme('calendar_node_month', $feed),
                ),
              );
              break;
            case 'nodes':
            case 'teasers':
            case 'list':
              $output = theme('calendar_node_day', $feed);
              break;
          }
          if (!empty($output)) {
            $new_items[] = $output;
          }
        }
      }
    }
  }
  return $new_items;
}

/**
 * The workhorse function that takes the beginning array of items and alters it
 *   to an array of calendar nodes that the theme can handle.
 */
function calendar_get_nodes(&$view, &$items, $type) {
  calendar_load_calendar_api();
  calendar_load_date_api();
  $view->nodes_per_page = 0;
  $type_names = node_get_types('names');
  unset($params['limit']);
  if ($type == 'block' || $view->calendar_type == 'year') {
    $params['mini'] = TRUE;
  }
  $fields = calendar_fields();
  $calendar_fields = (array) array_keys($fields);
  $nodes = array();
  $i = 0;
  $items_in = $items;

  // explode out field and format info from the view
  foreach ($view->field as $delta => $data) {
    if (in_array($data['field'], $calendar_fields)) {
      $option = $fields[$data['field']];
      $field_type = strstr($option['type'], 'string') ? 'string' : 'timestamp';
      $field_function = strstr($option['type'], 'cck') ? 'content_format' : $data['handler'];
      $field_formatter = $data['options'];
      $field_field = $option['query_name'];
      $field_end = $field_field . '2';
      $field_field_name = $option['field_name'];
      $timestamp_fromto = $option['timestamp_fromto'];
      $string_fromto = $option['string_fromto'];
      $field_id = $delta;
      $tz_handling = $option['tz_handling'];
      $offset_field = str_replace('.', '_', $option['offset_field']);
      $label = $data['label'];
      $granularity = $option['granularity'];
      $view_fields = _views_get_fields();

      // iterate through the $items array returned by the query and create date or pseudo date nodes
      foreach ($items as $delta => $item) {

        // we have to serialize and unserialize to force a completely new copy of $item when duplicate fields use the same node
        // without doing this, values added to the item in later iterations get applied to earlier ones
        $node = unserialize(serialize($item));
        $node->title = $node->node_title;
        $node->label = $label;
        foreach ($view->field as $field) {
          if (!in_array($field['field'], $calendar_fields) && $field['field'] != 'title') {
            if ($view_fields[$field['id']]['visible'] !== FALSE) {
              $node->fields[$field['queryname']] = views_theme_field('views_handle_field', $field['queryname'], $view_fields, $field, $node, $view);
            }
          }
        }

        // If we're dealing with an event node, join the start and to dates together in one node and get rid of the other
        if (module_exists('event') && ($field_field == 'event_event_start' || $field_field == 'event_event_end') && !$event_field_processed[$item->nid]) {
          if ($node->event_event_start > 0) {
            $node->calendar_start = $node->event_event_start;
            $node->calendar_end = $node->event_event_end;
            $event_field_processed[$item->nid] = TRUE;
          }
        }
        elseif (module_exists('event') && ($field_field == 'event_event_start' || $field_field == 'event_event_end') && $event_field_processed[$item->nid]) {

          // if more than one event field was added to the view (like start and end dates)
          // don't process it more than once
          unset($node);
        }
        if (isset($node) && !isset($node->calendar_start) && !isset($item->{$field_field})) {

          // if no date for the node and no date in the item
          // there is no way to display it on the calendar
          unset($node);
        }
        if (isset($node) && !$node->calendar_start && $item->{$field_field}) {

          // if calendar_start field holds a numeric value, treat it as a unix timestamp
          // if string, convert to timestamp
          if ($field_type == 'timestamp') {
            $node->calendar_start = $item->{$field_field};
            $node->calendar_end = $item->{$field_end} ? $item->{$field_end} : $item->{$field_field};
          }
          else {

            // get the timestamp value for this date, use UTC to make sure no timezone conversion gets done on it
            $node->calendar_start = date_iso2unix($item->{$field_field});
            $node->calendar_end = $item->{$field_end} ? date_iso2unix($item->{$field_end}) : date_iso2unix($item->{$field_field});
          }
        }
        if (isset($node)) {

          // get appropriate timezone offset
          switch ($tz_handling) {
            case 'user':
              global $user;
              $node->start_offset = $node->end_offset = $user->timezone;
              break;
            case 'none':
            case 'GMT':
              $node->start_offset = $node->end_offset = 0;
              break;
            case 'date':
              $node->start_offset = $node->end_offset = $node->{$offset_field};
              break;
            case 'event':
              $node->start_offset = date_get_offset($node->event_timezone, $node->event_event_start);
              $node->end_offset = date_get_offset($node->event_timezone, $node->event_event_end);
              break;
            default:
              $timezone = variable_get('date_default_timezone_name', 'UTC');
              $node->start_offset = date_offset(date_unix2array($node->calendar_start), $timezone);
              $node->end_offset = date_offset(date_unix2array($node->calendar_end), $timezone);
              break;
          }
        }
        if (isset($node) && function_exists($field_function) && $node->calendar_start && $item->{$field_field}) {
          if ($field_function == 'content_format') {

            // force the original value for this field into the array that content_format expects
            $node->start_format = content_format($field_field_name, array(
              'value' => $item->{$field_field},
              'value2' => $item->{$field_field},
            ), $field_formatter);
            if ($node->calendar_end) {
              $node->end_format = content_format($field_field_name, array(
                'value' => $item->{$field_end},
                'value2' => $item->{$field_end},
              ), $field_formatter);
            }
          }
          else {

            // or call date format function
            if (!$node->start_format) {
              $node->start_format = $field_function(NULL, NULL, $item->{$field_field}, NULL);
              if ($node->calendar_end && !$node->end_format) {
                $node->end_format = $field_function(NULL, NULL, $node->calendar_end, NULL);
              }
            }
          }

          // format a time-only display for the month calendar for dates that have time elements
          if (array_intersect($granularity, array(
            'H',
            'N',
            'S',
          ))) {
            $node->start_time_format = date_format_date(variable_get('calendar_time_format_' . $view->name, 'H:i'), intval($node->calendar_start + $node->start_offset));
            if ($node->calendar_end) {
              $node->end_time_format = date_format_date(variable_get('calendar_time_format_' . $view->name, 'H:i'), intval($node->calendar_end + $node->end_offset));
            }
          }
          else {
            $node->start_time_format = $node->start_format;
            $node->end_time_format = $node->end_format;
          }
          if ($node) {

            // we can have multiple representations with the same nid, like multi-day values
            // or different fields that refer to the same node
            // create a unique id so it gets displayed in the calendar
            // Add delta to key to make multiple value CCK fields display as separate items.
            if (strstr($option['type'], 'cck')) {
              $id = $item->nid . ':' . $delta . ':' . $field_field;
            }
            else {
              $id = $item->nid . ':0:' . $field_field;
            }
            $node->nid = $id;
            if ($view->build_type == 'page' && $view->calendar_type != 'year') {
              $node->stripe = calendar_node_stripe($view, $node, $option['query_name'], $field_field);
            }
            $nodes[$id] = $node;
            unset($node);
          }
        }
      }
    }
  }

  // make sure there is at least one item in $nodes to force the calendar to display
  // set the hour to 12 to minimize timezone adjustments that might push it to previous or next day
  if ($view->calendar_type == 'year') {

    // for the year view to work, must have at least one node in each month
    for ($i = 1; $i < 13; $i++) {
      $nodes[] = _calendar_make_node(NULL, NULL, _views_get_timezone(), $view->year, $i, 1, 12, 0);
    }
  }
  elseif ($view->calendar_type == 'week') {

    // make sure at least one node is created for the current week
    // add both start and end of week in case week crosses from one month to next
    $week_range = calendar_week_range($view->year, $view->week);
    $nodes[] = _calendar_make_node(NULL, NULL, _views_get_timezone(), date_format_date('Y', $week_range[0]), date_format_date('m', $week_range[0]), date_format_date('j', $week_range[0]), 12, 0);
    $nodes[] = _calendar_make_node(NULL, NULL, _views_get_timezone(), date_format_date('Y', $week_range[1]), date_format_date('m', $week_range[1]), date_format_date('j', $week_range[1]), 12, 0);
  }
  elseif (sizeof($nodes) == 0) {

    // otherwise add a blank node for the current day
    $nodes = array(
      _calendar_make_node(NULL, NULL, _views_get_timezone(), $view->year, $view->month, $view->day, 12, 0),
    );
  }
  if (calendar_part_is_valid($view->year, 'year')) {

    // valid year is a test that indicates if arguments were available to establish a date for the calendar
    // a full view with an argument date will display a single month, day or week calendar page
    // with navigation that mimics regular calendar
    // trim off date values that are outside the selected range to prevent display of incomplete extra calendars
    $params['limit'] = _calendar_limit_nodes($nodes, $view->calendar_type, $view->year, $view->month, $view->day, $view->week, _views_get_timezone());

    // hide the intermediate header rows created by the event api and
    // push title and navigation into calendar header
    $params['hide_header'] = $view->calendar_type == 'week' ? false : true;

    //$title = theme('calendar_nav_wrapper', calendar_nav($view, $params['mini']), array());

    // standard api displays a whole month instead of a single week
    // adjust here for single week display
    if ($view->calendar_type == 'week' && $view->week) {
      $params['force_week'] = $view->week;
    }
  }

  // use calendar_get_calendar api to draw the calendar
  $params['url'] = calendar_real_url($view, $view->args);
  $params['append'] = calendar_url_append($view);
  $params['stripe'] = 'stripe';
  $params['with_weekno'] = $view->build_type == 'block' || $view->calendar_type == 'year' || $view->year < 1970 ? FALSE : TRUE;
  return array(
    'nodes' => $nodes,
    'params' => $params,
  );
}

/**
 *  Function to construct back and next navigation from views arguments
 */
function calendar_nav($view, $mini = FALSE) {
  if (!calendar_part_is_valid($view->year, 'year')) {
    return array(
      $view->subtitle,
    );
  }
  calendar_load_date_api();
  if ($view->calendar_type == 'week' && calendar_part_is_valid($view->week, 'week')) {
    $range = calendar_week_range($view->year, $view->week + 1);
    $cur_stamp = $range[0];
  }
  else {
    $cur_stamp = date_gmmktime(array(
      'mon' => $view->month ? $view->month : 1,
      'mday' => $view->day ? $view->day : 1,
      'year' => $view->year ? $view->year : date_format_date("Y", date_time()),
    ));
  }

  // make the navigation into a header, with prev and next links
  // use the calendar_nav themes to mimic standard calendar navigation
  $paths = calendar_get_paths($cur_stamp, $view);
  $prev_path = implode('/', array_reverse($paths[0]));
  $next_path = implode('/', array_reverse($paths[1]));
  $prev_query = $next_query = array();
  if ($_GET['view']) {
    $prev_query[] = 'view=' . $_GET['view'];
    $next_query[] = 'view=' . $_GET['view'];
  }

  // for the mini calendar in a block, treat the url as a querystring to avoid actually changing the page
  if ($mini && $view->calendar_type == 'month') {
    $prev_query[] = 'mini=' . $prev_path;
    $prev_path = $_GET['q'];
    $next_query[] = 'mini=' . $next_path;
    $next_path = $_GET['q'];
  }
  $prev_query[] = calendar_url_append($view);
  $next_query[] = calendar_url_append($view);
  $header = array();
  $header[] = array(
    'data' => theme('calendar_nav_prev', $prev_path, $mini ? FALSE : TRUE, implode('&', $prev_query)),
    'class' => 'prev',
  );
  $header[] = array(
    'data' => $view->subtitle,
    'class' => 'heading',
    'colspan' => 5,
  );
  $header[] = array(
    'data' => theme('calendar_nav_next', $next_path, $mini ? FALSE : TRUE, implode('&', $next_query)),
    'class' => 'next',
  );
  return $header;
}
function calendar_get_paths($cur_stamp, $view) {
  calendar_load_date_api();
  $path = array();

  // build an array of the current path and its parts
  $i = 0;
  $path[$i] = array(
    'path' => $view->real_url,
    'type' => 'url',
  );
  foreach ($view->argument as $delta => $arg) {
    if ($view->args[$delta]) {
      $i++;
      $pathtype = str_replace('calendar_', '', $arg['type']);
      $path[$i] = array(
        'path' => $view->args[$delta] != CALENDAR_EMPTY_ARG ? $view->{$pathtype} : CALENDAR_EMPTY_ARG,
        'type' => $pathtype,
      );
    }
  }

  // if there are other arguments after the view arguments, add them to the navigation links
  while ($i < sizeof($view->args)) {
    $i++;
    $path[$i] = array(
      'path' => $view->args[intval($i - 1)],
      'type' => '',
    );
  }

  // reverse through the path, creating a $nextpath and $prevpath arrays
  for ($x = $i; $x >= 0; $x--) {
    if ($path[$x]['path'] != CALENDAR_EMPTY_ARG) {
      switch ($path[$x]['type']) {
        case 'day':
          $day = $path[$x]['path'];
          $next_stamp = $cur_stamp + 86400;
          $prev_stamp = $cur_stamp - 86400;
          $nextpath[$x] = date_format_date('j', $next_stamp);
          $prevpath[$x] = date_format_date('j', $prev_stamp);
          break;
        case 'week':
          $week = $path[$x]['path'];
          $year = $view->year;
          if (!$next_stamp) {
            $cal_range = calendar_week_range($year, $week);
            $next_stamp = $cal_range[1] + 1;
            $prev_stamp = $cal_range[0] - 1;
          }
          $week_year = calendar_week_year($next_stamp);
          $next_year = $week_year[1];
          $nextpath[$x] = 'W' . sprintf("%02d", $week_year[0]);
          $week_year = calendar_week_year($prev_stamp);
          $prev_year = $week_year[1];
          $prevpath[$x] = 'W' . sprintf("%02d", $week_year[0]);
          break;
        case 'month':
          $month = $path[$x]['path'];
          $year = $view->year;
          $next_year = $month < 12 ? $year : $year + 1;
          $prev_year = $month > 1 ? $year : $year - 1;
          if (!$next_stamp) {
            $next_stamp = date_gmmktime(array(
              'mon' => $month < 12 ? $month + 1 : 1,
              'mday' => 1,
              'year' => $next_year,
            ));
            $prev_stamp = date_gmmktime(array(
              'mon' => $month > 1 ? $month - 1 : 12,
              'mday' => 1,
              'year' => $prev_year,
            ));
          }
          $nextpath[$x] = date_format_date('n', $next_stamp);
          $prevpath[$x] = date_format_date('n', $prev_stamp);
          break;
        case 'year':
          $year = $view->year;
          if (!$next_stamp) {
            $next_stamp = date_gmmktime(array(
              'mon' => 1,
              'mday' => 1,
              'year' => $next_year ? $next_year : $year + 1,
            ));
            $prev_stamp = date_gmmktime(array(
              'mon' => 12,
              'mday' => 1,
              'year' => $prev_year ? $prev_year : $year - 1,
            ));
          }
          $nextpath[$x] = date_format_date('Y', $next_stamp);
          $prevpath[$x] = date_format_date('Y', $prev_stamp);
          break;
        default:

          // all other arguments are just passed through
          if ($path[$x]['path']) {
            $nextpath[$x] = $path[$x]['path'];
            $prevpath[$x] = $path[$x]['path'];
          }
          break;
      }
    }
    else {
      $nextpath[$x] = $path[$x]['path'];
      $prevpath[$x] = $path[$x]['path'];
    }
  }
  return array(
    $prevpath,
    $nextpath,
  );
}

/**
 *  A function to create a blank date to force a calendar display when there is no data
 */
function _calendar_make_node($node = NULL, $timestamp = NULL, $offset = NULL, $year = NULL, $month = NULL, $day = NULL, $hour = NULL, $minute = NULL) {
  calendar_load_date_api();
  $offset = $offset ? $offset : _views_get_timezone();
  if (!$timestamp) {
    $year = calendar_part_is_valid($year, 'year') ? $year : date_format_date('Y', date_time());
    $month = calendar_part_is_valid($month, 'month') ? $month : date_format_date('m', date_time());
    $day = calendar_part_is_valid($day, 'day') ? $day : date_format_date('j', date_time());
    $hour = calendar_part_is_valid($hour, 'hour') ? $hour : date_format_date('H', date_time());
    $minute = calendar_part_is_valid($minute, 'minute') ? $minute : date_format_date('i', date_time());
    while (!checkdate($month, $day, $year)) {
      $day--;
    }

    // Account for days that don't exist
    $timestamp = date_gmmktime(array(
      'hours' => $hour,
      'minutes' => $minute,
      'mon' => $month,
      'mday' => $day,
      'year' => $year,
    ));
  }
  if (!$node) {
    $node = new stdClass();
    $node->nid = 0;
  }
  $node->calendar_start = $timestamp;
  $node->start_offset = $offset;
  $node->calendar_end = $timestamp;
  $node->end_offset = $offset;
  return $node;
}

/**
 *  A function to adjust node values to slice off times before and after the selected view
 *  used for calendars that span days, months, or years since the calendar api
 *  automatically creates additional calendars for calendars that extend into another time period
 *  and the additional calendars will be incomplete (only containing cross-over calendars)
 */
function _calendar_limit_nodes($nodes, $type, $year, $month, $day, $week, $offset) {
  calendar_load_date_api();
  if (!calendar_part_is_valid($day, 'day')) {
    $day = 1;
  }
  if (!calendar_part_is_valid($month, 'month')) {
    $month = date_format_date('m', date_time());
  }
  if (!calendar_part_is_valid($year, 'year')) {
    $year = date_format_date('Y', date_time());
  }
  switch ($type) {
    case 'day':
      $min_date = date_gmmktime(array(
        'mon' => $month,
        'mday' => $day,
        'year' => $year,
      ));
      $max_date = $min_date + 86400 - 1;
      break;
    case 'week':
      return calendar_week_range($year, $week);
    case 'month':
      $min_date = date_gmmktime(array(
        'mon' => $month,
        'mday' => 1,
        'year' => $year,
      ));

      // find the first day of the next month and subtract one day
      if (intval($month) < 12) {
        $max_date = date_gmmktime(array(
          'mon' => intval($month + 1),
          'day' => 1,
          'year' => $year,
          'hours' => 23,
          'minutes' => 59,
          'seconds' => 59,
        ));
      }
      else {
        $max_date = date_gmmktime(array(
          'mon' => 1,
          'mday' => 1,
          'year' => intval($year + 1),
          'hours' => 23,
          'minutes' => 59,
          'seconds' => 59,
        ));
      }
      $max_date -= 86400;
      break;
    case 'year':
      $min_date = date_gmmktime(array(
        'hours' => 0,
        'minutes' => 0,
        'seconds' => 0,
        'mon' => 1,
        'mday' => 1,
        'year' => $year,
      ));
      $max_date = date_gmmktime(array(
        'hours' => 23,
        'minutes' => 59,
        'seconds' => 59,
        'mon' => 12,
        'mday' => 31,
        'year' => $year,
      ));
  }
  return array(
    $min_date,
    $max_date,
  );
}

/**
 *  TODO need to identify type of timezone handling needed for each date field
 */
function calendar_offset($field_name) {
  $default_offset = variable_get('date_default_timezone', 0);
  $configurable_zones = variable_get('configurable_timezones', 1);
}

/**
 *  Custom views handler for the year argument.
 */
function calendar_handler_arg_year($op, &$query, $argtype, $arg = '') {
  if ($op == 'filter' && !empty($arg) && $arg != CALENDAR_EMPTY_ARG) {
    calendar_filter_year($query, $arg);
  }
  return calendar_handler_arg_type($op, $query, $argtype, $arg, 'YEAR');
}

/**
 * Callback for year filter.
 *   Build year, month, day, min, and max into query object.
 *
 * @param object $query
 * @param integer $arg
 */
function calendar_filter_year(&$query, $arg) {
  calendar_load_calendar_api();
  $query->calendar_type = 'year';
  $query->year = calendar_part_is_valid($arg, 'year') ? $arg : calendar_user_date('year');
  $query->month = CALENDAR_EMPTY_ARG;
  $query->day = CALENDAR_EMPTY_ARG;
  $query->min = $query->year;
  $query->max = $query->year;
}

/**
 *  Custom views handler for the month argument.
 */
function calendar_handler_arg_month($op, &$query, $argtype, $arg = '') {
  if ($op == 'filter' && !empty($arg) && $arg != CALENDAR_EMPTY_ARG) {
    calendar_filter_month($query, $arg);
  }
  return calendar_handler_arg_type($op, $query, $argtype, $arg, 'MONTH');
}

/**
 * Callback for month filter.
 *   Build year, month, day, min, and max into query object.
 *
 * @param object $query
 * @param integer $arg
 */
function calendar_filter_month(&$query, $arg) {
  calendar_load_calendar_api();
  $query->calendar_type = 'month';
  if (!isset($query->year)) {
    calendar_filter_year($query, calendar_user_date('year'));
  }
  $query->month = calendar_part_is_valid($arg, 'month') ? $arg : calendar_user_date('month');
  $query->day = CALENDAR_EMPTY_ARG;
  $query->min .= '-' . sprintf('%02d', $query->month);
  $query->max .= '-' . sprintf('%02d', $query->month);
}

/**
 *  Custom views handler for the day argument.
 */
function calendar_handler_arg_day($op, &$query, $argtype, $arg = '') {
  if ($op == 'filter' && !empty($arg) && $arg != CALENDAR_EMPTY_ARG) {
    calendar_filter_day($query, $arg);
  }
  return calendar_handler_arg_type($op, $query, $argtype, $arg, 'DAY');
}

/**
 * Callback for day filter.
 *   Build year, month, day, min, and max into query object.
 *
 * @param object $query
 * @param integer $arg
 */
function calendar_filter_day(&$query, $arg) {
  calendar_load_calendar_api();
  if (!isset($query->month)) {
    calendar_filter_month($query, calendar_user_date('month'));
  }
  $query->calendar_type = 'day';
  $query->day = calendar_part_is_valid($arg, 'day') ? $arg : calendar_user_date('day');
  $query->min .= '-' . sprintf('%02d', $query->day);
  $query->max .= '-' . sprintf('%02d', $query->day);
}

/**
 *  Custom views handlers for the week argument.
 */
function calendar_handler_arg_week($op, &$query, $argtype, $arg = '') {
  if ($op == 'filter' && !empty($arg) && $arg != CALENDAR_EMPTY_ARG) {
    calendar_filter_week($query, $arg);
  }
  return calendar_handler_arg_type($op, $query, $argtype, $arg, 'WEEK');
}

/**
 * Callback for week filter.
 *   Build year, month, day, min, and max into query object.
 *
 * @param object $query
 * @param integer $arg
 */
function calendar_filter_week(&$query, $arg) {
  calendar_load_calendar_api();
  if (!isset($query->year)) {
    calendar_filter_year($query, calendar_user_date('year'));
  }
  $arg = str_replace('W', '', $arg);
  $query->calendar_type = 'week';
  $query->week = calendar_part_is_valid($arg, 'week') ? $arg : calendar_user_date('week');
  $query->month = calendar_week('start_month', $query, $query->week);
  $query->day = calendar_week('start_day', $query, $query->week);
  $query->min = calendar_week('start_year', $query, $query->week) . '-' . sprintf('%02d', calendar_week('start_month', $query, $query->week)) . '-' . calendar_week('start_day', $query, $query->week) . ' 00:00:00';
  $query->max = calendar_week('end_year', $query, $query->week) . '-' . sprintf('%02d', calendar_week('end_month', $query, $query->week)) . '-' . calendar_week('end_day', $query, $query->week) . ' 23:59:59';
}

/**
 *  Custom views handler for all calendar arguments.
 */
function calendar_handler_arg_type($op, &$query, $argtype, $arg, $field_type) {
  calendar_load_date_api();
  switch ($op) {
    case 'summary':
    case 'link':

      // The query to do summaries when date ranges can include multiple days, months, and years
      // is extremely complex and has been omitted, so summary views with these arguments just will not work.
      // TODO add some kind of validation or warning to keep people from trying to use summary views.
      break;
    case 'filter':

      // Figure out which will be the final calendar argument in this view so we know when to insert the query.
      $view = $GLOBALS['current_view'];
      if ($argtype['type'] == calendar_is_last_arg($view)) {
        $query->calendar_finished = TRUE;
        calendar_build_filter($query, $view);
      }
      break;
    case 'title':

      // Set titles for each argument.
      $value = intval(str_replace('W', '', $arg ? $arg : $query));
      return theme('calendar_arg_title', $field_type, $value, $query);
  }
  return;
}

/**
 * Compile the filter query for this view.
 *
 * @param object $query
 * @param object $view
 */
function calendar_build_filter(&$query, &$view) {
  if (!isset($query->week)) {
    $query->week = calendar_week('week', $query);
  }
  if (!$query->min) {
    return;
  }

  // Add elements to the query min and max values to create a complete date value
  // for the minimum and maximum once all the View's args have been used.
  $minmax = array(
    'year' => array(
      '-01-01 00:00:00',
      "-12-31 23:59:59",
    ),
    'month' => array(
      '-01 00:00:00',
      '-' . sprintf("%02d", date_last_day_of_month($query->month, $query->year)) . ' 23:59:59',
    ),
    'day' => array(
      ' 00:00:00',
      ' 23:59:59',
    ),
    'hour' => array(
      ':00:00',
      ':59:59',
    ),
    'minute' => array(
      ':00',
      ':59',
    ),
  );
  $query->min .= $minmax[$query->calendar_type][0];
  $query->max .= $minmax[$query->calendar_type][1];

  // find all datetime fields in this view and add filters for them to the query
  $queries = array();
  foreach ($view->field as $delta => $field) {
    $query_strings = calendar_build_field_query($query, $field);
    if (!empty($query_strings)) {
      $queries = array_merge($queries, $query_strings);
    }
  }

  // bring the node type into the query so we can use it in the theme
  $query
    ->add_field('type', 'node');
  if ($queries) {
    $query
      ->add_where(implode(" OR ", $queries));
  }
  return;
}

/**
 * Build a filtering query for an individual date field
 *
 * @param object $query - the query object
 * @param array $field - the view field array
 */
function calendar_build_field_query(&$query, $field) {
  $queries = array();
  $fields = calendar_fields();
  $field_name = $field['field'];
  $this_field = $fields[$field_name];
  $view_fields[] = $field_name;
  if (array_key_exists($field_name, $fields)) {
    $query
      ->ensure_table($this_field['table'], $this_field['table']);
    $tz_handling = $this_field['tz_handling'];
    $offset_field = $this_field['offset_field'];
    $field_type = strstr($this_field['type'], 'string') ? 'iso' : 'int';

    // get appropriate timezone offset
    switch ($tz_handling) {
      case 'user':
        global $user;
        $start_offset = $end_offset = $user->timezone;
        break;
      case 'none':
      case 'GMT':
        $start_offset = $end_offset = 0;
        break;
      case 'date':
        $start_offset = $end_offset = $offset_field;
        break;
      case 'event':

      // event-specific timezones won't work right here because no offset is stored in the database
      // the best we can do is treat it the same as if it was a site timezone
      default:
        $start_offset = $end_offset = variable_get('date_default_timezone', 0);
        break;
    }

    // handling for from and to date ranges
    if ($this_field['timestamp_fromto']) {
      $queries[] = "(" . date_sql('DATE', $this_field['timestamp_fromto'][1], $field_type, $end_offset) . " >='" . $query->min . "' AND " . date_sql('DATE', $this_field['timestamp_fromto'][0], $field_type, $start_offset) . " <='" . $query->max . "')";
      $event_field_processed = TRUE;
    }
    elseif ($this_field['string_fromto']) {
      $queries[] = "(" . date_sql('DATE', $this_field['string_fromto'][1], $field_type, $end_offset) . " >='" . $query->min . "' AND " . date_sql('DATE', $this_field['string_fromto'][0], $field_type, $start_offset) . " <='" . $query->max . "')";
      $event_field_processed = TRUE;
    }
    elseif ($this_field['type'] == 'cck_string') {
      $queries[] = "(" . date_sql('DATE', $this_field['fullname'], $field_type, $start_offset) . ">='" . $query->min . "' AND " . date_sql('DATE', $field['fullname'], $field_type, $end_offset) . "<='" . $query->max . "')";
    }
    elseif (strstr($this_field['type'], 'timestamp')) {
      $queries[] = "(" . date_sql('DATE', $this_field['fullname'], $field_type, $start_offset) . ">='" . $query->min . "' AND " . date_sql('DATE', $this_field['fullname'], $field_type, $end_offset) . "<='" . $query->max . "')";
    }
  }
  return $queries;
}

/**
 *  Implementation of hook_views_query()
 *    Insert filters into the query based on the current calendar view and the selected fields
 *    Used when the actual view arguments don't provide enough info to construct the query.
 *    i.e. on a view with no arguments or one with partial arguments like year or year/month.
 *
 *  @param object $query
 *  @param object $view
 */
function calendar_views_query_alter(&$query, &$view) {
  $view->real_args = $view->args;
  $view->real_url = calendar_real_url($view, $view->args);
  if (!calendar_has_calendar_args($view) || empty($view->args) && !calendar_is_calendar_arg($view) && $view->argument[0]['argdefault'] != 2) {
    return;
  }
  calendar_load_calendar_api();
  calendar_load_date_api();

  // make sure block views default to the current month
  // and make sure day view is not selected
  if ($view->build_type == 'block') {
    $query->calendar_type = 'month';
    $view->args = explode('/', str_replace($view->url . '/', '', $_GET['mini']));
    foreach ($view->argument as $delta => $argument) {

      // Special handling for OG gid argument.
      // Find a default value for the gid when used in a block.
      if ($argument['type'] == 'gid') {
        $groupnodes = calendar_og_groups($view);
        $view->args[$delta] = $groupnodes[0];
        $query
          ->ensure_table('og_ancestry');
        $query
          ->add_where("og_ancestry.group_nid IN (%d)", implode(',', $groupnodes));
      }
      if ($argument['type'] == 'calendar_year') {
        if (!$view->args[$delta]) {
          $view->args[$delta] = calendar_user_date('year');
        }
        $query->year = $view->args[$delta];
        calendar_filter_year($query, $query->year);
      }
      elseif ($argument['type'] == 'calendar_month' || $argument['type'] == 'calendar_week') {
        if (!$view->args[$delta]) {
          $view->args[$delta] = calendar_user_date('month');
        }
        $query->month = $view->args[$delta];
        calendar_filter_month($query, $query->month);
      }
      elseif ($argument['type'] == 'calendar_day') {
        $query->day = CALENDAR_EMPTY_ARG;
        $view->args[$delta] = CALENDAR_EMPTY_ARG;
      }
    }
  }

  // Either a month or a week argument could occupy the second position of the group
  // this is done so that a single view has the capability to switch between all calendar layouts
  // to make this work we must make some adjustments to the view
  if ($view->build_type == 'page') {
    $GLOBALS['calendar_is_calendar'] = TRUE;
    if (empty($view->args) || !calendar_is_calendar_arg($view)) {

      // If no arguments are provided, default to current month view.
      $query->calendar_type = 'month';
      foreach ($view->argument as $delta => $argument) {
        if ($argument['type'] == 'calendar_year' && !$view->args[$delta]) {
          $view->args[$delta] = calendar_user_date('year');
          calendar_filter_year($query, calendar_user_date('year'));
        }
        elseif ($argument['type'] == 'calendar_month' && !$view->args[$delta]) {
          $view->args[$delta] = calendar_user_date('month');
          calendar_filter_month($query, calendar_user_date('month'));
        }
        elseif ($argument['type'] == 'calendar_day' && !$view->args[$delta]) {
          $view->args[$delta] = CALENDAR_EMPTY_ARG;
        }
        else {
          $view->args[$delta] = $view->real_args[$delta];
        }
      }
    }
    foreach ($view->argument as $delta => $argument) {
      if (in_array($argument['type'], calendar_args())) {

        // make sure 'display all values' is selected for the calendar arguments
        // summary views are meaningless and create errors in this context
        $view->argument[$delta]['argdefault'] = 2;

        // Pad any unused values in the view arguments with CALENDAR_EMPTY_ARG to indicate all values.
        if (empty($view->args[$delta])) {
          $view->args[$delta] = CALENDAR_EMPTY_ARG;
        }
      }

      // Calendar_week and Calendar_month can swap positions as the second arg in the url.
      // Do some work here to make sure we know which is which and swap view data to match it.
      // the difference between a calendar_month arg and a calendar_week arg is the preceeding 'W'
      if ($argument['type'] == 'calendar_week' || $argument['type'] == 'calendar_month') {
        if (strstr($view->args[$delta], 'W')) {
          calendar_filter_week($query, $view->args[$delta]);
          $view->argument[$delta]['type'] = 'calendar_week';
          $view->argument[$delta]['id'] = 'calendar_week';
          $view->argument[$delta + 1]['type'] = 'calendar_day';
          $view->argument[$delta + 1]['id'] = 'calendar_day';

          // Make sure that there is no day set for the week view.
          $view->args[$delta + 1] = CALENDAR_EMPTY_ARG;
        }
        elseif (!strstr($view->args[$delta], 'W') && $view->build_type == 'page' && $view->argument[$delta]['type'] == 'calendar_week') {
          calendar_filter_month($query, $view->args[$delta]);
          $view->argument[$delta]['type'] = 'calendar_month';
          $view->argument[$delta]['id'] = 'calendar_month';
          $view->argument[$delta + 1]['type'] = 'calendar_day';
          $view->argument[$delta + 1]['id'] = 'calendar_day';
        }
      }
    }
  }

  // Make sure the calendar query gets inserted. May not have finished yet on views like year or year/month.
  if (!$query->calendar_finished) {
    calendar_build_filter($query, $view);
  }
  $view->calendar_type = $query->calendar_type;
  $view->year = $query->year;
  $view->month = $query->month;
  $view->day = $query->day;
  $view->week = $query->week;
}

/**
 *  Implementation of hook_views_pre_view()
 */
function calendar_views_pre_view(&$view, &$items) {

  // If no part of this view has calendar elements, exit.
  if (!calendar_is_calendar($view) || !calendar_has_calendar_args($view)) {
    return;
  }

  // Construct a formatted title for the view from the last calendar argument encountered.
  switch ($view->calendar_type) {
    case 'year':
      $view->subtitle = theme('calendar_nav_title', 'YEAR', $view);
      break;
    case 'month':
      $view->subtitle = theme('calendar_nav_title', 'MONTH', $view);
      break;
    case 'day':
      $view->subtitle = theme('calendar_nav_title', 'DAY', $view);
      break;
    case 'week':
      $view->subtitle = theme('calendar_nav_title', 'WEEK', $view);
      break;
  }

  // If this is a view with calendar arguments but not a calendar view,
  // add navigation to the top of the view and return.
  if (!calendar_is_calendar($view) && calendar_has_calendar_args($view)) {
    return theme('calendar_show_nav', $view, $view->build_type == 'block', calendar_is_calendar($view));
  }

  // If this is a calendar plugin theme view, make sure empty results
  // will produce blank calendar page
  if (array_key_exists($view->page_type, calendar_view_types())) {
    if (!$items && $view->build_type == 'page' && $view->year) {
      $view->page_empty = check_markup($view->page_header, $view->page_header_format) . check_markup($view->page_empty, $view->page_empty_format) . theme('calendar_display', $view, array(), 'page') . check_markup($view->page_footer, $view->page_footer_format);
      $view->page_empty_format = 3;
    }
  }
  if (array_key_exists($view->block_type, calendar_view_types())) {
    if (!$items && $view->build_type == 'block' && $view->year) {
      $view->block_empty = check_markup($view->block_header, $view->block_header_format) . check_markup($view->block_empty, $view->block_empty_format) . theme('calendar_display', $view, array(), 'block') . check_markup($view->block_footer, $view->block_footer_format);
      $view->block_empty_format = 3;
    }
  }
}

/**
 * Implementation of hook_views_post_view().
 *
 * Views automatically sets the page title to the value of the last argument.
 * The calendar module has already created a proper title within the
 * calendar, so override Views to set the page title to match the View title.
 */
function calendar_views_post_view(&$view, $items, $output) {

  // If no part of this view has calendar elements, exit.
  if ($view->build_type != 'page' || !calendar_is_calendar($view) || !calendar_has_calendar_args($view)) {
    return;
  }
  $title = theme('calendar_page_title', $view, $items, $output);
  drupal_set_title($title);
}

/**
 * Get the start and end datestamp for a calendar week.
 */
function calendar_week_range($year, $week) {
  calendar_load_date_api();

  // Get timestamp for January 1 of the requested year.
  $min_date = date_gmmktime(array(
    'mon' => 1,
    'mday' => 1,
    'year' => $year,
  ));

  // Adjust back or forward to the first day of the calendar week for the specified first day of week.
  $dow = date_format_date('w', $min_date);
  $first_day = variable_get('date_first_day', 0);
  $diff = -((7 - $first_day + $dow) % 7);
  $min_date += $diff * 86400;

  // Add the requested number of weeks to that date.
  $min_date += intval(($week - 1) * 604800);

  // Find the end date, which is one week later, less one second.
  $max_date = $min_date + 604800 - 1;
  return array(
    $min_date,
    $max_date,
  );
}

/**
 * Find the calendar week number and year for a date.
 *
 * This is complicated by the fact that the calendar week does not match the ISO week,
 * since the ISO week starts on Monday for the first week that has 4 or more days in the new year
 * while the calendar week starts on the site's preferred first day of the week regardless
 * of the number of days that are in the first week or the year.
 *
 * @param unknown_type $timestamp
 * @return array of calendar week number and year
 */
function calendar_week_year($timestamp) {
  $first_day = variable_get('date_first_day', 0);
  $iso_week = intval(date_format_date('W', $timestamp));
  $year = date_format_date('Y', $timestamp);

  // Where does this timestamp fall within the range for the iso week number.
  $range = calendar_week_range($year, $iso_week);

  // If the timestamp is in the range, the ISO week number is correct.
  if ($timestamp >= $range[0] && $timestamp <= $range[1]) {
    return array(
      $iso_week,
      $year,
    );
  }
  elseif ($timestamp < $range[1]) {
    if ($iso_week >= 2) {
      return array(
        intval($iso_week - 1),
        $year,
      );
    }
    else {
      return array(
        1,
        $year,
      );
    }
  }
  elseif ($timestamp > $range[0]) {
    if ($iso_week < 52) {
      return array(
        intval($iso_week + 1),
        $year,
      );
    }
    else {
      return array(
        1,
        intval($year + 1),
      );
    }
  }
}

/**
 *  Handle a lot of messy week calculations all in one place to make maintenance easier
 */
function calendar_week($op, $view, $week = 0) {
  calendar_load_date_api();
  if ($week == 0) {
    $day = !empty($view->day) && $view->day != CALENDAR_EMPTY_ARG ? $view->day : calendar_user_date('day');
    $month = !empty($view->month) && $view->month != CALENDAR_EMPTY_ARG ? $view->month : calendar_user_date('month');
    $isodate = $view->year . '-' . sprintf('%02d', $month) . '-' . sprintf('%02d', $day) . 'T00:00:00';
    $week_year = calendar_week_year(date_iso2unix($isodate));
    if ($op == 'week') {
      return $week_year[0];
    }
  }
  $range = calendar_week_range($view->year, $week);
  $week_start = $range[0];
  $week_end = $range[1];
  switch ($op) {
    case 'start_year':
      return date_format_date('Y', $week_start);
    case 'end_year':
      return date_format_date('Y', $week_end);
    case 'start_month':
      return date_format_date('n', $week_start);
    case 'end_month':
      return date_format_date('n', $week_end);
    case 'start_day':
      return date_format_date('d', $week_start);
    case 'end_day':
      return date_format_date('d', $week_end);
    default:
      return $week_start;
  }
}

/**
 *  A function to test the validity of various date parts
 */
function calendar_part_is_valid($value, $type) {
  if (!preg_match('/^[0-9]*$/', $value)) {
    return false;
  }
  calendar_load_date_api();
  $value = intval($value);
  if ($value <= 0) {
    return false;
  }
  switch ($type) {
    case 'year':
      if ($value < DATE_MIN_YEAR) {
        return false;
      }
      break;
    case 'month':
      if ($value < 0 || $value > 12) {
        return false;
      }
      break;
    case 'day':
      if ($value < 0 || $value > 31) {
        return false;
      }
      break;
    case 'week':
      if ($value < 0 || $value > 53) {
        return false;
      }
  }
  return true;
}

/**
 *  implementation of hook_block()
 */
function calendar_block($op = 'list', $delta = 0) {
  switch ($op) {
    case 'list':
      $blocks[0]['info'] = t('Calendar Legend.');
      $blocks[1]['info'] = t('Switch Calendar.');
      return $blocks;
      break;
    case 'view':
      switch ($delta) {
        case 0:
          $block['subject'] = t('Calendar Legend');
          $block['content'] = $GLOBALS['calendar_stripe_labels'] ? '<div class="calendar legend">' . theme('calendar_stripe_legend', $GLOBALS['calendar_stripe_labels']) . '</div>' : '';
          return $block;
        case 1:
          $block['subject'] = t('Switch Calendar');
          $block['content'] = $GLOBALS['calendar_is_calendar'] ? drupal_get_form('calendar_switch_view') : '';
          return $block;
      }
  }
}

/**
 *  A block with a drop-down list that allows the user to switch between views of the current period
 */
function calendar_switch_view() {
  $options[''] = t('Calendar');
  $options['list'] = t('List');
  $options['teasers'] = t('Teasers');
  $options['nodes'] = t('Nodes');
  $options['table'] = t('Table');
  $form = array(
    '#method' => 'GET',
    'view' => array(
      '#type' => 'select',
      '#default_value' => $_GET['view'],
      '#options' => $options,
    ),
    'q' => array(
      '#type' => 'hidden',
      '#value' => $_GET['q'],
    ),
    'submit' => array(
      '#type' => 'submit',
      '#value' => t('Switch'),
    ),
  );
  return $form;
}

/**
 *  Implementation of hook_calendar_node() from the calendar_get_calendar() api
 *  calendar api is expecting a function for each calendar type but
 *  all of them need the same processing, so run them through a single function
 *  instead of duplicating the processing for each one
 */
function calendar_calendar_node_month($node) {
  return calendar_calendar_node($node, 'calendar_node_month');
}
function calendar_calendar_node_day($node) {
  return calendar_calendar_node($node, 'calendar_node_day');
}
function calendar_calendar_node_week($node) {
  return calendar_calendar_node($node, 'calendar_node_week');
}
function calendar_calendar_node($node, $type) {
  calendar_load_calendar_api();
  if ($node->nid && $node->nid !== 0 && $node->calendar_start) {

    // this is a real calendar, go ahead and display it
    if (!$node->remote) {

      // switch our psuedo nids back to the right values
      $tmp = explode(':', $node->nid);
      $node->nid = $tmp[0];
      $node->instance = $tmp[1];
    }
    if (isset($node->calendar_node_theme)) {
      return theme($node->calendar_node_theme, $node, $type);
    }
    else {
      return theme('calendar_calendar_node', $node, $type);
    }
  }
  else {

    // surpress display of psuedo calendars added to force display of a blank calendar
    // have to return some value for blank day so not overridden by normal calendar node theme
    // a blank space seems to be sufficient to do that
    return ' ';
  }
}

/**
 * Valid calendar arguments.
 */
function calendar_args() {
  return array(
    'calendar_year',
    'calendar_week',
    'calendar_month',
    'calendar_day',
  );
}

/**
 * Figure out what the URL of the calendar view we're currently looking at is.
 */
function calendar_real_url($view, $args) {

  // Add non-calendar arguments to the base url.
  $parts = explode('/', $view->url);
  $bump = 0;
  foreach ($parts as $delta => $part) {

    // If one of the args is buried in the url, add it here and adjust
    // the delta values we'll compare the calendar arg positions to.
    if (in_array($part, array(
      '$arg',
      '$node',
      '$user',
      '$group',
    ))) {
      $parts[$delta] = array_shift($args);
      $bump++;
    }
  }
  foreach ($args as $delta => $arg) {
    if (!in_array($delta + $bump, calendar_arg_positions($view)) && !empty($arg)) {
      array_push($parts, $arg);
    }
  }
  return implode('/', $parts);
}

/**
 * Pick up filter and sort info from url.
 */
function calendar_url_append($view) {
  if ($view->build_type == 'page') {
    foreach ($_GET as $key => $val) {
      if ($key != 'q' && $key != 'view') {
        if (!is_array($val)) {
          $url .= '&' . $key . '=' . $val;
        }
        else {
          foreach ($val as $v) {
            $url .= '&' . $key . '[]=' . $v;
          }
        }
      }
    }
  }
  return $url;
}

/**
 *  Function to test whether this is a view that uses the calendar plugin theme.
 */
function calendar_is_calendar($view) {
  $calendar_info = calendar_info();
  return $calendar_info[$view->name][$view->build_type];
}

/**
 * Function to test whether any calendar args are used in this view.
 */
function calendar_has_calendar_args($view, $reset = FALSE) {
  $calendar_info = calendar_info();
  if (count($calendar_info[$view->name]['args']) > 0) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * The positions in the view that hold calendar arguments.
 */
function calendar_arg_positions($view) {
  $calendar_info = calendar_info();
  if (array_key_exists($view->name, $calendar_info)) {
    return array_keys($calendar_info[$view->name]['args']);
  }
  else {
    return array();
  }
}

/**
 * Is the current argument a calendar argument.
 * Used to sort out whether or not to display the calendar at each point.
 */
function calendar_is_calendar_arg($view) {
  if (empty($view->real_args)) {
    $delta = 0;
  }
  else {
    $delta = max(array_keys($view->real_args));
  }
  if (in_array($delta, calendar_arg_positions($view))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Identify the final calendar argument in this view.
 *   Needed because we can't construct a query until we know all the calendar elements.
 *   Used to tell when to add the filter to the query object.
 *
 * @param object $view
 * @return string $argtype
 */
function calendar_is_last_arg($view, $reset = FALSE) {
  foreach ($view->argument as $argument) {
    if (in_array($argument['id'], calendar_args())) {
      $calendar_arg = $argument['id'];
    }
    $max = $argument['id'];
  }
  return $max < $calendar_arg;
}

/**
 * Helper function to find the display formats
 * for each part of this view.
 */
function calendar_get_formats($view) {
  return variable_get('calendar_display_format_' . $view->name, array(
    'year' => 'calendar',
    'month' => 'calendar',
    'week' => 'calendar',
    'day' => 'calendar',
    'block' => 'calendar',
  ));
}

/**
 * create a stripe id from a combination of the field and content types
 * and store value for legend
 * formula tries to create a unique id for each possible combination
 *
 * @param $node - the node object
 * @param $query_name - the views queryname for this date field
 * @param $delta - the delta for this field, used to distinguish fields that appear more than once in the calendar
 * @param $label - the label to give this stripe.
 */
function calendar_node_stripe($view, $node, $query_name, $delta, $stripe = NULL, $label = '') {
  $type_names = node_get_types('names');
  if (!$label) {
    $label = $view->field[$delta]['label'] . ' (' . $type_names[$node->type] . ')';
  }
  if (!$stripe) {
    $i = 1;
    foreach ($type_names as $k => $v) {
      if ($k == $node->type) {
        break;
      }
      $i++;
    }
    $stripe = intval(20 * $delta + $i);
  }
  $GLOBALS['calendar_stripe_labels'][$stripe] = $label;
  return $stripe;
}

/**
 * Moved the following infrequently-used functions to separate file
 * so the code is not parsed on every page.
 */

/**
 *  Implementation of hook_views_style_plugins()
 */
function calendar_views_style_plugins() {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_views_style_plugins();
}

/**
 * Implementation of hook_views_default_views()
 */
function calendar_views_default_views() {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_views_default_views();
}

/**
 *  Implementation of hook_views_arguments()
 */
function calendar_views_arguments() {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_views_arguments();
}

/**
 *  Function to return all possible calendar views page display types.
 */
function calendar_view_types($reset = FALSE) {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_view_types($reset);
}

/**
 * Function to get information about all views that have calendar components.
 */
function calendar_info($reset = FALSE) {
  static $calendar_views;
  if (empty($calendar_views) || $reset) {
    $cid = 'calendar_views';
    if (!$reset && ($cached = cache_get($cid, 'cache_views'))) {
      $calendar_views = unserialize($cached->data);
    }
    else {
      include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
      $calendar_views = _calendar_info();
    }
  }
  return $calendar_views;
}

/**
 *  Identify all potential date/timestamp fields
 */
function calendar_fields($reset = FALSE) {
  static $fields;
  if (empty($fields) || $reset) {
    $cid = 'calendar_fields';
    if (!$reset && ($cached = cache_get($cid, 'cache_views'))) {
      $fields = unserialize($cached->data);
    }
    else {
      include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
      $fields = _calendar_fields();
    }
  }
  return $fields;
}

/**
 * Validate a view during Views administration.
 */
function calendar_views_validate($type, $view, $form) {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_views_validate($type, $view, $form);
}

/**
 * Setup Calendar parameters in the Setup Tab.
 */
function calendar_setup_form($view_name) {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_setup_form($view_name);
}

/**
 * Save Setup values.
 */
function calendar_setup_form_submit($form_id, $form_values) {
  include_once './' . drupal_get_path('module', 'calendar') . '/calendar_admin.inc';
  return _calendar_setup_form_submit($form_id, $form_values);
}

/**
 * Empty or reset cached values.
 *
 * @param $remove
 *   whether or not to completely remove the caches.
 */
function calendar_clear_all($remove = FALSE) {
  if ($remove) {
    cache_clear_all('calendar_views', 'cache_views');
    cache_clear_all('calendar_fields', 'cache_views');
  }
  else {
    calendar_fields(TRUE);
    calendar_info(TRUE);
  }
}

/**
 * Helper function to figure out a group gid to use in blocks.
 *
 * @return an array of group nodes that are relevant.
 * @todo this may need more work.
 */
function calendar_og_groups($view) {
  if (!($groupnode = og_get_group_context())) {
    global $user;
    $groupnodes = array_keys($user->og_groups);
  }
  else {
    $groupnodes = array(
      $groupnode->nid,
    );
  }
  return $groupnodes;
}

Functions

Namesort descending Description
calendar_add_items Call hooks in other modules to add other items to a calendar view.
calendar_args Valid calendar arguments.
calendar_arg_positions The positions in the view that hold calendar arguments.
calendar_block implementation of hook_block()
calendar_build_field_query Build a filtering query for an individual date field
calendar_build_filter Compile the filter query for this view.
calendar_calendar_node
calendar_calendar_node_day
calendar_calendar_node_month Implementation of hook_calendar_node() from the calendar_get_calendar() api calendar api is expecting a function for each calendar type but all of them need the same processing, so run them through a single function instead of duplicating the…
calendar_calendar_node_week
calendar_clear_all Empty or reset cached values.
calendar_fields Identify all potential date/timestamp fields
calendar_filter_day Callback for day filter. Build year, month, day, min, and max into query object.
calendar_filter_month Callback for month filter. Build year, month, day, min, and max into query object.
calendar_filter_week Callback for week filter. Build year, month, day, min, and max into query object.
calendar_filter_year Callback for year filter. Build year, month, day, min, and max into query object.
calendar_form_alter Implementation of hook_form_alter(). Make sure calendar_info() and calendar_fields() get updated.
calendar_get_formats Helper function to find the display formats for each part of this view.
calendar_get_nodes The workhorse function that takes the beginning array of items and alters it to an array of calendar nodes that the theme can handle.
calendar_get_paths
calendar_handler_arg_day Custom views handler for the day argument.
calendar_handler_arg_month Custom views handler for the month argument.
calendar_handler_arg_type Custom views handler for all calendar arguments.
calendar_handler_arg_week Custom views handlers for the week argument.
calendar_handler_arg_year Custom views handler for the year argument.
calendar_has_calendar_args Function to test whether any calendar args are used in this view.
calendar_help Implementation of hook_help().
calendar_info Function to get information about all views that have calendar components.
calendar_is_calendar Function to test whether this is a view that uses the calendar plugin theme.
calendar_is_calendar_arg Is the current argument a calendar argument. Used to sort out whether or not to display the calendar at each point.
calendar_is_last_arg Identify the final calendar argument in this view. Needed because we can't construct a query until we know all the calendar elements. Used to tell when to add the filter to the query object.
calendar_load_calendar_api Helper function for loading calendar_api.
calendar_load_date_api Helper function for loading date_api.
calendar_menu Implementation of hook_menu().
calendar_nav Function to construct back and next navigation from views arguments
calendar_node_stripe create a stripe id from a combination of the field and content types and store value for legend formula tries to create a unique id for each possible combination
calendar_offset TODO need to identify type of timezone handling needed for each date field
calendar_og_groups Helper function to figure out a group gid to use in blocks.
calendar_part_is_valid A function to test the validity of various date parts
calendar_real_url Figure out what the URL of the calendar view we're currently looking at is.
calendar_setup_form Setup Calendar parameters in the Setup Tab.
calendar_setup_form_submit Save Setup values.
calendar_switch_view A block with a drop-down list that allows the user to switch between views of the current period
calendar_url_append Pick up filter and sort info from url.
calendar_views_arguments Implementation of hook_views_arguments()
calendar_views_default_views Implementation of hook_views_default_views()
calendar_views_post_view Implementation of hook_views_post_view().
calendar_views_pre_view Implementation of hook_views_pre_view()
calendar_views_query_alter Implementation of hook_views_query() Insert filters into the query based on the current calendar view and the selected fields Used when the actual view arguments don't provide enough info to construct the query. i.e. on a view with no arguments…
calendar_views_style_plugins Implementation of hook_views_style_plugins()
calendar_views_tabs Implementation of hook_views_tabs().
calendar_views_validate Validate a view during Views administration.
calendar_view_types Function to return all possible calendar views page display types.
calendar_week Handle a lot of messy week calculations all in one place to make maintenance easier
calendar_week_range Get the start and end datestamp for a calendar week.
calendar_week_year Find the calendar week number and year for a date.
_calendar_limit_nodes A function to adjust node values to slice off times before and after the selected view used for calendars that span days, months, or years since the calendar api automatically creates additional calendars for calendars that extend into another time…
_calendar_make_node A function to create a blank date to force a calendar display when there is no data