View complete documentation at !link.
", array('!link' => 'http://drupal.org/node/120710'));
}
}
/**
* Implementation of hook_menu().
*/
function calendar_menu($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'));
$items = array();
if (!$may_cache) {
foreach (calendar_views('page') as $view_name) {
$items[] = array(
'path' => $view_name['url'] .'/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['name']),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
}
}
return $items;
}
/**
* 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');
}
/**
* Helper function for loading calendar_api.
*/
function calendar_load_calendar_api() {
include_once(drupal_get_path('module', 'calendar') .'/calendar_api.inc');
}
/**
* Implementation of hook_views_style_plugins()
*/
function calendar_views_style_plugins() {
$plugins = array();
$types = array('calendar' => t('Calendar'));
foreach ($types as $name => $type) {
$plugins[$name] = array(
'name' => $type,
'theme' => 'calendar_display',
'summary_theme' => 'calendar_display',
'validate' => 'calendar_views_validate',
'needs_fields' => TRUE,
'needs_table_header' => FALSE,
);
}
return $plugins;
}
/**
* Validate a view.
*/
function calendar_views_validate($type, $view, $form) {
// list (and table) modes require there to be at least 1 field active.
if (is_array($view['field'])) {
$fields = array_filter(array_keys($view['field']), 'is_numeric');
}
if (!$fields) {
form_error($form["$type-info"][$type . '_type'], t('The Calendar View requires at least one field.'));
}
if (isset($view['field']['count'])) {
$defaultsort = false;
for ($i = 0; $i < $view['field']['count']; $i++) {
if ($view['field'][$i]['defaultsort']) {
if ($defaultsort) {
form_error($form['field'][$i]['defaultsort'], t('You can only set on Default Sort on one field.'));
break;
}
$defaultsort = true;
}
}
}
// Make sure all arguments are set to 'Display all values'.
$arg_types = array();
$cal_args = array('calendar_year', 'calendar_month', 'calendar_day', 'calendar_week');
foreach ($view['argument'] as $delta => $argument) {
if (in_array($argument['type'], $cal_args)) {
$view['argument'][$delta]['argdefault'] = 2;
if ($argument['argdefault'] != 2) {
form_error($form['argument'][$delta]['argdefault'], t('Calendar arguments must be set to \'Display All Values\'.'));
}
$arg_types[] = $argument['type'];
}
}
// Must have Year, Month, and Day or Year and Week calendar arguments.
if (!in_array('calendar_year', $arg_types) && ((!in_array('calendar_month', $arg_types) && !in_array('calendar_day', $arg_types) || !in_array('calendar_week', $arg_types)))) {
form_error($form['argument'], t('The Calendar requres as arguments Calendar: Year, Calendar: Month, and Calendar: Day, or Calendar: Year and Calendar: Week'));
}
// CCK date fields cannot use grouped handler.
$calendar_fields = array_keys(calendar_fields());
foreach ($view['field'] as $delta => $field) {
if (in_array($field['field'], $calendar_fields) && $field['handler'] == 'content_views_field_handler_group') {
form_error($form['field'][$delta]['handler'], t('Calendar CCK Date fields must be set to \'Do not group multiple values\'.'));
}
}
}
/**
* Function to return all possible calendar views page display types.
* Implements hook_calendar_add_types($view) for other modules to add calendar types.
*/
function calendar_view_types($reset = FALSE) {
static $types;
if ($reset || !isset($types)) {
$types = array('calendar' => t('Calendar'));
foreach (module_implements('calendar_add_items') as $module) {
$function = $module .'_calendar_add_types';
$types += $function($view);
}
}
return $types;
}
/**
* Function to return all views that are of the type 'calendar'.
*/
function calendar_views($build_type = 'page', $reset = FALSE) {
static $calendar_views;
views_load_cache();
if (empty($calendar_views)) {
$calendar_views = array($build_type => '');
}
if ($reset || empty($calendar_views[$build_type])) {
$calendar_views = array();
$result = db_query("SELECT vid, name, url FROM {view_view} ORDER BY name");
while ($view = db_fetch_object($result)) {
$view->build_type = $build_type;
if (calendar_is_calendar($view)) {
$calendar_views[$build_type][] = array('name' => $view->name, 'url' => $view->url);
}
}
$check = $build_type .'_type';
foreach (_views_get_default_views() as $view) {
if ($view->$check == 'calendar' && empty($view->disabled)) {
$calendar_views[$build_type][] = array('name' => $view->name, 'url' => $view->url);
}
}
}
if (empty($calendar_views) || !array_key_exists($build_type, $calendar_views)) {
$calendar_views = array($build_type => array());
}
return $calendar_views[$build_type];
}
/**
* Function to test whether this is a view that uses the calendar plugin theme.
*/
function calendar_is_calendar($view, $reset = FALSE) {
static $is_calendar;
if (empty($is_calendar)) {
$is_calendar = array($view->name => array($view->build_type => FALSE));
}
if ($reset || empty($is_calendar[$view->name][$view->build_type])) {
$is_calendar = FALSE;
if ($view->build_type == 'page' && !array_key_exists($view->page_type, calendar_view_types())) {
$is_calendar[$view->name][$view->build_type] = FALSE;
}
elseif ($view->build_type == 'page' && calendar_has_calendar_args($view)) {
$is_calendar[$view->name][$view->build_type] = TRUE;
}
if ($view->build_type == 'block' && !array_key_exists($view->block_type, calendar_view_types())) {
$is_calendar[$view->name][$view->build_type] = FALSE;
}
elseif ($view->build_type == 'block' && calendar_has_calendar_args($view)) {
$is_calendar[$view->name][$view->build_type] = TRUE;
}
}
return $is_calendar[$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) {
static $has_args;
if (empty($has_args)) {
$has_args = array($view->name => FALSE);
}
if ($reset || empty($has_args[$view->name])) {
foreach ($view->argument as $delta => $argument) {
if (in_array($argument['type'], array('calendar_year', 'calendar_month', 'calendar_day', 'calendar_week'))) {
$has_args[$view->name] = TRUE;
}
}
}
return $has_args[$view->name];
}
/**
* 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_last_argtype($view, $reset = FALSE) {
static $argtype;
if (empty($argtype)) {
$argtype = array($view->name => '');
}
if ($reset || empty($argtype[$view->name])) {
$calendar_args[] = array();
foreach ($view->argument as $argument) {
if (in_array($argument['id'], array('calendar_year', 'calendar_month', 'calendar_week', 'calendar_day'))) {
$calendar_args[] = $argument['id'];
}
}
$argtype[$view->name] = array_pop($calendar_args);
}
return $argtype[$view->name];
}
/**
* Identify all potential date/timestamp fields
* @return an array with fieldname, type, and table
* TODO add caching
*/
function calendar_fields($reset = FALSE) {
static $fields;
if ($reset || empty($fields)) {
// iterate through all the views fields
$delta = 0;
foreach (_views_get_fields() as $name => $val) {
$timestamp_fromto = array();
$string_fromto = array();
$type = '';
// for cck fields, get the date type
if ($val['content_field']['type'] == 'date' || $val['content_field']['type'] == 'datestamp') {
$type = $val['content_field']['type'] == 'date' ? 'cck_string' : 'cck_timestamp';
}
// all other fields that use the views date handler are timestamps
elseif ($val['handler'] == views_handler_field_dates()) {
$type = 'timestamp';
}
// don't do anything if this is not a date field
if ($type) {
// dates with from and to dates need to handle both fields as one
// add the from and to dates to the first one found and ignore the second
// Handling for event fromto dates
if (module_exists('event') && !$event_field_processed && ($name == 'event.event_start' || $name == 'event.event_end')) {
$timestamp_fromto = array('event.event_start', 'event.event_end');
$offset_field = 'event.timezone';
$tz_handling = variable_get('event_timezone_display', 'site');
$event_field_processed = TRUE;
}
elseif ($val['content_field']['tz_handling']) {
$tz_handling = $val['content_field']['tz_handling'];
if ($tz_handling == 'date') $offset_field = $val['table'] .'.'. $val['content_db_info']['columns']['offset']['column'];
}
else {
$tz_handling = 'site';
}
// Handling for cck fromto dates
if (!$event_field_processed && $val['content_field']['todate']) {
switch ($val['content_field']['type']) {
case 'datestamp':
$timestamp_fromto = array($val['table'] .'.'. $val['content_field']['field_name'] .'_value', $val['table'] .'.'. $val['content_field']['field_name'] .'_value2');
break;
case 'date':
$string_fromto = array($val['table'] .'.'. $val['content_field']['field_name'] .'_value', $val['table'] .'.'. $val['content_field']['field_name'] .'_value2');
break;
}
$event_field_processed == TRUE;
}
// skip this step on second occurance of fromto date fields, if more than one exists in view
if (!$event_field_processed || $timestamp_fromto || $string_fromto) {
// cck fields append a column name to the field, others do not
// need a real field_name with no column name appended for cck date formatters
$tmp = explode('.', $name);
$field_name = $val['content_field']['field_name'] ? $val['content_field']['field_name'] : $tmp[1];
$fields[$tmp[1]] = array(
'type' => $type,
'delta' => $delta,
'label' => $val['name'],
'granularity' => is_array($val['content_field']['granularity']) ? (array) array_keys($val['content_field']['granularity']) : array('Y', 'M', 'D', 'H', 'N'),
'fullname' => $name,
'table' => $tmp[0],
'field' => $tmp[1],
'field_name' => $field_name,
'query_name' => str_replace('.', '_', $name),
'timestamp_fromto' => $timestamp_fromto,
'string_fromto' => $string_fromto,
'tz_handling' => $tz_handling,
'offset_field' => $offset_field,
);
}
}
}
}
return $fields;
}
/**
* Calendar Views plugin theme, overrides default views theme
*/
function theme_calendar_display(&$view, &$items, $type) {
if ($type == 'block') {
$view->calendar_type = 'month';
}
$mini = $view->calendar_type == 'year' || $view->build_type == 'block' ? TRUE : FALSE;
$links = $view->build_type == 'block' ? FALSE : TRUE;
// Bail out here to display regular views views instead of calendar.
// Check first for 'view' in url, then for 'calendar_display_format' variable, default to normal calendar display.
if ($_GET['view'] && $view->build_type == 'page') {
$display = check_plain($_GET['view']);
}
else {
$display_formats = variable_get('calendar_display_format_'. $view->name, array('year' => 'calendar', 'month' => 'calendar', 'week' => 'calendar', 'day' => 'calendar', 'block' => 'calendar'));
$display = $view->build_type == 'block' ? $display_formats['block'] : $display_formats[$view->calendar_type];
}
switch($display) {
case ('table') :
$view->table_header = _views_construct_header($view, _views_get_fields());
return theme('calendar_show_nav', $view, $mini, $links) . theme('calendar_view_table', $view, $items, $type);
case ('teasers') :
return theme('calendar_show_nav', $view, $mini, $links) . theme('calendar_view_teasers', $view, $items, $type);
case ('nodes') :
return theme('calendar_show_nav', $view, $mini, $links) . theme('calendar_view_nodes', $view, $items, $type);
case ('list') :
return theme('calendar_show_nav', $view, $mini, $links) . theme('calendar_view_list', $view, $items, $type);
}
$results = calendar_get_nodes($view, $items, $type);
$nodes = (array) $results['nodes'];
$params = (array) $results['params'];
// Weeks still aren't working right in all situations, turning them off for now.
$params[with_weekno] = FALSE;
$nodes += calendar_add_items($view, 'calendar');
return theme('calendar_show_nav', $view, $mini, $links) . calendar_get_calendar($view->calendar_type, $nodes, 'calendar', '', $params);
}
/**
* Display the nodes of a view as a list.
*/
function theme_calendar_view_list($view, $nodes, $type) {
$fields = _views_get_fields();
$items = array();
foreach ($nodes as $node) {
$item = '';
foreach ($view->field as $field) {
if ($fields[$field['id']]['visible'] !== FALSE) {
if ($field['label']) {
$item .= "" . $field['label'] . "
";
}
$item .= "" . views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view) . "
";
}
}
$items[] = "name) ."'>$item
\n"; // l($node->title, "node/$node->nid");
}
$items += calendar_add_items($view, 'list');
if ($items) {
return theme('item_list', $items);
}
}
/**
* Display the nodes of a view as a table.
*/
function theme_calendar_view_table($view, $nodes, $type) {
$fields = _views_get_fields();
$rows = array();
foreach ($nodes as $node) {
$row = array();
foreach ($view->field as $field) {
if ($fields[$field['id']]['visible'] !== FALSE) {
$cell['data'] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
$cell['class'] = "view-field ". views_css_safe('view-field-'. $field['queryname']);
$row[] = $cell;
}
}
$rows[] = $row;
}
$rows += calendar_add_items($view, 'table');
return theme('table', $view->table_header, $rows);
}
/**
* Display the nodes of a view as teasers.
*/
function theme_calendar_view_teasers($view, $nodes, $type) {
return views_theme('calendar_view_nodes', $view, $nodes, 'teasers');
}
/**
* Display the nodes of a view as plain nodes.
*/
function theme_calendar_view_nodes($view, $nodes, $type, $teasers = false, $links = true) {
$output = array();
foreach ($nodes as $n) {
$node = node_load($n->nid);
$output[] = node_view($node, $teasers, false, $links);
}
$output += calendar_add_items($view, $teasers ? 'teasers' : 'nodes');
return implode($output);
}
/**
* 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 = $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 = $offset_field;
break;
case 'event' :
include_once(DATE_TIMEZONES);
$node->start_offset = event_get_offset($node->event_timezone, $node->event_event_start);
$node->end_offset = event_get_offset($node->event_timezone, $node->event_event_end);
break;
default :
$node->start_offset = $node->end_offset = variable_get('date_default_timezone', 0);
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_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_get_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_is_valid($view->year, 'year')) {
return $view->subtitle;
}
calendar_load_date_api();
if ($view->calendar_type == 'week' && _calendar_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_is_valid($year, 'year') ? $year : date_format_date('Y', date_time());
$month = _calendar_is_valid($month, 'month') ? $month : date_format_date('m', date_time());
$day = _calendar_is_valid($day, 'day') ? $day : date_format_date('j', date_time());
$hour = _calendar_is_valid($hour, 'hour') ? $hour : date_format_date('H', date_time());
$minute = _calendar_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_is_valid($day, 'day')) $day = 1;
if (!_calendar_is_valid($month, 'month')) $month = date_format_date('m', date_time());
if (!_calendar_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));
}
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);
}
/**
* Implementation of hook_views_arguments()
*/
function calendar_views_arguments() {
$arguments = array(
'calendar_year' => array(
'name' => t('Calendar: Year'),
'handler' => 'calendar_handler_arg_year',
'help' => t('Filter by the calendar year (YYYY).'),
),
'calendar_month' => array(
'name' => t('Calendar: Month'),
'handler' => 'calendar_handler_arg_month',
'help' => t("Filter by the calendar month (1-12). Place this argument after a 'Year' argument."),
),
'calendar_day' => array(
'name' => t('Calendar: Day'),
'handler' => 'calendar_handler_arg_day',
'help' => t("Filter by the calendar day (1-31). Place this argument after a 'Year' and a 'Month' argument."),
),
'calendar_week' => array(
'name' => t('Calendar: Week'),
'handler' => 'calendar_handler_arg_week',
'help' => t("Filter by the week number (1-52). Place this argument after a 'Year' argument and use a 'W' in front of the week number in the url."),
),
);
return $arguments;
}
/**
* 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_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_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_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_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 rangess 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_last_argtype($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;
}
// handling for single day dates
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) {
if (!calendar_has_calendar_args($view)) {
return;
}
$view->real_url = calendar_get_url($view, $view->args);
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) {
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)) {
// 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;
}
}
}
foreach ($view->argument as $delta => $argument) {
if (in_array($argument['type'], array('calendar_year', 'calendar_month', 'calendar_day', 'calendar_week'))) {
// 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;
}
// if this is not a week argument and view was created with a week argument, change it back
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 = $view->page_header . $view->page_empty . theme('calendar_display', $view, array(), 'page');
$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 = $view->block_header . $view->block_empty . theme('calendar_display', $view, array(), 'block');
$view->block_empty_format = 3;
}
}
}
/**
* 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);
}
// If the timestamp is earlier, the calendar week is earlier than the ISO week.
elseif ($timestamp < $range[1]) {
if ($iso_week >= 2) {
return array(intval($iso_week - 1), $year);
}
else {
return array(1, $year);
}
}
// If the timestamp is later, the calendar week is later than the ISO week.
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_is_valid($value, $type) {
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;
}
function calendar_views_default_views() {
$view = new stdClass();
$view->name = t('calendar');
$view->description = t('Calendar view of any date field');
$view->access = array ();
$view->view_args_php = '';
$view->page = TRUE;
$view->page_title = t('Calendar');
$view->page_header = "";
$view->page_header_format = '1';
$view->page_footer = "";
$view->page_footer_format = '1';
$view->page_empty = "";
$view->page_empty_format = '1';
$view->page_type = 'calendar';
$view->url = t('calendar');
$view->use_pager = FALSE;
$view->nodes_per_page = '0';
$view->block = TRUE;
$view->block_title = t('Calendar');
$view->block_header = "";
$view->block_header_format = '1';
$view->block_footer = "";
$view->block_footer_format = '1';
$view->block_empty = "";
$view->block_empty_format = '1';
$view->block_type = 'calendar';
$view->nodes_per_block = '99';
$view->block_more = '1';
$view->block_use_page_header = FALSE;
$view->block_use_page_footer = FALSE;
$view->block_use_page_empty = FALSE;
$view->sort = array (
array (
'tablename' => 'node',
'field' => 'changed',
'sortorder' => 'ASC',
'options' => '',
),
);
$view->argument = array (
array (
'type' => 'calendar_year',
'argdefault' => '2',
'title' => '%1',
'options' => '',
'wildcard' => '',
'wildcard_substitution' => '',
),
array (
'type' => 'calendar_month',
'argdefault' => '2',
'title' => '%2',
'options' => '',
'wildcard' => '',
'wildcard_substitution' => '',
),
array (
'type' => 'calendar_day',
'argdefault' => '2',
'title' => '%3',
'options' => '',
'wildcard' => '',
'wildcard_substitution' => '',
),
);
$view->field = array (
array (
'tablename' => 'node',
'field' => 'title',
'label' => 'Title:',
'handler' => 'views_handler_field_nodelink',
'options' => 'link',
),
array (
'tablename' => 'node',
'field' => 'changed',
'label' => 'Updated:',
'handler' => 'views_handler_field_date_small',
),
);
$view->filter = array (
array (
'tablename' => 'node',
'field' => 'status',
'operator' => '=',
'options' => '',
'value' => '1',
),
);
$view->exposed_filter = array (
);
$view->requires = array(node);
$views[$view->name] = $view;
return $views;
}
/**
* 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'] ? ''. theme('calendar_stripe_legend', $GLOBALS['calendar_stripe_labels']) .'
' : '';
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];
}
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 ' ';
}
}
/**
* Figure out what the URL of the calendar view we're currently looking at is.
*/
function calendar_get_url($view, $args) {
static $cal_args;
// Figure out which arguments are calendar arguments.
if (!is_array($cal_args)) $cal_args = array();
if (empty($cal_args[$view->name])) {
foreach ($view->argument as $delta => $arg) {
if (in_array($arg['type'], array('calendar_year', 'calendar_week', 'calendar_month', 'calendar_day', 'calendar_ical'))) {
$cal_args[$view->name][] = $delta;
}
}
}
// Add non-calendar arguments to the base url.
$parts = array($view->url);
foreach ($args as $delta => $arg) {
if (!in_array($delta, $cal_args[$view->name]) && $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;
}
/**
* Setup Calendar parameters.
*/
function calendar_setup_form($view_name) {
calendar_load_date_api();
$view = views_load_view($view_name);
$form = array();
$time = mktime(1, 15, 0, 1, 1, date('Y', date_time()));
$time_options = array(
'G:i' => date('G:i', $time),
'g:ia' => date('g:ia', $time),
'g:iA' => date('g:iA', $time),
'g:i a' => date('g:i a', $time),
'g:i A' => date('g:i A', $time),
'H:i' => date('H:i', $time),
'h:ia' => date('h:ia', $time),
'h:iA' => date('h:iA', $time),
'h:i a' => date('h:i a', $time),
'h:i A' => date('h:i A', $time),
);
$form['calendar_time_format'] = array(
'#title' => t('Time format'),
'#default_value' => variable_get('calendar_time_format_'. $view->name, 'H:i'),
'#type' => 'select',
'#options' => $time_options,
'#description' => t('The format to use for the time-only date display.'),
);
$display_options = array('calendar' => t('Calendar'), 'table' => t('Table'), 'teasers' => t('Teasers'), 'nodes' => t('Full Nodes'), 'list' => t('List'));
$display_format = variable_get('calendar_display_format_'. $view->name, array('year' => 'calendar', 'month' => 'calendar', 'week' => 'calendar', 'day' => 'calendar', 'block' => 'calendar'));
$form['year'] = array(
'#title' => t('Year display'),
'#default_value' => $display_format['year'],
'#type' => 'select',
'#options' => $display_options,
);
$form['month'] = array(
'#title' => t('Month display'),
'#default_value' => $display_format['month'],
'#type' => 'select',
'#options' => $display_options,
);
$form['week'] = array(
'#title' => t('Week display'),
'#default_value' => $display_format['week'],
'#type' => 'select',
'#options' => $display_options,
);
$form['day'] = array(
'#title' => t('Day display'),
'#default_value' => $display_format['day'],
'#type' => 'select',
'#options' => $display_options,
);
$form['block'] = array(
'#title' => t('Block display'),
'#default_value' => $display_format['block'],
'#type' => 'select',
'#options' => $display_options,
);
$form['calendar_empty_arg'] = array(
'#title' => t('Wildcard argument'),
'#type' => 'textfield',
'#default_value' => variable_get('calendar_empty_arg', 'all'),
'#description' => t('A character or short text string to use for empty calendar arguments. For instance, \'all\' would create the url 2007/12/all to show all days in December of 2007. Note that non-ASCII characters will not display correctly in urls.')
);
$form['view_name'] = array(
'#type' => 'hidden',
'#value' => $view->name,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Save requested values.
*/
function calendar_setup_form_submit($form_id, $form_values) {
$view_name = $form_values['view_name'];
$display_format = array();
variable_set('calendar_empty_arg', check_plain($form_values['calendar_empty_arg']));
foreach ($form_values as $value_name => $value) {
switch ($value_name) {
case 'calendar_time_format':
variable_set('calendar_time_format_'. $view_name, $value);
break;
case 'year':
case 'month':
case 'week':
case 'day':
case 'block':
$display_format[$value_name] = $value;
break;
}
}
variable_set('calendar_display_format_'. $view_name, $display_format);
}
/**
* 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;
}