Skip to content
content.module 32.9 KiB
Newer Older
John VanDyk's avatar
John VanDyk committed
<?php
// $Id$
/**
 * @file
 * Allows administrators to define new content types.
John VanDyk's avatar
John VanDyk committed
 */

define('CONTENT_DB_STORAGE_PER_FIELD', 0);
define('CONTENT_DB_STORAGE_PER_CONTENT_TYPE', 1);
John VanDyk's avatar
John VanDyk committed
/**
 * Implementation of hook_help().
 */
function content_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('<strong>CCK:</strong> Allows administrators to define new content types.');
John VanDyk's avatar
John VanDyk committed
  }

  if (preg_match('!^admin/node/types/.*/display$!', $section)) {
    return t("Configure how this content type's fields and field labels should be displayed when it's viewed in teaser and full-page mode.");
  }
  
John VanDyk's avatar
John VanDyk committed
  if (strpos($section, 'node/add') === 0) {
John Hwang's avatar
John Hwang committed
    $types = content_types();
John VanDyk's avatar
John VanDyk committed
    foreach ($types as $type_name => $type) {
      if ($section == 'node/add#'. $type_name) {
John Hwang's avatar
John Hwang committed
        return t($types[$type_name]['description']);
John VanDyk's avatar
John VanDyk committed
      }
      if ($section == 'node/add/'. $type_name) {
John Hwang's avatar
John Hwang committed
        return t($types[$type_name]['help']);
John VanDyk's avatar
John VanDyk committed
      }
    }
  }
}

/**
 * Implementation of hook_perm().
 */
function content_perm() {
  $perms = array('administer content types');
John Hwang's avatar
John Hwang committed
  foreach (content_types() as $name => $type) {
John VanDyk's avatar
John VanDyk committed
    $perms[] = 'create '. $name .' content';
    $perms[] = 'edit own '. $name .' content';
John VanDyk's avatar
John VanDyk committed
  }
  return $perms;
}

/**
 * Implementation of hook_init().
 */
function content_init() {
  // ensure we are not serving a cached page
  if (function_exists('drupal_set_content')) {
    // we don't do this in hook_menu to ensure the files are already included when
    // views_menu is executed
    if (module_exist('views')) {
      include_once('./'. drupal_get_path('module', 'content') .'/content_views.inc');
    }
    // according to http://drupal.org/node/60526, this should not go in hook_menu
    if (module_exist('pathauto')) {
      include_once('./'. drupal_get_path('module', 'content') .'/content_pathauto.inc');
    }
  }
}

John VanDyk's avatar
John VanDyk committed
/**
 * Implementation of hook_menu().
 */
function content_menu($may_cache) {
  if (!$may_cache) {
    // Only include administrative callbacks if we are viewing an admin page.
    if (arg(0) == 'admin') {
      include_once('./'. drupal_get_path('module', 'content') .'/content_admin.inc');
    }
    theme_add_style(drupal_get_path('module', 'content') .'/content.css');
  }

John VanDyk's avatar
John VanDyk committed
  $items = array();
  $access = user_access('administer content types');
John VanDyk's avatar
John VanDyk committed
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/node/types',
      'title' => t('content types'),
      'callback' => '_content_admin_type_overview',
      'access' => $access,
    );
    $items[] = array(
      'path' => 'admin/node/types/list',
      'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
    );
    $items[] = array(
      'path' => 'admin/node/types/add',
      'title' => t('add content type'),
      'callback' => '_content_admin_type_edit',
      'access' => $access,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/node/types/fields',
      'title' => t('fields'),
      'callback' => '_content_admin_type_fields',
      'access' => $access,
      'type' => MENU_LOCAL_TASK,
    );
John VanDyk's avatar
John VanDyk committed

John Hwang's avatar
John Hwang committed
    $types = content_types();
John VanDyk's avatar
John VanDyk committed
    foreach ($types as $type_name => $type) {
      $items[] = array(
        'path' => 'node/add/'. $type_name,
John Hwang's avatar
John Hwang committed
        'title' => t($type['label']),
        'access' => user_access('create '. $type_name .' content'),
      );
John VanDyk's avatar
John VanDyk committed
    }
  }
  else {
    if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types' && arg(3)) {
John Hwang's avatar
John Hwang committed
      $type = content_types(arg(3));
John VanDyk's avatar
John VanDyk committed

John Hwang's avatar
John Hwang committed
      if ($type) {
        $items[] = array(
          'path' => 'admin/node/types/'. arg(3),
John Hwang's avatar
John Hwang committed
          'title' => t($type['label']),
          'callback' => '_content_admin_type_edit',
          'access' => $access,
          'type' => MENU_CALLBACK,
        );
        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/delete',
          'callback' => '_content_admin_type_delete',
          'access' => $access,
John VanDyk's avatar
John VanDyk committed
          'callback arguments' => array(arg(3)),
          'type' => MENU_CALLBACK,
        );
        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/duplicate',
          'callback' => '_content_admin_type_edit',
          'access' => $access,
          'type' => MENU_CALLBACK,
        );
John VanDyk's avatar
John VanDyk committed

        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/edit',
John VanDyk's avatar
John VanDyk committed
          'title' => t('edit'),
          'type' => MENU_DEFAULT_LOCAL_TASK,
          'weight' => -10,
        );
John VanDyk's avatar
John VanDyk committed

        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/fields',
John VanDyk's avatar
John VanDyk committed
          'title' => t('manage fields'),
          'callback' => '_content_admin_field_overview',
          'access' => $access,
          'callback arguments' => array(arg(3)),
          'type' => MENU_LOCAL_TASK,
          'weight' => 0,
        );
        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/display',
          'title' => t('display fields'),
          'callback' => 'content_admin_display_overview_form',
          'access' => $access,
          'callback arguments' => array(arg(3)),
          'type' => MENU_LOCAL_TASK,
          'weight' => 1,
        );
        $items[] = array(
          'path' => 'admin/node/types/'. arg(3) .'/add_field',
          'callback' => '_content_admin_field_add',
          'access' => $access,
John VanDyk's avatar
John VanDyk committed
          'callback arguments' => array(arg(3)),
John VanDyk's avatar
John VanDyk committed

        if (arg(4) == 'fields' && arg(5)
John Hwang's avatar
John Hwang committed
            && isset($type['fields'][arg(5)])) {
          $items[] = array(
            'path' => 'admin/node/types/'. arg(3) .'/fields/'. arg(5),
John Hwang's avatar
John Hwang committed
            'title' => t($type['fields'][arg(5)]['widget']['label']),
            'callback' => '_content_admin_field',
            'access' => $access,
John VanDyk's avatar
John VanDyk committed
            'callback arguments' => array(arg(3), arg(5)),
            'type' => MENU_CALLBACK,
          );
          $items[] = array(
            'path' => 'admin/node/types/'. arg(3) .'/fields/'. arg(5) .'/remove',
John VanDyk's avatar
John VanDyk committed
            'title' => t('remove field'),
            'callback' => '_content_admin_field_remove',
            'access' => $access,
John VanDyk's avatar
John VanDyk committed
            'callback arguments' => array(arg(3), arg(5)),
            'type' => MENU_CALLBACK,
          );
John VanDyk's avatar
John VanDyk committed
 */
John VanDyk's avatar
John VanDyk committed
  $types = array();
John Hwang's avatar
John Hwang committed
  foreach (content_types() as $name => $type) {
    $types[$name] = array('name' => t($type['label']), 'base' => 'content');
John VanDyk's avatar
John VanDyk committed
  }
  return $types;
}

/**
 * Implementation of hook_access().
 */
function content_access($op, $node) {
  global $user;
  $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);

  if ($op == 'create') {
    return user_access('create '. $type .' content');
  }

  if ($op == 'update' || $op == 'delete') {
    if ((user_access('edit own '. $type .' content') && $user->uid == $node->uid) ||
      user_access('edit '. $type .' content')) {
John VanDyk's avatar
John VanDyk committed
      return TRUE;
    }
  }
}

/**
 * Implementation of hook_load().
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 *
 * When loading one of the content.module nodes, we need to let each field handle
 * its own loading. This can make for a number of queries in some cases, so we
 * cache the loaded object structure and invalidate it during the update process.
John VanDyk's avatar
John VanDyk committed
 */
function content_load($node) {
  $cid = 'content:'. $node->nid .':'. $node->vid;
Jonathan Chaffer's avatar
Jonathan Chaffer committed
  if ($cached = cache_get($cid)) {
    return unserialize($cached->data);
  }
  else {
John Hwang's avatar
John Hwang committed
    $default_additions = _content_field_invoke_default('load', $node);
    if ($default_additions) {
      foreach ($default_additions as $key => $value) {
        $node->$key = $value;
      }
    }
Jonathan Chaffer's avatar
Jonathan Chaffer committed
    $additions = _content_field_invoke('load', $node);
John Hwang's avatar
John Hwang committed
    if ($additions) {
      foreach ($additions as $key => $value) {
        $default_additions[$key] = $value;
      }
    }
    cache_set($cid, serialize($default_additions), CACHE_PERMANENT);
    return $default_additions;
Jonathan Chaffer's avatar
Jonathan Chaffer committed
  }
John VanDyk's avatar
John VanDyk committed
}

/**
 * Implementation of hook_form().
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 *
 * Each field defines its own component of the content entry form, via its
 * chosen widget.
John VanDyk's avatar
John VanDyk committed
 */
function content_form(&$node, &$param) {
John Hwang's avatar
John Hwang committed
  $type = content_types($node->type);
  // Set form parameters so we can accept file uploads.
  $form['#attributes'] = array("enctype" => "multipart/form-data");
John VanDyk's avatar
John VanDyk committed

  $form['title'] = array(
    '#type' => 'textfield',
John Hwang's avatar
John Hwang committed
    '#title' => t($type['title_label']),
    '#required' => TRUE,
    '#default_value' => $node->title,
  );
  _content_widget_invoke('prepare form values', $node);
  $form = array_merge($form, _content_widget_invoke('form', $node));
John VanDyk's avatar
John VanDyk committed
}

/**
 * Implementation of hook_validate().
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 *
 * Both widgets and fields have a chance to raise error flags when a node is
 * being validated.
John VanDyk's avatar
John VanDyk committed
 */
function content_validate(&$node) {
  _content_widget_invoke('validate', $node);
  _content_widget_invoke('process form values', $node);
  _content_field_invoke('validate', $node);
John Hwang's avatar
John Hwang committed
  _content_field_invoke_default('validate', $node);
John VanDyk's avatar
John VanDyk committed
}

Jonathan Chaffer's avatar
Jonathan Chaffer committed
 *
 * At submit time, the widget does whatever data massaging is necessary so that
 * the field has the content in the expected format and can commit the changes
 * to the database.
  _content_widget_invoke('submit', $node);
  _content_widget_invoke('process form values', $node);
  _content_field_invoke('submit', $node);
John Hwang's avatar
John Hwang committed
  _content_field_invoke_default('submit', $node);
/**
 * Implementation of hook_insert().
 */
function content_insert(&$node) {
  _content_field_invoke('insert', $node);
John Hwang's avatar
John Hwang committed
  _content_field_invoke_default('insert', $node);
}

/**
 * Implementation of hook_update().
 */
function content_update(&$node) {
  _content_field_invoke('update', $node);
John Hwang's avatar
John Hwang committed
  _content_field_invoke_default('update', $node);
  cache_clear_all('content:'. $node->nid .':'. $node->vid);
}

/**
 * Implementation of hook_delete().
 */
function content_delete(&$node) {
  _content_field_invoke('delete', $node);
John Hwang's avatar
John Hwang committed
  _content_field_invoke_default('delete', $node);
  db_query('DELETE FROM {node_'. $node->type .'} WHERE nid = %d', $node->nid);
  cache_clear_all('content:'. $node->nid, TRUE);
John VanDyk's avatar
John VanDyk committed
/**
 * Implementation of hook_view().
 */
function content_view(&$node, $teaser = FALSE, $page = FALSE) {
  if ($node->in_preview) {
    _content_widget_invoke('process form values', $node);
  }
  $node->body = implode('', _content_field_view($node, FALSE, $page));
  $node->teaser = implode('', _content_field_view($node, TRUE, $page));
  $node->readmore = (strlen($node->teaser) < strlen($node->body));
John VanDyk's avatar
John VanDyk committed
}

/**
 * Implementation of hook_nodeapi().
 *
 * When a revision is deleted, make sure the appropriate cache item is cleared.
 */
function content_nodeapi(&$node, $op, $teaser, $page) {
  switch ($op) {
    case 'delete revision':
John Hwang's avatar
John Hwang committed
      if (content_types($node->type)) {
        _content_field_invoke('delete revision', $node);
        _content_field_invoke_default('delete revision', $node);
        cache_clear_all('content:'. $node->nid .':'. $node->vid);
        db_query('DELETE FROM {node_'. $node->type .'} WHERE vid = %d', $node->vid);
/**
 * 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 the database interface for field types that do not choose to
 * do their own storage.
 */
function content_field($op, &$node, $field, &$node_field, $teaser, $page) {
  $db_info = content_database_info($field);
  switch ($op) {
    case 'load':
      $column_names = array();
      foreach ($db_info['columns'] as $column => $attributes) {
        $column_names[] = $attributes['column'] .' AS '. $column;
        $result = db_query('SELECT '. implode(', ', $column_names) .' FROM {'. $db_info['table'] .'} WHERE vid = %d ORDER BY delta', $node->vid);
        $values = array();
        while ($value = db_fetch_array($result)) {
          $values[] = $value;
        }
        $additions = array($field['field_name'] => $values);
      }
      else {
        $result = db_query('SELECT '. implode(', ', $column_names) .' FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid);
        $additions = array($field['field_name'] => array(db_fetch_array($result)));
      foreach ($node_field as $delta => $item) {
        $column_names = array();
        $column_placeholders = array();
        foreach ($db_info['columns'] as $column => $attributes) {
          $column_names[] = $attributes['column'];
          if ($item[$column] == '' && !$attributes['not null'] && !$field['required']) {
            $column_placeholders[] = '%s';
            $column_assignments[] = $attributes['column'] .' = %s';
            $item[$column] = 'NULL';
          }
          else {
           switch ($attributes['type']) {
           case 'int':
            case 'bigint':
              $column_placeholders[] = '%d';
              $column_assignments[] = $attributes['column'] .' = %d';
              break;
            case 'float':
              $column_placeholders[] = '%f';
              $column_assignments[] = $attributes['column'] .' = %f';
              break;
            default:
              $column_placeholders[] = "'%s'";
              $column_assignments[] = $attributes['column'] ." = '%s'";
        $data[] = $node->vid;
        $data[] = $node->nid;
        if ($field['multiple']) {
          $data[] = $delta;
        }

          db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data);
          if (db_result(db_query('SELECT COUNT(*) FROM {'. $db_info['table'] .'} WHERE vid = %d AND nid = %d', $node->vid, $node->nid))) {
            db_query('UPDATE {'. $db_info['table'] .'} SET '. implode(', ', $column_assignments) .' WHERE vid = %d AND nid = %d', $data);
          }
          else {
            db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid) VALUES ('. implode(', ', $column_placeholders) .', %d, %d)', $data);
          }
      if ($field['multiple']) {
        // Delete and insert, rather than update, in case a field was added.
        db_query('DELETE FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid);
      }
      foreach ($node_field as $delta => $item) {
        $column_names = array();
        $column_placeholders = array();
        foreach ($db_info['columns'] as $column => $attributes) {
          $column_names[] = $attributes['column'];
          if ($item[$column] == '' && !$attributes['not null'] && !$field['required']) {
            $column_placeholders[] = '%s';
            $column_assignments[] = $attributes['column'] .' = %s';
            $item[$column] = 'NULL';
          }
          else {
           switch ($attributes['type']) {
            case 'int':
            case 'mediumint':
            case 'bigint':
              $column_placeholders[] = '%d';
              $column_assignments[] = $attributes['column'] .' = %d';
              break;
            case 'float':
              $column_placeholders[] = '%f';
              $column_assignments[] = $attributes['column'] .' = %f';
              break;
            default:
              $column_placeholders[] = "'%s'";
              $column_assignments[] = $attributes['column'] ." = '%s'";
        $data[] = $node->vid;
        $data[] = $node->nid;
        if ($field['multiple']) {
          $data[] = $delta;
        }

          db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data);
          if (db_result(db_query('SELECT COUNT(*) FROM {'. $db_info['table'] .'} WHERE vid = %d AND nid = %d', $node->vid, $node->nid))) {
            db_query('UPDATE {'. $db_info['table'] .'} SET '. implode(', ', $column_assignments) .' WHERE vid = %d AND nid = %d', $data);
          }
          else {
            db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid) VALUES ('. implode(', ', $column_placeholders) .', %d, %d)', $data);
          }
        }
      }
      return;

    case 'delete':
      // Delete using nid rather than vid to purge all revisions.
      db_query('DELETE FROM {'. $db_info['table'] .'} WHERE nid = %d', $node->nid);
John Hwang's avatar
John Hwang committed

    case 'delete revision':
      db_query('DELETE FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid);
      return;
John VanDyk's avatar
John VanDyk committed
/**
John Hwang's avatar
John Hwang committed
 * For each operation, both this function and _content_field_invoke_default() are
 * called so that the default database handling can occur.
John VanDyk's avatar
John VanDyk committed
 */
function _content_field_invoke($op, &$node, $teaser = NULL, $page = NULL) {
John VanDyk's avatar
John VanDyk committed
  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
John Hwang's avatar
John Hwang committed
  $type = content_types($type_name);
John VanDyk's avatar
John VanDyk committed
  $field_types = _content_field_types();

  $return = array();
John Hwang's avatar
John Hwang committed
  foreach ($type['fields'] as $field) {
    $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();

John VanDyk's avatar
John VanDyk committed
    $module = $field_types[$field['type']]['module'];
    $function = $module .'_field';
    if (function_exists($function)) {
      $result = $function($op, $node, $field, $node_field, $teaser, $page);
John VanDyk's avatar
John VanDyk committed
        $return = array_merge($return, $result);
      }
      else if (isset($result)) {
        $return[] = $result;
      }
    }
    // test for values in $node_field in case modules added items on insert
    if (isset($node->$field['field_name']) || count($node_field)) {
John Hwang's avatar
John Hwang committed
      $node->$field['field_name'] = $node_field;
    }
  }

  return $return;
}

/**
 * Invoke content.module's version of a field hook.
 */
function _content_field_invoke_default($op, &$node, $teaser = NULL, $page = NULL) {
  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
  $type = content_types($type_name);
  $field_types = _content_field_types();

  $return = array();
  foreach ($type['fields'] as $field) {
    $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();

    $db_info = content_database_info($field);
    if (count($db_info['columns'])) {
      $result = content_field($op, $node, $field, $node_field, $teaser, $page);
      if (is_array($result)) {
        $return = array_merge($return, $result);
      }
      else if (isset($result)) {
        $return[] = $result;
      }
    if (isset($node->$field['field_name'])) {
      $node->$field['field_name'] = $node_field;
    }
John VanDyk's avatar
John VanDyk committed
  }

  return $return;
}

/**
 * Format field output based on display settings.
 */
function _content_field_view(&$node, $teaser = NULL, $page = NULL) {
  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
  $type = content_types($type_name);
  $field_types = _content_field_types();
  $context = $teaser ? 'teaser' : 'full';

  $return = array();
  if (count($type['fields'])) {
    foreach ($type['fields'] as $field) {
      $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
      $formatter = isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
      $value = '';

      if ($formatter != 'hidden') {
        $module = $field_types[$field['type']]['module'];
        $callbacks = module_invoke($module, 'field_settings', 'callbacks', $field);
        if (isset($callbacks['view']) && $callbacks['view']) {
          $function = $module .'_field';
          if (function_exists($function)) {
            $value = $function('view', $node, $field, $node_field, $teaser, $page);
          }
        }
        if (empty($value)) {
          foreach ($node_field as $delta => $item) {
            $node_field[$delta]['view'] = content_format($field, $item, $formatter, $node);
          $value = theme('field', $node, $field, $node_field, $teaser, $page);
      // test for values in $node_field in case modules added items
      if (isset($node->$field['field_name']) || count($node_field)) {
        $node->$field['field_name'] = $node_field;
      }
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 * Invoke a widget hook.
 */
function _content_widget_invoke($op, &$node) {
  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
John Hwang's avatar
John Hwang committed
  $type = content_types($type_name);
  $widget_types = _content_widget_types();

  $return = array();
John Hwang's avatar
John Hwang committed
  foreach ($type['fields'] as $field) {
    $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();

    $module = $widget_types[$field['widget']['type']]['module'];
    $function = $module .'_widget';
    if (function_exists($function)) {
      $result = $function($op, $node, $field, $node_field);
      if (is_array($result) && $op == 'form') {
        $result[$field['field_name']]['#weight'] = $field['widget']['weight'];
      }
      if (is_array($result)) {
        $return = array_merge($return, $result);
      }
      else if (isset($result)) {
        $return[] = $result;
      }
    }
    // test for values in $node_field in case modules added items on insert
    if (is_object($node) && (isset($node->$field['field_name']) || count($node_field))) {
      $node->$field['field_name'] = $node_field;
    }
John VanDyk's avatar
John VanDyk committed
/**
 * Return a list of all content types.
John Hwang's avatar
John Hwang committed
 * @param $content_type_name
 *   If set, return information on just this type.
John VanDyk's avatar
John VanDyk committed
 */
John Hwang's avatar
John Hwang committed
function content_types($content_type_name = NULL) {
  $info = _content_type_info();
  if (isset($content_type_name)) {
    if (isset($info['content types'][$content_type_name])) {
      return $info['content types'][$content_type_name];
John VanDyk's avatar
John VanDyk committed
    }
    else {
John Hwang's avatar
John Hwang committed
      return NULL;
John VanDyk's avatar
John VanDyk committed
    }
  }
John Hwang's avatar
John Hwang committed
  return $info['content types'];
John VanDyk's avatar
John VanDyk committed
}

/**
 * Return a list of all fields.
John Hwang's avatar
John Hwang committed
 * @param $field_name
 *   If set, return information on just this field.
 * @param $content_type_name
 *   If set, return information of the field within the context of this content
 *   type.
John VanDyk's avatar
John VanDyk committed
 */
John Hwang's avatar
John Hwang committed
function content_fields($field_name = NULL, $content_type_name = NULL) {
  $info = _content_type_info();
  if (isset($field_name)) {
    if (isset($info['fields'][$field_name])) {
      if (isset($content_type_name)) {
        if (isset($info['content types'][$content_type_name]['fields'][$field_name])) {
          return $info['content types'][$content_type_name]['fields'][$field_name];
        }
        else {
          return NULL;
        }
      }
      else {
        return $info['fields'][$field_name];
      }
    }
    else {
      return NULL;
John VanDyk's avatar
John VanDyk committed
    }
  }
John Hwang's avatar
John Hwang committed
  return $info['fields'];
John VanDyk's avatar
John VanDyk committed
}

/**
 * Return a list of field types.
 */
John Hwang's avatar
John Hwang committed
function _content_field_types() {
  $info = _content_type_info();
  return $info['field types'];
John VanDyk's avatar
John VanDyk committed
}
/**
 * Return a list of widget types.
John Hwang's avatar
John Hwang committed
 */
function _content_widget_types() {
  $info = _content_type_info();
  return $info['widget types'];
}

/**
 * Collate all information on content types, fields, and related structures.
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 *
 * @param $reset
 *   If TRUE, clear the cache and fetch the information from the database again.
John Hwang's avatar
John Hwang committed
function _content_type_info($reset = FALSE) {
  static $info;

  if ($reset || !isset($info)) {
    if ($cached = cache_get('content_type_info')) {
      $info = unserialize($cached->data);
    }
    else {
      $info = array(
        'field types' => array(),
        'widget types' => array(),
        'fields' => array(),
        'content types' => array(),
      );

      foreach (module_list() as $module) {
        $module_field_types = module_invoke($module, 'field_info');
        if ($module_field_types) {
          foreach ($module_field_types as $name => $field_info) {
John Hwang's avatar
John Hwang committed
            $info['field types'][$name] = $field_info;
            $info['field types'][$name]['module'] = $module;
            $info['field types'][$name]['formatters'] = array();
        $module_widgets = module_invoke($module, 'widget_info');
        if ($module_widgets) {
          foreach ($module_widgets as $name => $widget_info) {
John Hwang's avatar
John Hwang committed
            $info['widget types'][$name] = $widget_info;
            $info['widget types'][$name]['module'] = $module;
          }
John Hwang's avatar
John Hwang committed

      foreach (module_list() as $module) {
        $module_formatters = module_invoke($module, 'field_formatter_info');
        if ($module_formatters) {
          foreach ($module_formatters as $name => $formatter_info) {
            foreach ($formatter_info['field types'] as $field_type) {
              $info['field types'][$field_type]['formatters'][$name] = $formatter_info;
              $info['field types'][$field_type]['formatters'][$name]['module'] = $module;
            }
          }
        }
      }

John Hwang's avatar
John Hwang committed
      $field_result = db_query('SELECT * FROM {node_field} nf');
      while ($field = db_fetch_array($field_result)) {
        $global_settings = $field['global_settings'] ? unserialize($field['global_settings']) : array();
        unset($field['global_settings']);
        // Preventative error handling for PHP5 if field nodule hasn't created an arrray.
        if (is_array($global_settings)) {
          $field = array_merge($field, $global_settings);
        }
John Hwang's avatar
John Hwang committed
        $instance_info = db_fetch_array(db_query("SELECT type_name, label FROM {node_field_instance} WHERE field_name = '%s'", $field['field_name']));
        $field['widget']['label'] = $instance_info['label'];
        $field['type_name'] = $instance_info['type_name'];
        $info['fields'][$field['field_name']] = $field;
      }

      $type_result = db_query('SELECT * FROM {node_type_content} nt ORDER BY nt.type_name ASC');
John Hwang's avatar
John Hwang committed
      while ($type = db_fetch_array($type_result)) {
        $type['fields'] = array();
        $field_result = db_query("SELECT nfi.field_name, nfi.weight, nfi.label, nfi.widget_type, nfi.widget_settings, nfi.display_settings, nfi.description FROM {node_field_instance} nfi WHERE nfi.type_name = '%s' ORDER BY nfi.weight ASC, nfi.label ASC", $type['type_name']);
John Hwang's avatar
John Hwang committed
        while ($field = db_fetch_array($field_result)) {
          // Overwrite global field information with specific information
          $field = array_merge($info['fields'][$field['field_name']], $field);

          $widget_settings = $field['widget_settings'] ? unserialize($field['widget_settings']) : array();
          unset($field['widget_settings']);
          $field['widget'] = $widget_settings;
          $field['widget']['type'] = $field['widget_type'];
          unset($field['widget_type']);
          $field['widget']['weight'] = $field['weight'];
          unset($field['weight']);
          $field['widget']['label'] = $field['label'];
          unset($field['label']);
          $field['widget']['description'] = $field['description'];
          unset($field['description']);
          $field['type_name'] = $type['type_name'];
          $field['display_settings'] = $field['display_settings'] ? unserialize($field['display_settings']) : array();
John Hwang's avatar
John Hwang committed
          $type['fields'][$field['field_name']] = $field;
        }

        $info['content types'][$type['type_name']] = $type;
      }
John Hwang's avatar
John Hwang committed
      cache_set('content_type_info', serialize($info), CACHE_PERMANENT);
John Hwang's avatar
John Hwang committed
  return $info;
Jonathan Chaffer's avatar
Jonathan Chaffer committed
 * Clear the cache of content_types; called in several places when content
 * information is changed.
 */
function content_clear_type_cache() {
John Hwang's avatar
John Hwang committed
  cache_clear_all('content_type_info');
John Hwang's avatar
John Hwang committed
  _content_type_info(TRUE);
  if (module_exist('views')) {
    views_invalidate_cache();
  }
}
/**
 * Retrieve the database storage location(s) for a field.
 *
 * @param $field
 *   The field whose database information is requested.
 * @return
 *   An array with the keys:
 *     "table": The name of the database table where the field data is stored.
 *     "columns": An array of columns stored for this field. Each is a collection
 *       of information returned from hook_field_settings('database columns'),
 *       with the addition of a "column" attribute which holds the name of the
 *       database column that stores the data.
 */
function content_database_info($field) {
  $field_types = _content_field_types();
  $module = $field_types[$field['type']]['module'];
  $columns = module_invoke($module, 'field_settings', 'database columns', $field);
  if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) {
    $db_info['table'] = 'node_data_'. $field['field_name'];
  }
  else {
    $db_info['table'] = 'node_'. $field['type_name'];

  if (is_array($columns) && count($columns)) {
    $db_info['columns'] = $columns;
    foreach ($columns as $column_name => $attributes) {
      $db_info['columns'][$column_name]['column'] = $field['field_name'] .'_'. $column_name;
/**
 * Manipulate a 2D array to reverse rows and columns.
 *
 * The default data storage for fields is delta first, column names second.
 * This is sometimes inconvenient for field modules, so this function can be
 * used to present the data in an alternate format.
 *
 * @param $array
 *   The array to be transposed. It must be at least two-dimensional, and
 *   the subarrays must all have the same keys or behavior is undefined.
 *
 * @return
 *   The transposed array.
 */
function content_transpose_array_rows_cols($array) {
  $result = array();
  if (is_array($array)) {
    foreach ($array as $key1 => $value1) {
      if (is_array($value1)) {
        foreach ($value1 as $key2 => $value2) {
          if (!isset($result[$key2])) {
            $result[$key2] = array();
          }
          $result[$key2][$key1] = $value2;
        }
/**
 * Format a field item for display.
 *
 * @param $field
 *   Either a field array or the name of the field.
 * @param $item
 *   The field item to be formatted (such as $node->field_foo[0]).
 * @param $formatter
 *   The name of the formatter to use.
 * @param $node
 *   Optionally, the containing node object for context purposes.
 *
 * @return
 *   A string containing the contents of the field item sanitized for display.
 *   It will have been passed through the necessary check_plain() or check_markup()
 *   functions as necessary.
 */
function content_format($field, $item, $formatter = 'default', $node = NULL) {
  if (!is_array($field)) {
    $field = content_fields($field);
  }
  $field_types = _content_field_types();
  $formatters = $field_types[$field['type']]['formatters'];

  if (!isset($formatters[$formatter])) {
    $formatter = 'default';
  }
  return module_invoke($formatters[$formatter]['module'], 'field_formatter', $field, $item, $formatter, $node);
}

/**
 * Format an individual field for display.
 *
 * @param $node
 *   The node being displayed (provided for context).
 * @param $field
 *   The field that is to be displayed (for information about the label, field
 *   type, and so forth).
 * @param $items
 *   The actual items to theme. This will be a linear array, each element of
 *   which has a "view" property which contains the filtered, formatted contents
 *   of the item.
 * @param $teaser
 *   Whether the node is being displayed as a teaser or full version.
 * @param $page
 *   Whether the node is being displayed as a full web page.
 *
 * @return
 *   An HTML string containing the fully themed field.
 */
function theme_field(&$node, &$field, &$items, $teaser, $page) {
  $label = isset($field['display_settings']['label']['format']) ? $field['display_settings']['label']['format'] : 'above';

    if (!empty($item['view']) || $item['view'] === "0") {
      $items_output .= '<div class="field-item">';
      if ($label == 'inline') {
        $items_output .= '<div class="field-label-inline'. ($delta ? '' : '-first') .'">';
        $items_output .= $field['widget']['label'] .':</div> ' ;
      }
      $items_output .= $item['view'] .'</div>';
    $output .= '<div class="field field-type-'. strtr($field['type'], '_', '-') .' field-'. strtr($field['field_name'], '_', '-') .'">';
    if ($label == 'above') {
      $output .= '<div class="field-label">'. $field['widget']['label'] .':</div>';