Newer
Older
Karen Stevenson
committed
* Allows administrators to associate custom fields to content types.
define('CONTENT_DB_STORAGE_PER_FIELD', 0);
define('CONTENT_DB_STORAGE_PER_CONTENT_TYPE', 1);
define('CONTENT_CALLBACK_NONE', 0x0001);
define('CONTENT_CALLBACK_DEFAULT', 0x0002);
define('CONTENT_CALLBACK_CUSTOM', 0x0004);
Yves Chedemois
committed
function content_help($section) {
Yves Chedemois
committed
switch ($section) {
case 'admin/help#content':
$output = '<p>'. 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 <a href="@content-types">content types administration page</a>. (See the <a href="@node-help">node module help page</a> for more information about content types.)', array('@content-types' => url('admin/content/types'), '@node-help' => url('admin/help/node'))) .'</p>';
$output .= '<p>'. 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.') .'</p>';
$output .= '<p>'. t('Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The <a href="@modules">modules page</a> allows you to enable or disable CCK components. A default installation of CCK includes:', array('@modules' => url('admin/build/modules'))) .'</p>';
$output .= '<ul>';
$output .= '<li>'. t('<em>number</em>, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available.') .'</li>';
$output .= '<li>'. t("<em>text</em>, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values.") .'</li>';
$output .= '<li>'. t('<em>nodereference</em>, which creates custom references between Drupal nodes. By adding a <em>nodereference</em> field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple "employee" nodes may contain a <em>nodereference</em> field linking to an "employer" node).') .'</li>';
$output .= '<li>'. t('<em>userreference</em>, which creates custom references to your sites\' user accounts. By adding a <em>userreference</em> field, you can create complex relationships between your site\'s users and posts. To track user involvement in a post beyond Drupal\'s standard <em>Authored by</em> field, for instance, add a <em>userreference</em> field named "Edited by" to a content type to store a link to an editor\'s user account page.') .'</li>';
$output .= '<li>'. t('<em>fieldgroup</em>, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module.') .'</li>';
$output .= '</ul>';
$output .= '<p>'. t('For more information, see the online handbook entry for <a href="@handbook-cck">CCK</a> or the <a href="@project-cck">CCK project page</a>.', array('@handbook-cck' => 'http://drupal.org/handbook/modules/cck', '@project-cck' => 'http://drupal.org/project/cck')) .'</p>';
return $output;
}
Yves Chedemois
committed
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');
}
Yves Chedemois
committed
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');
}
}
}
Yves Chedemois
committed
/**
* 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');
Jonathan Chaffer
committed
Karen Stevenson
committed
$items[] = array(
'path' => 'admin/content/types/fields',
'title' => t('Fields'),
Karen Stevenson
committed
'callback' => '_content_admin_type_fields',
'access' => $access,
'type' => MENU_LOCAL_TASK,
);
Karen Stevenson
committed
if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') {
Karen Stevenson
committed
$content_type = content_types(arg(3));
Karen Stevenson
committed
$type = node_get_types('types', $content_type['type']);
Karen Stevenson
committed
if (!empty($type) && arg(3) && arg(3) == $content_type['url_str']) {
Karen Stevenson
committed
'path' => 'admin/content/types/'. $content_type['url_str'] .'/edit',
'title' => t('Edit'),
Karen Stevenson
committed
'callback' => 'drupal_get_form',
'callback arguments' => array('node_type_form', $type),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[] = array(
Karen Stevenson
committed
'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields',
'title' => t('Manage fields'),
'callback' => 'drupal_get_form',
'callback arguments' => array('content_admin_field_overview_form', $content_type['type']),
Jonathan Chaffer
committed
'type' => MENU_LOCAL_TASK,
Yves Chedemois
committed
'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(
Karen Stevenson
committed
'path' => 'admin/content/types/'. $content_type['url_str'] .'/add_field',
'title' => t('Add field'),
'callback' => '_content_admin_field_add',
'access' => $access,
Karen Stevenson
committed
'callback arguments' => array($content_type['type']),
Jonathan Chaffer
committed
'type' => MENU_LOCAL_TASK,
Karen Stevenson
committed
if (arg(4) == 'fields' && arg(5) && isset($content_type['fields'][arg(5)])) {
Karen Stevenson
committed
'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields/'. arg(5),
'title' => t($content_type['fields'][arg(5)]['widget']['label']),
'callback' => 'drupal_get_form',
Karen Stevenson
committed
'callback arguments' => array('_content_admin_field', $content_type['type'], arg(5)),
'type' => MENU_CALLBACK,
);
$items[] = array(
Karen Stevenson
committed
'path' => 'admin/content/types/'. $content_type['url_str'] .'/fields/'. arg(5) .'/remove',
'title' => t('Remove field'),
Karen Stevenson
committed
'callback' => 'drupal_get_form',
Karen Stevenson
committed
'callback arguments' => array('_content_admin_field_remove', $content_type['type'], arg(5)),
'type' => MENU_CALLBACK,
);
Karen Stevenson
committed
* 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.
$cid = 'content:'. $node->nid .':'. $node->vid;
if ($cached = cache_get($cid, 'cache_content')) {
$default_additions = _content_field_invoke_default('load', $node);
if ($default_additions) {
foreach ($default_additions as $key => $value) {
$node->$key = $value;
}
}
if ($additions) {
foreach ($additions as $key => $value) {
$default_additions[$key] = $value;
}
}
cache_set($cid, 'cache_content', serialize($default_additions));
return $default_additions;
Karen Stevenson
committed
* Create fields' form for a content type.
*
* Each field defines its own component of the content entry form, via its
* chosen widget.
Karen Stevenson
committed
function content_form(&$node) {
Jonathan Chaffer
committed
$form = array();
Jonathan Chaffer
committed
$type = content_types($node->type);
Jonathan Chaffer
committed
// Set form parameters so we can accept file uploads.
Yves Chedemois
committed
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));
Jonathan Chaffer
committed
return $form;
Karen Stevenson
committed
* 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.
_content_widget_invoke('validate', $node);
_content_widget_invoke('process form values', $node);
Jonathan Chaffer
committed
_content_field_invoke('validate', $node);
_content_field_invoke_default('validate', $node);
Jonathan Chaffer
committed
/**
Karen Stevenson
committed
* 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.
Jonathan Chaffer
committed
*/
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);
Jonathan Chaffer
committed
}
Jonathan Chaffer
committed
/**
Karen Stevenson
committed
* Insert node type fields.
Jonathan Chaffer
committed
*/
function content_insert(&$node) {
_content_field_invoke('insert', $node);
_content_field_invoke_default('insert', $node);
Jonathan Chaffer
committed
}
/**
Karen Stevenson
committed
* Update node type fields.
Jonathan Chaffer
committed
*/
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');
Jonathan Chaffer
committed
}
/**
Karen Stevenson
committed
* Delete node type fields.
Jonathan Chaffer
committed
*/
function content_delete(&$node) {
Yves Chedemois
committed
$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);
Yves Chedemois
committed
if (db_table_exists($table)) {
db_query('DELETE FROM {'. $table .'} WHERE nid = %d', $node->nid);
Yves Chedemois
committed
}
cache_clear_all('content:'. $node->nid, 'cache_content', TRUE);
Karen Stevenson
committed
}
/**
* delete node type fields for a revision.
*/
function content_delete_revision(&$node) {
Yves Chedemois
committed
$type = content_types($node->type);
if (!empty($type['fields'])) {
Karen Stevenson
committed
_content_field_invoke('delete revision', $node);
_content_field_invoke_default('delete revision', $node);
}
$table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
Yves Chedemois
committed
if (db_table_exists($table)) {
db_query('DELETE FROM {'. $table .'} WHERE vid = %d', $node->vid);
Yves Chedemois
committed
}
cache_clear_all('content:'. $node->nid .':'. $node->vid, 'cache_content');
Jonathan Chaffer
committed
}
Karen Stevenson
committed
* Generate field render arrays.
*/
function content_view(&$node, $teaser = FALSE, $page = FALSE) {
Jonathan Chaffer
committed
if ($node->in_preview) {
_content_widget_invoke('process form values', $node);
}
Yves Chedemois
committed
$content = _content_field_view($node, $teaser, $page);
Yves Chedemois
committed
$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.
Karen Stevenson
committed
* @todo: deprecate op==validate & op==submit in favor of form callbacks.
*/
function content_nodeapi(&$node, $op, $teaser, $page) {
switch ($op) {
Karen Stevenson
committed
case 'load':
return content_load($node);
case 'validate':
content_validate($node);
Karen Stevenson
committed
case 'submit':
content_submit($node);
break;
case 'insert':
Karen Stevenson
committed
if ($node->devel_generate) {
include_once('./'. drupal_get_path('module', 'content') .'/content.devel.inc');
content_generate_fields($node);
}
Karen Stevenson
committed
content_insert($node);
break;
case 'update':
content_update($node);
break;
case 'delete':
content_delete($node);
break;
case 'delete revision':
Karen Stevenson
committed
content_delete_revision($node);
break;
case 'view':
content_view($node, $teaser, $page);
Karen Stevenson
committed
}
}
Yves Chedemois
committed
/**
* Implementation of hook_form_alter().
*/
Karen Stevenson
committed
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();
Jonathan Chaffer
committed
/**
* 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) {
Jonathan Chaffer
committed
$db_info = content_database_info($field);
Jonathan Chaffer
committed
switch ($op) {
case 'load':
$column_names = array();
Jonathan Chaffer
committed
foreach ($db_info['columns'] as $column => $attributes) {
Jonathan Chaffer
committed
$column_names[] = $attributes['column'] .' AS '. $column;
Jonathan Chaffer
committed
}
$query = 'SELECT '. implode(', ', $column_names) .' FROM {'. $db_info['table'] .'} WHERE vid = %d';
Jonathan Chaffer
committed
if ($field['multiple']) {
Yves Chedemois
committed
$result = db_query($query .' ORDER BY delta', $node->vid);
Jonathan Chaffer
committed
}
else {
$result = db_query_range($query, $node->vid, 0, 1);
}
$additions = array();
while ($value = db_fetch_array($result)) {
$additions[$field['field_name']][] = $value;
Jonathan Chaffer
committed
}
return $additions;
case 'insert':
Jonathan Chaffer
committed
foreach ($node_field as $delta => $item) {
Jonathan Chaffer
committed
$data = array();
Jonathan Chaffer
committed
$column_names = array();
$column_placeholders = array();
Jonathan Chaffer
committed
$column_assignments = array();
Jonathan Chaffer
committed
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'";
Jonathan Chaffer
committed
}
Jonathan Chaffer
committed
$data[] = $item[$column];
}
Jonathan Chaffer
committed
$data[] = $node->vid;
$data[] = $node->nid;
if ($field['multiple']) {
$data[] = $delta;
}
Jonathan Chaffer
committed
if ($field['multiple']) {
Jonathan Chaffer
committed
db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data);
Jonathan Chaffer
committed
}
else {
Jonathan Chaffer
committed
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);
}
Jonathan Chaffer
committed
}
}
return;
case 'update':
Jonathan Chaffer
committed
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);
}
Jonathan Chaffer
committed
Jonathan Chaffer
committed
foreach ($node_field as $delta => $item) {
Jonathan Chaffer
committed
$data = array();
Jonathan Chaffer
committed
$column_names = array();
$column_placeholders = array();
Jonathan Chaffer
committed
$column_assignments = array();
Jonathan Chaffer
committed
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 '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'";
Jonathan Chaffer
committed
}
Jonathan Chaffer
committed
$data[] = $item[$column];
}
Jonathan Chaffer
committed
$data[] = $node->vid;
$data[] = $node->nid;
if ($field['multiple']) {
$data[] = $delta;
}
Jonathan Chaffer
committed
if ($field['multiple']) {
Jonathan Chaffer
committed
db_query('INSERT INTO {'. $db_info['table'] .'} ('. implode(', ', $column_names) .', vid, nid, delta) VALUES ('. implode(', ', $column_placeholders) .', %d, %d, %d)', $data);
Jonathan Chaffer
committed
}
else {
Jonathan Chaffer
committed
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);
}
Jonathan Chaffer
committed
}
}
return;
case 'delete':
// Delete using nid rather than vid to purge all revisions.
Jonathan Chaffer
committed
db_query('DELETE FROM {'. $db_info['table'] .'} WHERE nid = %d', $node->nid);
Jonathan Chaffer
committed
return;
Jonathan Chaffer
committed
case 'delete revision':
db_query('DELETE FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid);
return;
Jonathan Chaffer
committed
}
}
Jonathan Chaffer
committed
* Invoke a field hook.
Jonathan Chaffer
committed
*
* 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);
Jonathan Chaffer
committed
$type = content_types($type_name);
$field_types = _content_field_types();
$return = array();
Karen Stevenson
committed
if (count($type['fields'])) {
foreach ($type['fields'] as $field) {
$node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
Karen Stevenson
committed
$module = $field_types[$field['type']]['module'];
$function = $module .'_field';
if (function_exists($function)) {
$result = $function($op, $node, $field, $node_field, $teaser, $page);
Yves Chedemois
committed
if (is_array($result)) {
Karen Stevenson
committed
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
Karen Stevenson
committed
// 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);
Jonathan Chaffer
committed
$type = content_types($type_name);
$return = array();
Karen Stevenson
committed
if (count($type['fields'])) {
foreach ($type['fields'] as $field) {
$node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
Karen Stevenson
committed
$db_info = content_database_info($field);
if (count($db_info['columns'])) {
$result = content_field($op, $node, $field, $node_field, $teaser, $page);
Yves Chedemois
committed
if (is_array($result)) {
Karen Stevenson
committed
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
Jonathan Chaffer
committed
}
Karen Stevenson
committed
if (isset($node->$field['field_name'])) {
$node->$field['field_name'] = $node_field;
Jonathan Chaffer
committed
}
Jonathan Chaffer
committed
}
Yves Chedemois
committed
/**
* Format field output based on display settings.
*/
function _content_field_view(&$node, $teaser = NULL, $page = NULL) {
Yves Chedemois
committed
$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);
}
Yves Chedemois
committed
}
else {
Yves Chedemois
committed
foreach ($node_field as $delta => $item) {
$node_field[$delta]['view'] = content_format($field, $item, $formatter, $node);
Yves Chedemois
committed
}
$value = theme('field', $node, $field, $node_field, $teaser, $page);
Yves Chedemois
committed
}
}
$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;
}
Yves Chedemois
committed
}
}
return $return;
}
/**
*/
function _content_widget_invoke($op, &$node) {
$type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
Jonathan Chaffer
committed
$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)) {
Karen Stevenson
committed
// 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;
}
* @param $content_type_name
Jonathan Chaffer
committed
* If set, return information on just this type.
*/
Karen Stevenson
committed
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;
Jonathan Chaffer
committed
$info = _content_type_info();
Karen Stevenson
committed
if (isset($type_name)) {
if (isset($info['content types'][$type_name])) {
return $info['content types'][$type_name];
Jonathan Chaffer
committed
}
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.
Jonathan Chaffer
committed
*/
function content_fields($field_name = NULL, $content_type_name = NULL) {
Jonathan Chaffer
committed
$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;
}
}
Jonathan Chaffer
committed
return $info['fields'];
}
/**
* 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.
Jonathan Chaffer
committed
function _content_type_info($reset = FALSE) {
static $info;
Jonathan Chaffer
committed
if ($reset || !isset($info)) {
if ($cached = cache_get('content_type_info', 'cache_content')) {
Jonathan Chaffer
committed
$info = unserialize($cached->data);
Jonathan Chaffer
committed
$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();
Jonathan Chaffer
committed
}
}
$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);
}
Jonathan Chaffer
committed
}
}
Karen Stevenson
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) {
// 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;
Jonathan Chaffer
committed
}
Yves Chedemois
committed
$field_result = db_query('SELECT * FROM {node_field}');
Jonathan Chaffer
committed
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);
}
Jonathan Chaffer
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;
}
Yves Chedemois
committed
$type_result = db_query('SELECT * FROM {node_type} ORDER BY type ASC');
Jonathan Chaffer
committed
while ($type = db_fetch_array($type_result)) {
Karen Stevenson
committed
$type['url_str'] = str_replace('_', '-', $type['type']);
Jonathan Chaffer
committed
$type['fields'] = array();
Yves Chedemois
committed
$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']);
Jonathan Chaffer
committed
// 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']);
Karen Stevenson
committed
$field['type_name'] = $type['type'];
Yves Chedemois
committed
$field['display_settings'] = $field['display_settings'] ? unserialize($field['display_settings']) : array();
Jonathan Chaffer
committed
$type['fields'][$field['field_name']] = $field;
Karen Stevenson
committed
$info['content types'][$type['type']] = $type;
cache_set('content_type_info', 'cache_content', serialize($info));
Jonathan Chaffer
committed
return $info;
}
Karen Stevenson
committed
/**
* Implementation of hook_node_type()
* React to change in node types
*/
function content_node_type($op, $info) {
switch ($op) {
include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc');
Karen Stevenson
committed
content_type_create($info);
break;
include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc');
Karen Stevenson
committed
content_type_update($info);
break;
include_once('./'. drupal_get_path('module', 'content') .'/content_crud.inc');
Karen Stevenson
committed
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() {
Karen Stevenson
committed
// 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);
Jonathan Chaffer
committed
Jonathan Chaffer
committed
_content_type_info(TRUE);
Jonathan Chaffer
committed
Jonathan Chaffer
committed
if (module_exists('views')) {
Karen Stevenson
committed
// Needed because this can be called from .install files
include_once('./'. drupal_get_path('module', 'views') .'/views.module');
views_invalidate_cache();
}
}
Jonathan Chaffer
committed
/**
* 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);
Jonathan Chaffer
committed
$db_info = array();
if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) {
Yves Chedemois
committed
$db_info['table'] = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD);
Yves Chedemois
committed
$db_info['table'] = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
Jonathan Chaffer
committed
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;
Jonathan Chaffer
committed
}
}
else {
$db_info['columns'] = array();
}
Jonathan Chaffer
committed
return $db_info;
}
Jonathan Chaffer
committed
/**
* Manipulate a 2D array to reverse rows and columns.
*
Jonathan Chaffer
committed
* 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.
Jonathan Chaffer
committed
*
* @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;
}
Jonathan Chaffer
committed
}
}
}
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)) {
$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).