You are here

function content_field in Content Construction Kit (CCK) 6.3

Same name and namespace in other branches
  1. 5 content.module \content_field()
  2. 6 content.module \content_field()
  3. 6.2 content.module \content_field()

Implementation of hook_field(). Handles common field housekeeping.

This implementation is special, as content.module does not define any field types. Instead, this function gets called after the type-specific hook, and takes care of default stuff common to all field types.

Db-storage ops ('load', 'insert', 'update', 'delete', 'delete revisions') are not executed field by field, and are thus handled separately in content_storage.

The 'view' operation constructs the $node in a way that you can use drupal_render() to display the formatted output for an individual field. i.e. print drupal_render($node->countent['field_foo']);

The code now supports both single value formatters, which theme an individual item value as has been done in previous version of CCK, and multiple value formatters, which theme all values for the field in a single theme. The multiple value formatters could be used, for instance, to plot field values on a single map or display them in a graph. Single value formatters are the default, multiple value formatters can be designated as such in formatter_info().

The node array will look like: $node->content['field_foo']['wrapper'] = array( '#type' => 'content_field', '#title' => 'label' '#field_name' => 'field_name', '#node' => $node, // Value of the $teaser param of hook_nodeapi('view'). '#teaser' => $teaser, // Value of the $page param of hook_nodeapi('view'). '#page' => $page, // The curent rendering context ('teaser', 'full', NODE_BUILD_SEARCH_INDEX...). '#context' => $context, 'items' => 0 => array( '#item' => $items[0], // Only for 'single-value' formatters '#theme' => $theme, '#field_name' => 'field_name', '#type_name' => $node->type, '#formatter' => $formatter_name, '#node' => $node, '#delta' => 0, ), 1 => array( '#item' => $items[1], // Only for 'single-value' formatters '#theme' => $theme, '#field_name' => 'field_name', '#type_name' => $node->type, '#formatter' => $formatter_name, '#node' => $node, '#delta' => 1, ), // Only for 'multiple-value' formatters '#theme' => $theme, '#field_name' => 'field_name', '#type_name' => $node->type, '#formatter' => $formatter_name, ), );

4 calls to content_field()
content_rules_action_populate_field_validate in includes/content.rules.inc
Validate the chosen value or php code.
content_view_field in ./content.module
Render a single field, fully themed with label and multiple values.
fieldgroup_view_group in modules/fieldgroup/fieldgroup.module
Render a single field group, fully themed with label.
_content_field_invoke_default in ./content.module
Invoke content.module's version of a field hook.

File

./content.module, line 708
Allows administrators to associate custom fields to content types.

Code

function content_field($op, &$node, $field, &$items, $teaser, $page, $wrappers = NULL) {
  switch ($op) {
    case 'validate':

      // If the field is configured for multiple values and these are handled
      // by content module, we need to filter out items flagged for removal and
      // count non-empty items to enforce field requirement settings.
      if ($field['multiple'] >= 1 && content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) {
        module_load_include('inc', 'content', 'includes/content.node_form');

        // Note that the $teaser argument for nodeapi('validate') is the $form.
        content_multiple_value_nodeapi_validate($node, $field, $items, $teaser);
      }
      break;
    case 'presave':
      if (!empty($node->devel_generate)) {
        include_once './' . drupal_get_path('module', 'content') . '/includes/content.devel.inc';
        content_generate_fields($node, $field);
        $items = $node->{$field['field_name']};
      }

      // Manual node_save calls might not have all fields filled in.
      // On node insert, we need to make sure all tables get at least an empty
      // record, or subsequent edits, using drupal_write_record() in update mode,
      // won't insert any data.
      // Missing fields on node update are handled in content_storage().
      if (empty($items) && !isset($node->nid)) {
        foreach (array_keys($field['columns']) as $column) {
          $items[0][$column] = NULL;
        }
        $node->{$field}['field_name'] = $items;
      }

      // If there was an AHAH add more button in this field, don't save it.
      // TODO: is it still needed ?
      unset($items[$field['field_name'] . '_add_more']);
      if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) {

        // Reorder items to account for drag-n-drop reordering.
        $items = _content_sort_items($field, $items);
      }

      // Filter out items flagged for removal.
      $items = content_set_empty($field, $items);
      break;
    case 'view':
      $addition = array();

      // Previewed nodes bypass the 'presave' op, so we need to do some massaging.
      if ($node->build_mode == NODE_BUILD_PREVIEW) {
        if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) {

          // Reorder items to account for drag-n-drop reordering.
          $items = _content_sort_items($field, $items);
        }

        // Filter out items flagged for removal.
        $items = content_set_empty($field, $items);
      }

      // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===.
      if ($node->build_mode === NODE_BUILD_NORMAL || $node->build_mode == NODE_BUILD_PREVIEW) {
        $context = $teaser ? 'teaser' : 'full';
      }
      else {
        $context = $node->build_mode;
      }

      // The field may be missing info for $contexts added by modules
      // enabled after the field was last edited.
      $formatter_name = isset($field['display_settings'][$context]) && isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
      if ($formatter = _content_get_formatter($formatter_name, $field['type'])) {
        $theme = $formatter['module'] . '_formatter_' . $formatter_name;
        $single = content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE;
        $label_display = isset($field['display_settings']['label']['format']) ? $field['display_settings']['label']['format'] : 'above';

        // Do not include field labels when indexing content.
        if ($context == NODE_BUILD_SEARCH_INDEX) {
          $label_display = 'hidden';
        }
        $element = array(
          '#type' => 'content_field',
          '#title' => check_plain(t($field['widget']['label'])),
          '#field_name' => $field['field_name'],
          '#access' => $formatter_name != 'hidden' && content_access('view', $field, NULL, $node),
          '#label_display' => $label_display,
          '#node' => $node,
          '#teaser' => $teaser,
          '#page' => $page,
          '#context' => $context,
          '#single' => $single,
          'items' => array(),
        );

        // Fill-in items.
        foreach (array_keys($items) as $weight => $delta) {
          $element['items'][$delta] = array(
            '#item' => $items[$delta],
            '#weight' => $weight,
          );
        }

        // Append formatter information either on each item ('single-value' formatter)
        // or at the upper 'items' level ('multiple-value' formatter)
        $format_info = array(
          '#theme' => $theme,
          '#field_name' => $field['field_name'],
          '#type_name' => $node->type,
          '#formatter' => $formatter_name,
          '#node' => $node,
        );
        if ($single) {
          foreach ($items as $delta => $item) {
            $element['items'][$delta] += $format_info;
            $element['items'][$delta]['#item']['#delta'] = $delta;
          }
        }
        else {
          $element['items'] += $format_info;
        }

        // The wrapper lets us get the themed output for the whole field
        // to populate the $FIELD_NAME_rendered variable for node templates,
        // and hide it from the $content variable if needed.
        // See 'preprocess_node' op and theme_content_field_wrapper()?
        $wrapper = array(
          'field' => $element,
          '#weight' => $field['widget']['weight'],
          '#post_render' => array(
            'content_field_wrapper_post_render',
          ),
          '#field_name' => $field['field_name'],
          '#type_name' => $node->type,
          '#context' => $context,
        );
        $addition = array(
          $field['field_name'] => $wrapper,
        );
      }
      return $addition;
    case 'alter':

      // Add back the formatted values in the 'view' element,
      // so that tokens and node templates can use it.
      // Note: Doing this in 'preprocess_node' breaks token integration.
      // The location of the field's rendered output depends on whether the
      // field is in a fieldgroup or not. _content_field_invoke_default() takes
      // care of pre-searching all fields and providing the wrappers. In case of
      // direct calls, search the wrappers ourselves.
      if (is_null($wrappers)) {
        $wrappers = content_get_nested_elements($node->content, $field['field_name']);
      }
      foreach ($wrappers as $wrapper) {
        $element = $wrapper['field'];

        // '#single' is not set if the field is hidden or inaccessible.
        if (isset($element['#single'])) {
          if (!empty($element['#single'])) {

            // Single value formatter.
            foreach (element_children($element['items']) as $delta) {

              // '#children' is not set if the field is empty.
              $items[$delta]['view'] = isset($element['items'][$delta]['#children']) ? $element['items'][$delta]['#children'] : '';
            }
          }
          elseif (isset($element['items']['#children'])) {

            // Multiple values formatter.
            $items[0]['view'] = $element['items']['#children'];
          }
        }
        else {

          // Hidden or inaccessible field.
          $items[0]['view'] = '';
        }
      }
      break;
    case 'preprocess_node':

      // Add $FIELD_NAME_rendered variables.
      $addition = array(
        $field['field_name'] . '_rendered' => '',
      );

      // The location of the field's rendered output depends on whether the
      // field is in a fieldgroup or not. _content_field_invoke_default() takes
      // care of pre-searching all fields and providing the wrappers. In case of
      // direct calls, search the wrappers ourselves.
      if (is_null($wrappers)) {
        $wrappers = content_get_nested_elements($node->content, $field['field_name']);
      }
      foreach ($wrappers as $wrapper) {

        // '#children' is not set if the field is empty.
        $addition[$field['field_name'] . '_rendered'] .= isset($wrapper['#children']) ? $wrapper['#children'] : '';
      }
      return $addition;
    case 'prepare translation':
      $addition = array();
      if (isset($node->translation_source->{$field}['field_name'])) {
        $addition[$field['field_name']] = $node->translation_source->{$field}['field_name'];
      }
      return $addition;
  }
}