Select the Calendar display as the page type for your view and add any date fields you want to display on the calendar as fields to the view. Date fields can include CCK date fields, event start and end dates, or dates like the node created or updated dates.
Add three arguments to the view: Calendar Year, Calendar Month, and Calendar Day, in that order. Set each of them to 'Display All Values'.
A mini calendar is available using the block view if the block view is also set to the Calendar type.
Two other blocks are available. A legend block will display only on calendar pages and will show a legend of the color coding for field names and content types.
A Switch Calendar block will display only on calendar pages and allows the user to switch between calendar, list, table, teaser, and full node views for whichever time period is being viewed. The back/next navigation will remain at the top of the traditional views displays so you can move from one month to the next, for instance.
"); } } /** * Implementation of hook_views_style_plugins() */ function calendar_views_style_plugins() { $plugins = array(); $types = calendar_view_types(); foreach ($types as $name => $type) { $plugins[$name] = array( 'name' => $type, 'theme' => 'calendar_display', 'summary_theme' => 'calendar_display', 'validate' => 'views_ui_plugin_validate_table', 'needs_fields' => TRUE, 'needs_table_header' => FALSE, ); } return $plugins; } /** * Possible calendar views page display types */ function calendar_view_types() { return array('calendar' => t('Calendar')); } /** * Identify all potential date/timestamp fields * @return an array with fieldname, type, and table */ function calendar_fields() { // TODO add caching // iterate through all the views fields $delta = 0; foreach (_views_get_fields() as $name => $val) { $fromto = $timestamp_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 if (!$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'; } // TODO add handling for cck fromto dates here once date module has been updated // 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' => $val['content_field']['granularity'] ? 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, '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 (!is_array($items)) { $items = array(); } if ($view->build_type == 'page') { // bail out here to display regular views views switch($_GET['view']) { case ('table') : $view->table_header = _views_construct_header($view, _views_get_fields()); return theme('views_view_table', $view, $items, $type); case ('teasers') : return theme('views_view_teasers', $view, $items, $type); case ('nodes') : return theme('views_view_nodes', $view, $items, $type); case ('list') : return theme('views_view_list', $view, $items, $type); } } include_once('calendar_api.inc'); $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_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_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']; // iterate through the $items array returned by the query and create date or pseudo date nodes foreach ($items as $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') { $node->fields[$field['queryname']] = views_theme_field('views_handle_field', $field['queryname'], $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 (($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 (($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 using strtotime if ($field_type == 'timestamp') { $node->calendar_start = $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 = strtotime(str_replace('T', ' ', $item->$field_field) .' UTC'); } } if (isset($node)) { // get appropriate timezone offset switch ($tz_handling) { case 'user' : global $user; $node->start_offset = $node->end_offset = $user->timezone; break; 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(drupal_get_path('module', 'event') .'/event_timezones.inc'); $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), $field_formatter); if ($node->calendar_end) $node->end_format = content_format($field_field_name, array('value' => $item->$field_field), $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'))) { $format = explode(' - ', variable_get('date_format_short', 'm/d/Y - H:i')); $node->start_time_format = gmdate($format[1], $node->calendar_start + $node->start_offset); if ($node->calendar_end) $node->end_time_format = gmdate($format[1], $node->calendar_end + $node->end_offset); } 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 $id = $item->nid.':'.$field_field; $node->nid = $id; if ($view->build_type == 'page' && $view->calendar_type != 'year') { // 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 $node->stripe = intval((20 * $field_id) + $type_names[$node->type]); $GLOBALS['calendar_stripe_labels'][$node->stripe] = $view->field[$delta]['label'] .' ('. $type_names[$node->type] .') '; } $nodes[$id] = $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_start = strtotime('Jan 1, '.$view->year.' + '.intval($view->week - 1).' weeks'); $week_end = $week_start + 604800 -1; $nodes[] = _calendar_make_node(NULL, NULL, _views_get_timezone(), $view->year, date('m', $week_start), date('j', $week_start), 12, 0); $nodes[] = _calendar_make_node(NULL, NULL, _views_get_timezone(), $view->year, date('m', $week_end), date('j', $week_end), 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; } } else { // if this is a summary page or any view that doesn't use arguments // view results may contain multiple calendar months, days or weeks // the standard calendar api (not using the $params and $title above) // will display multiple calendar months, weeks, or days with no navigation // make no change to api, just set title $title = $view->subtitle ? $view->subtitle : $view->page_type; } // use calendar_get_calendar api to draw the calendar drupal_add_css(drupal_get_path('module', 'calendar') .'/calendar.css'); $title = ($view->build_type == 'page' ? '' : ($view->subtitle ? $view->subtitle : $view->page_type)); $params['url'] = $view->url; $params['stripe'] = 'stripe'; $params['with_weekno'] = $view->build_type == 'block' || $view->calendar_type == 'year' ? FALSE : TRUE; return calendar_get_calendar($view->calendar_type, $nodes, 'calendar', $title, $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; } if ($view->calendar_type == 'week' && _calendar_is_valid($view->week, 'week')) { $timezone = _views_get_timezone(); $cur_stamp = strtotime('Jan 1, '.$view->year.' + '.intval($view->week).' weeks GMT') - $timezone; } else { $cur_stamp = gmmktime(0, 0, 0, ($view->month ? $view->month : 1), ($view->day ? $view->day : 1), ($view->year ? $view->year : gmdate("Y", 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']; } $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) { $path = array(); // build an array of the current path and its parts $i = 0; $path[$i] = array( 'path' => $view->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->$pathtype, '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--) { switch ($path[$x]['type']) { case ('day'): $day = $path[$x]['path']; $next_stamp = $cur_stamp + (86400); $prev_stamp = $cur_stamp - (86400); $nextpath[$x] = gmdate('j', $next_stamp); $prevpath[$x] = gmdate('j', $prev_stamp); break; case ('week'): $week = $path[$x]['path']; $year = $view->year; if (!$next_stamp) { $next_stamp = $cur_stamp + (604800); $prev_stamp = $cur_stamp - (604800); } $nextpath[$x] = 'W'. gmdate('W', $next_stamp); $prevpath[$x] = 'W'. gmdate('W', $prev_stamp); break; case ('month'): $month = $path[$x]['path']; $year = $view->year; if (!$next_stamp) { $next_stamp = gmmktime(0, 0, 0, ($month < 12 ? $month + 1 : 1), 1, ($month < 12 ? $year : $year + 1)); $prev_stamp = gmmktime(0, 0, 0, ($month > 1 ? $month - 1 : 12), 1, ($month > 1 ? $year : $year - 1)); } $nextpath[$x] = gmdate('n', $next_stamp); $prevpath[$x] = gmdate('n', $prev_stamp); break; case ('year'): $year = $view->year; if (!$next_stamp) { $next_stamp = gmmktime(0, 0, 0, 1, 1, ($year + 1)); $prev_stamp = gmmktime(0, 0, 0, 12, 1, ($year - 1)); } $nextpath[$x] = gmdate('Y', $next_stamp); $prevpath[$x] = gmdate('Y', $prev_stamp); break; default: // all other arguments are just passed through $nextpath[$x] = $path[$x]['path']; $prevpath[$x] = $path[$x]['path']; break; } } 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) { $offset = $offset ? $offset : _views_get_timezone(); if (!$timestamp) { $year = _calendar_is_valid($year, 'year') ? $year : gmdate('Y', time()); $month = _calendar_is_valid($month, 'month') ? $month : gmdate('m', time()); $day = _calendar_is_valid($day, 'day') ? $day : gmdate('j', time()); $hour = _calendar_is_valid($hour, 'hour') ? $hour : gmdate('H', time()); $minute = _calendar_is_valid($minute, 'minute') ? $minute : gmdate('i', time()); $timestamp = gmmktime($hour, $minute, 0, $month, $day, $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) { if (!_calendar_is_valid($day, 'day')) $day = 1; if (!_calendar_is_valid($month, 'month')) $month = date('m', time()); if (!_calendar_is_valid($year, 'year')) $year = date('Y', time()); switch ($type) { case ('day'): $min_date = mktime(0, 0, 0, $month, $day, $year); $max_date = $min_date + 86400 - 1; break; case ('week'): // use strtotime to find first day of requested week $min_date = strtotime('Jan 1, '.$year.' + '.intval($week - 1).' weeks'); $max_date = $min_date + (604800) - 1; break; case ('month'); $min_date = mktime(0, 0, 0, $month, 1, $year); // find the first day of the next month and subtract one day if ($month < 12) { $max_date = mktime(0, 0, 0, intval($month + 1), 1, $year) - 1; } else { $max_date = mktime(0, 0, 0, 1, 1, intval($year + 1)) - 1; } break; } 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 handlers for the calendars arguments */ function calendar_handler_arg_year($op, &$query, $argtype, $arg = '') { return calendar_handler_arg_type($op, $query, $argtype, $arg, 'calendar_year', 'YEAR'); } function calendar_handler_arg_month($op, &$query, $argtype, $arg = '') { return calendar_handler_arg_type($op, $query, $argtype, $arg, 'calendar_month', 'MONTH'); } function calendar_handler_arg_day($op, &$query, $argtype, $arg = '') { return calendar_handler_arg_type($op, $query, $argtype, $arg, 'calendar_day', 'DAY'); } function calendar_handler_arg_week($op, &$query, $argtype, $arg = '') { return calendar_handler_arg_type($op, $query, $argtype, $arg, 'calendar_week', 'WEEK'); } function calendar_handler_arg_type($op, &$query, $argtype, $arg, $field_name, $field_type) { // arguments don't actually do anything except tell us what period we're in // and make sure views knows that a page with this argument must be processed by views // the individual argument provides too little info to set up filters // filtering is done in hook_query_alter since we don't have complete view info until then $query->field_name[] = $field_name; $query->field_type[] = $field_type; $query->field_args[] = $arg; return; } /** * Implementation of hook_views_query() * Insert filters into the query based on the current calendar view and the selected fields */ function calendar_views_query_alter(&$query, &$view) { if (!calendar_is_calendar($view)) return; include_once('calendar_api.inc'); // make sure block views default to the current month // and make sure day view is not selected if ($view->build_type == 'block') { // if the real view values were reset in the page, reset them if ($view->reset_argument) { $view->argument = $view->reset_argument; $view->args = $view->reset_args; unset($view->reset_argument); unset($view->reset_args); } $view->calendar_type = 'month'; $view->args = explode('/', str_replace($view->url .'/', '', $_GET['mini'])); foreach ($view->argument as $delta => $argument) { if ($argument['type'] == 'calendar_month' && !$view->args[$delta]) { $view->args[$delta] = calendar_user_date('month'); } elseif ($argument['type'] == 'calendar_day') { unset($view->args[$delta]); } } } // 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; foreach ($view->argument as $delta => $argument) { if ($argument['type'] == 'calendar_week' || $argument['type'] == 'calendar_month') { // see if this is a week view and reconstruct view info as needed // the difference between a calendar_month arg and a calendar_week arg is the preceeding 'W' if (strstr($view->args[$delta], 'W') && $view->build_type == 'page') { // keep track of the original values so blocks rendered on the same page don't get wrong values $view->reset_argument = $view->argument; $view->reset_args = $view->args; $view->argument[$delta]['type'] = 'calendar_week'; $view->argument[$delta]['id'] = 'calendar_week'; $view->argument[$delta]['title'] = t('Week'); $view->args[$delta] = str_replace('W', '', $view->args[1]); } // 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') { $view->argument[$delta]['type'] = 'calendar_month'; $view->argument[$delta]['id'] = 'calendar_month'; $view->argument[$delta]['title'] = t('Month'); if ($view->args[$delta + 1]) { $view->argument[$delta + 1]['type'] = 'calendar_day'; $view->argument[$delta + 1]['id'] = 'calendar_day'; $view->argument[$delta + 1]['title'] = t('Day'); } } } } } foreach ($view->argument as $delta => $argument) { // there might be non-calendar arguments in the view, so we must ignore those 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; if ($argument['type'] == 'calendar_year') { $view->year = _calendar_is_valid($view->args[$delta], 'year') ? $view->args[$delta] : calendar_user_date('year'); $query_min = $view->year; $query_max = $view->year; $view->subtitle = l($view->year, $view->url .'/'. $view->year); // make sure we have at least a year available as an argument if (!$view->args[$delta]) { $view->args[$delta] = $view->year; } $view->calendar_type = str_replace('calendar_', '', $argument['type']); $view->delta['year'] = $delta; } if ($argument['type'] == 'calendar_month' && $view->args[$delta]) { $view->month = _calendar_is_valid($view->args[$delta], 'month') ? $view->args[$delta] : calendar_user_date('month'); $view->week = calendar_week('week', $view); $query_min .= '-'. sprintf('%02d', $view->month); $query_max .= '-'. sprintf('%02d', $view->month); // use the second day of the month because gm functions sometimes return the previous month $format = $view->build_type == 'block' || $view->calendar_type == 'year' ? 'M Y' : 'F Y'; $iso = $view->year .'-'. sprintf('%02d', $view->month) .'-02'; $view->subtitle = l(gmdate($format, strtotime($iso)), $view->url .'/'. $view->year .'/'. $view->month); $view->calendar_type = str_replace('calendar_', '', $argument['type']); $view->delta['month'] = $delta; } if ($argument['type'] == 'calendar_day' && $view->args[$delta]) { $view->day = _calendar_is_valid($view->args[$delta], 'day') ? $view->args[$delta] : calendar_user_date('day'); $query_min .= '-'. sprintf('%02d', $view->day); $query_max .= '-'. sprintf('%02d', $view->day); $iso = $view->year .'-'. sprintf('%02d', $view->month) .'-'. sprintf('%02d', $view->day) .'T12:00:00'; $view->subtitle = l(gmdate('l, F j Y', strtotime($iso)), $view->url .'/'. $view->year .'/'. $view->month .'/'. $view->day); $view->calendar_type = str_replace('calendar_', '', $argument['type']); $view->delta['week'] = $delta; } if ($argument['type'] == 'calendar_week' && $view->args[$delta]) { $view->week = _calendar_is_valid($view->args[$delta], 'week') ? $view->args[$delta] : 1; // use strtotime to find first day of requested week $week = $view->args[$delta]; $view->week = $week; $query_min = calendar_week('start_year', $view, $week) .'-'. sprintf('%02d', calendar_week('start_month', $view, $week)) .'-'. calendar_week('start_day', $view, $week) .'T00:00:00'; $query_max = calendar_week('end_year', $view, $week) .'-'. sprintf('%02d', calendar_week('end_month', $view, $week)) .'-'. calendar_week('end_day', $view, $week) .'T23:59:59'; $view->subtitle = l(t("Week of !date", array('!date' => gmdate('F j Y', calendar_week('start_timestamp', $view, $week)))), $view->url .'/'. $view->year .'/W'. $view->week); $view->calendar_type = str_replace('calendar_', '', $argument['type']); $view->month = calendar_week('start_month', $view, $week); $view->day = calendar_week('start_day', $view, $week); $view->delta['day'] = $delta; } } } if (!$query_min) { return; } // add elements to the query min a max values to finish a date value after supplying arg values // in the case of a year view, we need at least a year, month, and day to get a value timestamp date to search for $minmax = array( 'year' => array('-01-01', '-12-31'), 'month' => array('-01', '-31'), 'day' => array(' 00', ' 24'), 'hour' => array(':00:00', ':59:59'), 'minute' => array(':00', ':59'), ); $query_min .= $minmax[$view->calendar_type][0]; $query_max .= $minmax[$view->calendar_type][1]; $query_timestamp_min = strtotime($query_min); $query_timestamp_max = strtotime($query_max); $query_min .= '%'; $query_max .= '%'; // find all datetime fields in this view and add filters for them to the query $fields = calendar_fields(); foreach ($view->field as $delta => $field) { $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 '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[] = "(". calendar_sql('DATE', $this_field['timestamp_fromto'][1], $field_type, $end_offset) ." >='$query_min' AND ". calendar_sql('DATE', $this_field['timestamp_fromto'][0], $field_type, $start_offset) ." <='$query_max')"; $event_field_processed = TRUE; } elseif ($this_field['string_fromto']) { $queries[] = "(". calendar_sql('DATE', $this_field['string_fromto'][1], $field_type, $end_offset) ." >='$query_min' AND ". calendar_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[] = "(". calendar_sql('DATE', $this_field['fullname'], $field_type, $start_offset) .">='$query_min' AND ". calendar_sql('DATE', $field['fullname'], $field_type, $end_offset) ."<='$query_max')"; } elseif (strstr($this_field['type'], 'timestamp')) { $queries[] = "(". calendar_sql('DATE', $this_field['fullname'], $field_type, $start_offset) .">='$query_min' AND ". calendar_sql('DATE', $this_field['fullname'], $field_type, $end_offset) ."<='$query_max')"; } } } // 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; } /** * Implementation of hook_views_pre_view() */ function calendar_views_pre_view(&$view, &$items) { if (!calendar_is_calendar($view)) return; include_once('calendar_api.inc'); drupal_add_css(drupal_get_path('module', 'calendar') .'/calendar.css'); // add this so empty calendar results will produce blank calendar page // needed for smooth prev/next transitions and to make sure at least // a blank calendar is displayed in calendar block 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 (!$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; } } // add links to the top of the calendar to switch from one view to another if ($view->build_type == 'page') { $base_url = $view->url .'/'. $view->year; $month = $view->month ? $view->month : calendar_user_date('month'); $day = $view->day ? $view->day : calendar_user_date('day'); $week = $view->week ? $view->week : calendar_user_date('week'); if ($_GET['view']) { $append = 'view='. $_GET['view']; } $calendar_links[] = array('title' => t('Year'), 'href' => $view->calendar_type == 'year' ? '' : $view->url .'/'. $view->year, 'query' => $append); $calendar_links[] = array('title' => t('Month'), 'href' => $view->calendar_type == 'month' ? '' : $view->url .'/'. $view->year .'/'. $month , 'query' => $append); $calendar_links[] = array('title' => t('Week'), 'href' => $view->calendar_type == 'week' ? '' : $view->url .'/'. $view->year .'/W'. $week, 'query' => $append); $calendar_links[] = array('title' => t('Day'), 'href' => $view->calendar_type == 'day' ? '' : $view->url .'/'. $view->year .'/'. $month .'/'. $day, 'query' => $append); $output .= theme('calendar_links', $calendar_links, 'month'); $output .= '