$item) { if (empty($item->calendar_start_date) || empty($item->calendar_end_date)) { continue; } $item_start = date_format($item->calendar_start_date, DATE_FORMAT_DATE); $item_end = date_format($item->calendar_end_date, DATE_FORMAT_DATE); if (($item_start >= $view->min_date_date && $item_start <= $view->max_date_date) || ($item_end >= $view->min_date_date && $item_end <= $view->max_date_date)) { $values[$item_start][$item_start][] = $item; } } $items = $values; ksort($items); $rows = array(); $curday = drupal_clone($view->min_date); switch ($view->granularity) { case 'year': $rows = array(); for ($i = 1; $i <= 12; $i++) { $rows[$i] = calendar_build_month($curday, $view, $items); } break; case 'month': $rows = calendar_build_month($curday, $view, $items); break; case 'day': $rows = calendar_build_day($curday, $view, $items); break; case 'week': $rows = calendar_build_week($curday, $view, $items); // Merge the day names in as the first row. $rows = array_merge(array(calendar_week_header($view)), $rows); break; } return $rows; } /** * Build one month. */ function calendar_build_month(&$curday, $view, $items) { $month = date_format($curday, 'n'); date_modify($curday, '-' . strval(date_format($curday, 'j')-1) . ' days'); $rows = array(); do { $rows = array_merge($rows, calendar_build_week($curday, $view, $items, TRUE)); $curday_date = date_format($curday, DATE_FORMAT_DATE); $curday_month = date_format($curday, 'n'); } while ($curday_month == $month && $curday_date <= $view->max_date_date); // Merge the day names in as the first row. $rows = array_merge(array(calendar_week_header($view)), $rows); return $rows; } /** * Build one week row. */ function calendar_build_week(&$curday, $view, $items, $check_month = FALSE) { $curday_date = date_format($curday, DATE_FORMAT_DATE); $weekdays = calendar_untranslated_days($items, $view); $today = date_format(date_now(date_default_timezone_name()), DATE_FORMAT_DATE); $month = date_format($curday, 'n'); $week = date_week($curday_date); $first_day = variable_get('date_first_day', 0); // move backwards to the first day of the week $day_wday = date_format($curday, 'w'); date_modify($curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days'); $curday_date = date_format($curday, DATE_FORMAT_DATE); // If we're displaying the week number, add it as the // first cell in the week. if (!empty($view->with_weekno) && $view->granularity != 'day') { $url = $view->get_path() .'/'. $view->year .'-W'. $week; if (!empty($view->display_types['week'])) { $weekno = l($week, $url, array('query' => !empty($view->append) ? $view->append : '')); } else { // Do not link week numbers, if Week views are disabled. $weekno = $week; } $rows[$week][] = array( 'data' => $weekno, 'id' => $view->name . '-weekno-' . $curday_date, 'class' => 'week'); } for ($i = 0; $i < 7; $i++) { $curday_date = date_format($curday, DATE_FORMAT_DATE); if ($check_month && ($curday_date < $view->min_date_date || $curday_date > $view->max_date_date || date_format($curday, 'n') != $month)) { $class = strtolower($weekdays[$i] . ($view->mini ? ' mini' : '')); $content = theme('calendar_empty_day'); } else { $content = calendar_build_day($curday, $view, $items); $class = strtolower($weekdays[$i] . ($curday_date == $today ? ' today' : '') . ($curday_date < $today ? ' past' : '') . ($curday_date > $today ? ' future' : '') . ($view->mini ? ' mini' : '')); } $rows[$week][] = array( 'data' => $view->mini ? $content : '
'. $content .'
', 'class' => $class, 'id' => $view->name . '-' . $curday_date); date_modify($curday, '+1 day'); } return $rows; } /** * Build the contents of a single day for the $rows results. */ function calendar_build_day($curday, $view, $items) { $curday_date = date_format($curday, DATE_FORMAT_DATE); $inner = ''; $selected = FALSE; foreach ($items as $date => $day) { if ($date == $curday_date) { $selected = TRUE; ksort($day); foreach ($day as $hour) { foreach ($hour as $item) { if (!$view->mini) { $theme = isset($item->calendar_node_theme) ? $item->calendar_node_theme : 'calendar_'. $view->granularity .'_node'; $inner .= theme($theme, $item, $view); } } } } } if (empty($inner)) { $inner = theme('calendar_empty_day'); } $content = theme('calendar_datebox', $curday_date, $view, $items, $selected) . $inner; return $content; } /** * Formats the weekday information into table header format * * @ingroup event_support * @return array with weekday table header data */ function calendar_week_header($view) { $len = isset($view->name_size) ? $view->name_size : (!empty($view->mini) ? 1 : 3); $with_week = !empty($view->with_weekno); // create week header $untranslated_days = calendar_untranslated_days(); if ($len == 99) { $translated_days = date_week_days_ordered(date_week_days(TRUE)); } else { $translated_days = date_week_days_ordered(date_week_days_abbr(TRUE)); } if ($with_week) { $row[] = array('header' => TRUE, 'class' => "days week", 'data' => ' '); } foreach ($untranslated_days as $delta => $day) { $label = $len < 3 ? drupal_substr($translated_days[$delta], 0 , $len) : $translated_days[$delta]; $row[] = array('header' => TRUE, 'class' => "days ". $day, 'data' => $label); } return $row; } /** * Array of untranslated day name abbreviations, forced to lowercase * and ordered appropriately for the site setting for the first day of week. * * The untranslated day abbreviation is used in css classes. */ function calendar_untranslated_days() { $untranslated_days = date_week_days_ordered(date_week_days_untranslated()); foreach ($untranslated_days as $delta => $day) { $untranslated_days[$delta] = strtolower(substr($day, 0, 3)); } return $untranslated_days; } /** * Take the array of items and alter it to an array of * calendar nodes that the theme can handle. * * Iterate through each datefield in the view and each item * returned by the query, and create pseudo date nodes. * * If there is more than one date field in the node, this will create * multiple nodes, one each with the right calendar date for that * field's value. If a field value has a date range that covers more than * one day, separate nodes will be created for each day in the field's * day range, limited to the minimum and maximum dates for the view. * * When we finish, we will have a distinct node for each distinct day * and date field. */ function calendar_build_nodes(&$view, &$items) { if (empty($view->min_date) || empty($view->max_date)) { return $items; } // Midnights are determined based on the same timezone as the View uses $display_timezone = date_timezone_get($view->min_date); $display_timezone_name = timezone_name_get($display_timezone); // Translate the view min and max dates to UTC values // so we can compare UTC dates to the view range. $min_utc = drupal_clone($view->min_date); date_timezone_set($min_utc, timezone_open('UTC')); $max_utc = drupal_clone($view->max_date); date_timezone_set($max_utc, timezone_open('UTC')); $min_zone_string = array(); // Will cache $min_utc-strings in various timezones $max_zone_string = array(); $view->nodes_per_page = 0; $type_names = node_get_types('names'); $datefields = array(); $fields = date_api_fields(); foreach ($view->argument['date_argument']->options['date_fields'] as $name) { $datefields[] = $fields['name'][$name]['query_name']; } $view_fields = date_api_views_fetch_fields('node', 'field'); $field_names = (array) array_keys($fields); $nodes = array(); $i = 0; foreach ($view->argument['date_argument']->options['date_fields'] as $name) { $field = $fields['name'][$name]; $field_type = strstr($field['type'], 'string') ? 'string' : 'timestamp'; $alias = $field['query_name']; $field_name = $field['field_name']; $fromto = $field['fromto']; $tz_handling = $field['tz_handling']; $label = isset($view->field[$name]) ? $view->field[$name]['label'] : $field['field_name']; $tz_alias = str_replace('.', '_', $field['timezone_field']); $db_tz = 'UTC'; $local_tz = date_default_timezone_name(); switch ($field['tz_handling']) { case 'none': $db_tz = date_default_timezone_name(); break; case 'date': $local_tz = 'date'; break; } if (strstr($field['type'], 'cck')) { $cck_field_name = str_replace(array('_value2', '_value'), '', $field_name); $format = date_formatter_format(isset($data['options']) ? $data['options'] : 'default', $cck_field_name); } elseif (!empty($view->field[$name]['date_format'])) { $format = $view->field[$name]['date_format']; switch ($view->field[$name]['date_format']) { case 'long': $format = variable_get('date_format_long', 'l, F j, Y - H:i'); break; case 'medium': $format = variable_get('date_format_medium', 'D, m/d/Y - H:i'); break; case 'custom': $format = $view->field[$name]['date_format']; break; default: $format = variable_get('date_format_short', 'm/d/Y - H:i'); break; } } else { $format = variable_get('date_format_short', 'm/d/Y - H:i'); } foreach ($items as $pos => $item) { if (isset($item->calendar_fields->$alias)) { // Create from and to date values for each item, adjusted to // the correct timezone. $values[0] = $item->calendar_fields->$fromto[0]; $values[1] = $item->calendar_fields->$fromto[1]; $db_tz = date_get_timezone_db($tz_handling, isset($item->$tz_alias) ? $item->$tz_alias : $display_timezone_name); $to_zone = date_get_timezone($tz_handling, isset($item->$tz_alias) ? $item->$tz_alias : $display_timezone_name); // NOTE: Now $display_timezone determines how $item is split // into one entry per day, while $to_zone determines how date is displayed. // Maybe someone wants to the date fields's timezone for the day splitting, // then use: $display_timezone_name = $to_zone; $values_display = array(); // Start date $date = date_make_date($values[0], $db_tz, $field['sql_type']); if ($db_tz != $to_zone) { date_timezone_set($date, timezone_open($to_zone)); } $values[0] = date_format($date, DATE_FORMAT_DATETIME); if ($display_timezone_name != $to_zone) { date_timezone_set($date, $display_timezone); $values_display[0] = date_format($date, DATE_FORMAT_DATETIME); } else { $values_display[0] = $values[0]; } // End date $date = date_make_date($values[1], $db_tz, $field['sql_type']); if ($db_tz != $to_zone) { date_timezone_set($date, timezone_open($to_zone)); } $values[1] = date_format($date, DATE_FORMAT_DATETIME); if ($display_timezone_name != $to_zone) { date_timezone_set($date, $display_timezone); $values_display[1] = date_format($date, DATE_FORMAT_DATETIME); } else { $values_display[1] = $values[1]; } // Now $values contain start and end date of a node, // expressed as strings in the display (local) timezone. // $values_utc does the same in UTC timezone. // Get calendar min and max day (not time) as strings in the // $display_timezone. Cache in $min_zone_string and $max_zone_string, // since many items or fields typically use the samee timezone. if (!isset($min_zone_string[$display_timezone_name])) { $date = drupal_clone($view->min_date); date_timezone_set($date, $display_timezone); $min_zone_string[$display_timezone_name] = date_format($date, DATE_FORMAT_DATE); $date = drupal_clone($view->max_date); date_timezone_set($date, $display_timezone); $max_zone_string[$display_timezone_name] = date_format($date, DATE_FORMAT_DATE); } // Create a node for each date within the field's date range, // limited to the view's date range (regarding only day, not time). $now = max($min_zone_string[$display_timezone_name], substr($values_display[0], 0, 10)); $to = min($max_zone_string[$display_timezone_name], substr($values_display[1], 0, 10)); $next = date_make_date($now, $display_timezone); if ($display_timezone_name != $to_zone) { // Make $start and $end (derived from $node) use the timezone $to_zone, just as $values[..] do date_timezone_set($next, timezone_open($to_zone)); } if (empty($to)) { $to = $now; } // $now and $next are midnight (in display timezone) on the first day where node will occur. // $to is midnight on the last day where node will occur. // All three were limited by the min-max date range of the view. while ($now <= $to) { $node = drupal_clone($item); // Make sure the pseudo node has the same properties a // regular node would have. if (isset($node->node_title) && !isset($node->title)) { $node->title = $node->node_title; } if (isset($node->node_type) && !isset($node->type)) { $node->type = $node->node_type; } $node->label = $label; $node->format = $format; $node->format_time = date_limit_format($format, array('hour', 'minute', 'second')); $node->url = calendar_get_node_link($node); //$node->$fromto[0] = $values[0]; //$node->$fromto[1] = $values[1]; // Flag which datefield this node is using, in case // there are multiple date fields in the view. $node->datefield = $alias; // If there are other datefields in the View, get rid // of them in this pseudo node. There should only be one // date in each calendar node. foreach ($node as $key => $val) { if ($key != $alias && in_array($key, $datefields)) { unset($node->$key); } } // Get start and end of current day $start = date_format($next, DATE_FORMAT_DATETIME); date_modify($next, '+1 day'); date_modify($next, '-1 second'); $end = date_format($next, DATE_FORMAT_DATETIME); // Get intersection of current day and the node value's duration (as strings in $to_zone timezone) $node->calendar_start = $values[0] < $start ? $start : $values[0]; $node->calendar_end = !empty($values[1]) ? ($values[1] > $end ? $end : $values[1]) : $node->calendar_start; // Make date objects $node->calendar_start_date = date_create($node->calendar_start, timezone_open($to_zone)); $node->calendar_end_date = date_create($node->calendar_end, timezone_open($to_zone)); // Change string timezones into // calendar_start and calendar_end are UTC dates as formatted strings $node->calendar_start = date_format($node->calendar_start_date, DATE_FORMAT_DATETIME); $node->calendar_end = date_format($node->calendar_end_date, DATE_FORMAT_DATETIME); unset($node->calendar_fields); if (isset($node) && (empty($node->calendar_start))) { // if no date for the node and no date in the item // there is no way to display it on the calendar unset($node); } else { $delta = !empty($field['delta_field']) && !empty($item->{$field['delta_field']}) ? $item->{$field['delta_field']} : 0; $real_field = $field_name; if (substr($field['type'], 0, 3) == 'cck') { $real_field = str_replace(array('_value2', '_value'), '', $field_name); } $id = 'calendar:'. $item->nid .':'. $real_field .':'. $delta .':'. $pos; calendar_node_stripe($view, $node, $alias, $alias); $node->date_id = $id; $nodes[] = $node; unset($node); } date_modify($next, '+1 second'); $now = date_format($next, DATE_FORMAT_DATE); } } } } return $nodes; } /** * Identify all potential date/timestamp fields. * * @return * array with fieldname, type, and table */ function _calendar_fields($base = 'node') { $cid = 'calendar_fields'; cache_clear_all($cid, 'cache_views'); $all_fields = date_api_views_fetch_fields($base, 'field'); $fields = array(); foreach ((array) $all_fields as $name => $val) { $fromto = array(); $tmp = explode('.', $name); $field_name = $tmp[1]; $table_name = $tmp[0]; $alias = str_replace('.', '_', $name); $handler = views_get_handler($table_name, $field_name, 'field'); $type = ''; // For cck fields, get the date type. if (isset($handler->content_field)) { if ($handler->content_field['type'] == 'date') { $type = 'cck_string'; } elseif ($handler->content_field['type'] == 'datestamp') { $type = 'cck_timestamp'; } elseif ($handler->content_field['type'] == 'datetime') { $type = 'cck_datetime'; } } // This is a core timestamp field. elseif (strstr($field_name, 'timestamp') || strstr($field_name, 'updated') || strstr($field_name, 'created') || strstr($field_name, 'changed')) { $type = 'timestamp'; } // Don't do anything if this is not a date field we can handle. if (!empty($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 $fields[$name]['table_name'] = $table_name; $fields[$name]['field_name'] = $field_name; $fields[$name]['type'] = $type; $related_fields = array(); $timezone_field = ''; $delta_field = ''; $rrule_field = ''; // Handling for simple timestamp fields if (empty($handler->content_field['type'])) { $fromto = array($alias, $alias); $tz_handling = 'site'; } // Handling for content field dates else { if ($handler->content_field['tz_handling']) { $tz_handling = $handler->content_field['tz_handling']; $db_info = content_database_info($handler->content_field); if ($tz_handling == 'date') { $offset_field = $table_name .'.'. $db_info['columns']['offset']['column']; $timezone_field = $table_name .'.'. $db_info['columns']['timezone']['column']; $related_fields = array( $table_name .'.'. $field_name, $table_name .'.'. $db_info['columns']['value2']['column'], $table_name .'.'. $db_info['columns']['timezone']['column'], $table_name .'.'. $db_info['columns']['offset']['column'], $table_name .'.'. $db_info['columns']['offset2']['column'], ); } else { $related_fields = array( $table_name .'.'. $field_name, $table_name .'.'. $db_info['columns']['value2']['column'], ); } } // Get the delta value into the query. if (!empty($handler->content_field['multiple'])) { array_push($related_fields, "$table_name.delta"); $delta_field = 'delta'; } if (!empty($db_info['columns']['rrule'])) { $rrule_field = $table_name .'.'. $db_info['columns']['rrule']['column']; } // Handling for cck fromto dates switch ($handler->content_field['type']) { case 'date': case 'datetime': case 'datestamp': $db_info = content_database_info($handler->content_field); $fromto = array( $table_name .'_'. $db_info['columns']['value']['column'], $table_name .'_'. ($handler->content_field['todate'] ? $db_info['columns']['value2']['column'] : $db_info['columns']['value']['column']), ); break; } } if (is_array($handler->content_field['granularity'])) { $granularity = $handler->content_field['granularity']; } else { $granularity = array('year', 'month', 'day', 'hour', 'minute'); } // skip this step on second occurrence of fromto date fields, if more than one exists in view // 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 switch ($type) { case 'cck_string': $sql_type = DATE_ISO; break; case 'cck_datetime': $sql_type = DATE_DATETIME; break; default: $sql_type = DATE_UNIX; break; } $fields['name'][$name] = array( 'type' => $type, 'sql_type' => $sql_type, 'label' => $val['group'] .': '. $val['title'], 'granularity' => $granularity, 'fullname' => $name, 'table_name' => $table_name, 'field_name' => $field_name, 'query_name' => $alias, 'fromto' => $fromto, 'tz_handling' => $tz_handling, 'offset_field' => $offset_field, 'timezone_field' => $timezone_field, 'delta_field' => $delta_field, 'rrule_field' => $rrule_field, 'related_fields' => $related_fields, ); $fields['alias'][$alias] = $fields['name'][$name]; } } //cache_set($cid, $fields, 'cache_views'); return $fields; }