You are here

function calendar_build_nodes in Calendar 5.2

Same name and namespace in other branches
  1. 6.2 calendar.module \calendar_build_nodes()
  2. 7 calendar.module \calendar_build_nodes()
  3. 7.2 calendar.module \calendar_build_nodes()

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.

2 calls to calendar_build_nodes()
calendar_views_pre_view in ./calendar.module
Implementation of hook_views_pre_view()
theme_calendar_ical_feed in ./calendar_ical.module
plugin that actually displays an ical feed

File

./calendar.inc, line 538
All the code used while processing a calendar is stored in this file and is included only when needed.

Code

function calendar_build_nodes(&$view, &$items, $display_type) {
  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');
  $fields = calendar_fields();
  foreach ($fields as $field) {
    $datefields[] = $field['query_name'];
  }
  $view_fields = _views_get_fields();
  $field_names = (array) array_keys($fields);
  $nodes = array();
  $i = 0;

  // explode out field and format info from the view
  foreach ($view->field as $delta => $data) {
    if ($fields[$data['id']]['visible'] !== FALSE && array_key_exists($data['field'], $fields)) {
      if (in_array($data['field'], $field_names)) {
        $field = $fields[$data['field']];
        $field_type = strstr($field['type'], 'string') ? 'string' : 'timestamp';
        $tz_handling = $field['tz_handling'];
        $label = $field['label'];
        $fromto = $field['fromto'];
        $fromto_alias = array(
          str_replace('.', '_', $fromto[0]),
          str_replace('.', '_', $fromto[1]),
        );
        $tz_alias = str_replace('.', '_', $field['timezone_field']);
        if (strstr($field['type'], 'cck')) {
          $format = date_formatter_format($data['options'], $field['field_name']);
        }
        else {
          switch ($data['handler']) {
            case 'views_handler_field_date_large':
              $format = variable_get('date_format_long', 'l, F j, Y - H:i');
              break;
            case 'views_handler_field_date':
              $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
              break;
            case 'views_handler_field_date_custom':
              $format = $data['options'];
              break;
            case 'views_handler_field_since':
            case 'views_handler_field_date_small':
            default:
              $format = variable_get('date_format_short', 'm/d/Y - H:i');
              break;
          }
        }

        // If there are multiple date fields in this calendar we may get
        // duplicate items from the other date fields, so add a way to
        // make sure each individual date field only gets added to the
        // calendar one time.
        $processed = array();
        foreach ($items as $pos => $item) {
          $delta = !empty($field['delta_field']) && !empty($item->{$field['delta_field']}) ? $item->{$field['delta_field']} : 0;
          $real_field = $field['field_name'];
          if (substr($field['type'], 0, 3) == 'cck') {
            $real_field = str_replace(array(
              '_value2',
              '_value',
            ), '', $field['field_name']);
          }
          $id = 'calendar:' . $item->nid . ':' . $real_field . ':' . $delta;
          if (!in_array($id, $processed) && isset($item->{$field['query_name']}) && isset($item->{$fromto_alias[0]})) {

            // Create from and to date values for each item, adjusted to
            // the correct timezone.
            $values = array(
              $item->{$fromto_alias}[0],
              isset($item->{$fromto_alias}[1]) ? $item->{$fromto_alias}[1] : $item->{$fromto_alias}[0],
            );
            $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);

            // Now $display_timezone determines how $item is split into
            // one entry per day, while $to_zone determines how date is displayed.
            // For now, use the date fields's timezone for the day splitting.
            $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 = variable_get('calendar_time_format_' . $view->name, 'H:i');
              $node->url = calendar_get_node_link($node);

              // Convert the table.field format in the fromto fields
              // to the table_field format used by the Views formatters.
              $node->{str_replace('.', '_', $fromto[0])} = $values[0];
              $node->{str_replace('.', '_', $fromto[1])} = $values[1];

              // Flag which datefield this node is using, in case
              // there are multiple date fields in the view.
              $node->datefield = $field['query_name'];

              // 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 != $field['query_name'] && in_array($key, $datefields)) {
                  unset($node->{$key});
                  foreach ($fields as $other_fields) {

                    // If the unused date has other fields, unset them, too.
                    if ($other_fields['query_name'] == $key) {
                      foreach ($other_fields['related_fields'] as $related_field) {
                        $key2 = str_replace('.', '_', $related_field);
                        unset($node->{$key2});
                      }
                    }
                  }
                }
              }

              // 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 {
                $node->date_id = $id . ':' . $pos;
                if ($view->build_type == 'page' && $view->calendar_type != 'year') {
                  calendar_node_stripe($view, $node, $field['query_name'], $field['query_name']);
                }
                $nodes[$node->calendar_start][] = $node;
                unset($node);
              }
              date_modify($next, '+1 second');
              $processed[] = $id;
              $now = date_format($next, DATE_FORMAT_DATE);
            }
          }
        }
      }
    }
  }
  return $nodes;
}