'. t('The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default "Title" and "Body" may be added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)', array('@content-types' => url('admin/content/types'), '@node-help' => url('admin/help/node'))) .'

'; $output .= '

'. t('When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a "person" may have multiple e-mail addresses) or a single value (i.e., an "employee" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules.') .'

'; $output .= '

'. t('Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A default installation of CCK includes:', array('@modules' => url('admin/build/modules'))) .'

'; $output .= ''; $output .= '

'. t('For more information, see the online handbook entry for CCK or the CCK project page.', array('@handbook-cck' => 'http://drupal.org/handbook/modules/cck', '@project-cck' => 'http://drupal.org/project/cck')) .'

'; return $output; } if (preg_match('!^admin/content/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."); } } /** * Implementation of hook_devel_caches(). * Include {cache_content} in the list of tables cleared by devel's 'empty cache' */ function content_devel_caches() { return array('cache_content'); } /** * 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_exists('views')) { include_once('./'. drupal_get_path('module', 'content') .'/content_views.inc'); } if (module_exists('panels')) { include_once('./'. drupal_get_path('module', 'content') .'/content_panels.inc'); } // according to http://drupal.org/node/60526, this should not go in hook_menu if (module_exists('pathauto')) { include_once('./'. drupal_get_path('module', 'content') .'/content_pathauto.inc'); } } } /** * Implementation of hook_perm(). */ function content_perm() { return array('Use PHP input for field settings (dangerous - grant with care)'); } /** * 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'); } // Unconditionally include css exactly once per page. drupal_add_css(drupal_get_path('module', 'content') .'/content.css'); } $items = array(); $access = user_access('administer content types'); if ($may_cache) { $items[] = array( 'path' => 'admin/content/types/fields', 'title' => t('Fields'), 'callback' => '_content_admin_type_fields', 'access' => $access, 'type' => MENU_LOCAL_TASK, ); } else { if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') { $content_type = content_types(arg(3)); $type = node_get_types('types', $content_type['type']); if (!empty($type) && arg(3) && arg(3) == $content_type['url_str']) { $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/edit', 'title' => t('Edit'), 'callback' => 'drupal_get_form', 'callback arguments' => array('node_type_form', $type), 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields', 'title' => t('Manage fields'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('content_admin_field_overview_form', $content_type['type']), 'type' => MENU_LOCAL_TASK, 'weight' => 0, ); $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/display', 'title' => t('Display fields'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('content_admin_display_overview_form', $content_type['type']), 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/add_field', 'title' => t('Add field'), 'callback' => '_content_admin_field_add', 'access' => $access, 'callback arguments' => array($content_type['type']), 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); if (arg(4) == 'fields' && arg(5) && isset($content_type['fields'][arg(5)])) { $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields/'. arg(5), 'title' => t($content_type['fields'][arg(5)]['widget']['label']), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('_content_admin_field', $content_type['type'], arg(5)), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields/'. arg(5) .'/remove', 'title' => t('Remove field'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('_content_admin_field_remove', $content_type['type'], arg(5)), 'type' => MENU_CALLBACK, ); } } } } return $items; } /** * Load data for a node type's fields. * * 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. */ function content_load(&$node) { $cid = 'content:'. $node->nid .':'. $node->vid; if ($cached = cache_get($cid, 'cache_content')) { $extra = unserialize($cached->data); foreach ($extra as $key => $value) { $node->$key = $value; } return $extra; } else { $default_additions = _content_field_invoke_default('load', $node); if ($default_additions) { foreach ($default_additions as $key => $value) { $node->$key = $value; } } $additions = _content_field_invoke('load', $node); if ($additions) { foreach ($additions as $key => $value) { $default_additions[$key] = $value; } } cache_set($cid, 'cache_content', serialize($default_additions)); return $default_additions; } } /** * Create fields' form for a content type. * * Each field defines its own component of the content entry form, via its * chosen widget. */ function content_form(&$node) { $form = array(); $type = content_types($node->type); // Set form parameters so we can accept file uploads. if (count($type['fields'])) { $form['#attributes'] = array("enctype" => "multipart/form-data"); } _content_widget_invoke('prepare form values', $node); $form = array_merge($form, _content_widget_invoke('form', $node)); return $form; } /** * Validate form callback to handle node type fields. * * Both widgets and fields have a chance to raise error flags when a node is * being validated. */ function content_validate(&$node) { _content_widget_invoke('validate', $node); _content_widget_invoke('process form values', $node); _content_field_invoke('validate', $node); _content_field_invoke_default('validate', $node); } /** * Submit form callback for node type fields. * * 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. */ function content_submit(&$node) { _content_widget_invoke('submit', $node); _content_widget_invoke('process form values', $node); _content_field_invoke('submit', $node); _content_field_invoke_default('submit', $node); } /** * Insert node type fields. */ function content_insert(&$node) { _content_field_invoke('insert', $node); _content_field_invoke_default('insert', $node); } /** * Update node type fields. */ function content_update(&$node) { _content_field_invoke('update', $node); _content_field_invoke_default('update', $node); cache_clear_all('content:'. $node->nid .':'. $node->vid, 'cache_content'); } /** * Delete node type fields. */ function content_delete(&$node) { $type = content_types($node->type); if (!empty($type['fields'])) { _content_field_invoke('delete', $node); _content_field_invoke_default('delete', $node); } $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); if (db_table_exists($table)) { db_query('DELETE FROM {'. $table .'} WHERE nid = %d', $node->nid); } cache_clear_all('content:'. $node->nid .':', 'cache_content', TRUE); } /** * delete node type fields for a revision. */ function content_delete_revision(&$node) { $type = content_types($node->type); if (!empty($type['fields'])) { _content_field_invoke('delete revision', $node); _content_field_invoke_default('delete revision', $node); } $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); if (db_table_exists($table)) { db_query('DELETE FROM {'. $table .'} WHERE vid = %d', $node->vid); } cache_clear_all('content:'. $node->nid .':'. $node->vid, 'cache_content'); } /** * Generate field render arrays. */ function content_view(&$node, $teaser = FALSE, $page = FALSE) { if ($node->in_preview) { _content_widget_invoke('process form values', $node); } $content = _content_field_view($node, $teaser, $page); $node->content = array_merge((array) $node->content, $content); } /** * Implementation of hook_nodeapi(). * * When a revision is deleted, make sure the appropriate cache item is cleared. * @todo: deprecate op==validate & op==submit in favor of form callbacks. */ function content_nodeapi(&$node, $op, $teaser, $page) { switch ($op) { case 'load': return content_load($node); case 'validate': content_validate($node); break; case 'submit': content_submit($node); break; case 'insert': if ($node->devel_generate) { include_once('./'. drupal_get_path('module', 'content') .'/content.devel.inc'); content_generate_fields($node); } content_insert($node); break; case 'update': content_update($node); break; case 'delete': content_delete($node); break; case 'delete revision': content_delete_revision($node); break; case 'view': content_view($node, $teaser, $page); break; } } /** * Implementation of hook_form_alter(). */ function content_form_alter($form_id, &$form) { if (isset($form['type'])) { $node = $form['#node']; if ($form['type']['#value'] .'_node_form' == $form_id) { $form = array_merge($form, content_form($node)); } } } /** * Make sure that CCK content type info is synched with node type data * any time the content module is enabled. */ function content_enable() { include_once('./'. drupal_get_path('module', 'content') .'/content_admin.inc'); include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc'); content_types_rebuild(); } /** * 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; } $query = 'SELECT '. implode(', ', $column_names) .' FROM {'. $db_info['table'] .'} WHERE vid = %d'; if ($field['multiple']) { $result = db_query($query .' ORDER BY delta', $node->vid); } else { $result = db_query_range($query, $node->vid, 0, 1); } $additions = array(); while ($value = db_fetch_array($result)) { $additions[$field['field_name']][] = $value; } return $additions; case 'insert': foreach ($node_field as $delta => $item) { $data = array(); $column_names = array(); $column_placeholders = array(); $column_assignments = 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 'tinyint': 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[] = $item[$column]; } $data[] = $node->vid; $data[] = $node->nid; if ($field['multiple']) { $data[] = $delta; } if ($field['multiple']) { db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data); } else { 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 'update': 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) { $data = array(); $column_names = array(); $column_placeholders = array(); $column_assignments = 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 'tinyint': 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[] = $item[$column]; } $data[] = $node->vid; $data[] = $node->nid; if ($field['multiple']) { $data[] = $delta; } if ($field['multiple']) { db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data); } else { 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); return; case 'delete revision': db_query('DELETE FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid); return; } } /** * Invoke a field hook. * * For each operation, both this function and _content_field_invoke_default() are * called so that the default database handling can occur. */ function _content_field_invoke($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(); if (count($type['fields'])) { foreach ($type['fields'] as $field) { $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array(); $module = $field_types[$field['type']]['module']; $function = $module .'_field'; if (function_exists($function)) { $result = $function($op, $node, $field, $node_field, $teaser, $page); 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 (isset($node->$field['field_name']) || count($node_field)) { $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); $return = array(); if (count($type['fields'])) { 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; } } } 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') { if (content_handle('field', 'view', $field) == CONTENT_CALLBACK_CUSTOM) { $module = $field_types[$field['type']]['module']; $function = $module .'_field'; if (function_exists($function)) { $value = $function('view', $node, $field, $node_field, $teaser, $page); } } else { 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); } } $return[$field['field_name']] = array( '#weight' => $field['widget']['weight'], '#value' => $value, '#access' => $formatter != 'hidden', ); // 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; } } } return $return; } /** * Invoke a widget hook. */ function _content_widget_invoke($op, &$node) { $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); $type = content_types($type_name); $widget_types = _content_widget_types(); $return = array(); if (count($type['fields'])) { 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)) { // If we're building a node creation form, pre-fill with default values if ($op == 'prepare form values' && empty($node->nid)) { $node_field = array_merge($node_field, content_default_value($node, $field, $node_field)); } $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 if (is_object($node) && (isset($node->$field['field_name']) || count($node_field))) { $node->$field['field_name'] = $node_field; } } } return $return; } /** * Return a list of all content types. * * @param $content_type_name * If set, return information on just this type. */ function content_types($type_name = NULL) { // handle type name with either an underscore or a dash $type_name = !empty($type_name) ? str_replace('-', '_', $type_name) : NULL; $info = _content_type_info(); if (isset($type_name)) { if (isset($info['content types'][$type_name])) { return $info['content types'][$type_name]; } else { return NULL; } } return $info['content types']; } /** * Return a list of all fields. * * @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. */ function content_fields($field_name = NULL, $content_type_name = NULL) { $info = _content_type_info(); // If there is no field name specified, return the entire fields array. if (!isset($field_name)) { return $info['fields']; } elseif (!isset($info['fields'][$field_name])) { return NULL; } elseif (!isset($content_type_name)) { return $info['fields'][$field_name]; } elseif (isset($info['content types'][$content_type_name]['fields'][$field_name])) { return $info['content types'][$content_type_name]['fields'][$field_name]; } // If all else fails, return NULL. return NULL; } /** * Return a list of field types. */ function _content_field_types() { $info = _content_type_info(); return $info['field types']; } /** * Return a list of widget types. */ function _content_widget_types() { $info = _content_type_info(); return $info['widget types']; } /** * Collate all information on content types, fields, and related structures. * * @param $reset * If TRUE, clear the cache and fetch the information from the database again. */ function _content_type_info($reset = FALSE) { static $info; if ($reset || !isset($info)) { if ($cached = cache_get('content_type_info', 'cache_content')) { $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) { // Truncate names to match the value that is stored in the database. $db_name = substr($name, 0, 32); $info['field types'][$db_name] = $field_info; $info['field types'][$db_name]['module'] = $module; $info['field types'][$db_name]['formatters'] = array(); } } $module_widgets = module_invoke($module, 'widget_info'); if ($module_widgets) { foreach ($module_widgets as $name => $widget_info) { // Truncate names to match the value that is stored in the database. $db_name = substr($name, 0, 32); $info['widget types'][$db_name] = $widget_info; $info['widget types'][$db_name]['module'] = $module; foreach ($widget_info['field types'] as $delta => $type) { $info['widget types'][$db_name][$delta] = substr($type, 0, 32); } } } } 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) { // Truncate names to match the value that is stored in the database. $db_name = substr($field_type, 0, 32); $info['field types'][$db_name]['formatters'][$name] = $formatter_info; $info['field types'][$db_name]['formatters'][$name]['module'] = $module; } } } } $field_result = db_query('SELECT * FROM {node_field}'); 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); } $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} ORDER BY type ASC'); while ($type = db_fetch_array($type_result)) { $type['url_str'] = str_replace('_', '-', $type['type']); $type['fields'] = array(); $field_result = db_query("SELECT * FROM {node_field_instance} nfi WHERE nfi.type_name = '%s' ORDER BY nfi.weight ASC, nfi.label ASC", $type['type']); 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']; $field['display_settings'] = $field['display_settings'] ? unserialize($field['display_settings']) : array(); $type['fields'][$field['field_name']] = $field; } $info['content types'][$type['type']] = $type; } cache_set('content_type_info', 'cache_content', serialize($info)); } } return $info; } /** * Implementation of hook_node_type() * React to change in node types */ function content_node_type($op, $info) { switch ($op) { case 'insert': include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc'); content_type_create($info); break; case 'update': include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc'); content_type_update($info); break; case 'delete': include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc'); content_type_delete($info); break; } } /** * Clear the cache of content_types; called in several places when content * information is changed. */ function content_clear_type_cache() { // If type type cache needs to be cleared because type information has // changed, the cached node values also need to be emptied so they // don't carry stale values. // TODO see if there is a less destructive way to do this. cache_clear_all('*', 'cache_content', TRUE); _content_type_info(TRUE); if (module_exists('views')) { // Needed because this can be called from .install files include_once('./'. drupal_get_path('module', 'views') .'/views.module'); 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); $db_info = array(); if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { $db_info['table'] = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD); } else { $db_info['table'] = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); } 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; } } else { $db_info['columns'] = array(); } return $db_info; } /** * 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; } } } } return $result; } /** * 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($formatter, $formatters, $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_display = isset($field['display_settings']['label']['format']) ? $field['display_settings']['label']['format'] : 'above'; $label = t($field['widget']['label']); $items_output = ''; foreach ($items as $delta => $item) { if (!empty($item['view']) || $item['view'] === "0") { $items_output .= '
'; if ($label_display == 'inline') { $items_output .= '
'; $items_output .= $label .': 
' ; } $items_output .= $item['view'] .'
'; } } $output = ''; if (!empty($items_output)) { $output .= '
'; if ($label_display == 'above') { $output .= '
'. $label .': 
'; } $output .= '
'. $items_output .'
'; $output .= '
'; } return $output; } /** * Generate a table name for a field or a content type. * * @param $name * The name of the content type or content field * @param $entity * 'field' or 'type' * * @return * A string containing the generated name for the database table */ function _content_tablename($name, $entity, $version = NULL) { if (is_null($version)) { $version = variable_get('content_schema_version', 0); } if ($version < 1003) { $version = 0; } else { $version = 1003; } $name = str_replace('-', '_', $name); switch ("$version-$entity") { case '0-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE : return "node_$name"; case '0-'. CONTENT_DB_STORAGE_PER_FIELD : return "node_data_$name"; case '1003-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE : return "content_type_$name"; case '1003-'. CONTENT_DB_STORAGE_PER_FIELD : return "content_$name"; } } /** * Helper function for determining the behaviour of a field or a widget * with respect to a given operation * (currently only used for 'view' field op and 'default value' widegt op) * * @param $entity * 'field' or 'widget' * * @param $op * the name of the operation ('view', 'validate'...) * * @param $field * The field array, including widget info. * @return * CONTENT_CALLBACK_NONE - do nothing for this operation * CONTENT_CALLBACK_CUSTOM - use the module's callback function. * CONTENT_CALLBACK_DEFAULT - use content module default behaviour * */ function content_handle($entity, $op, $field) { $entity_types = ($entity == 'field') ? _content_field_types() : _content_widget_types(); $entity_type = ($entity == 'field') ? $field['type'] : $field['widget']['type']; $module = $entity_types[$entity_type]['module']; if ($op == 'default value' && $module == 'computed_field') { $callback_value = CONTENT_CALLBACK_NONE; } else { $callbacks = module_invoke($module, "{$entity}_settings", 'callbacks', $field); $callback_value = (isset($callbacks[$op])) ? $callbacks[$op] : CONTENT_CALLBACK_DEFAULT; } return $callback_value; } /** * Helper function to return the correct default value for a field. * * @param $node * The node. * * @param $field * The field array. * * @param $items * The value of the field in the node. * * @return * The default value for that field. */ function content_default_value($node, $field, $items) { $widget_types = _content_widget_types(); $module = $widget_types[$field['widget']['type']]['module']; $default_value = array(); if (content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_CUSTOM) { $function = $module .'_widget'; $default_value = $function('default value', $node, $field, $items); } elseif (!empty($field['widget']['default_value_php'])) { ob_start(); $result = eval($field['widget']['default_value_php']); ob_end_clean(); if (is_array($result)) { $default_value = $result; } } elseif (!empty($field['widget']['default_value'])) { $default_value = $field['widget']['default_value']; } return (array) $default_value; }