$type) { if ($section == 'node/add#' . $type_name) { return t($types[$type_name]['description']); } if ($section == 'node/add/' . $type_name) { return t($types[$type_name]['help']); } } } } /** * Implementation of hook_perm(). */ function content_perm() { $perms = array('administer content types'); foreach (content_types() as $name => $type) { $perms[] = 'create '. $name .' content'; $perms[] = 'edit own '. $name .' content'; } return $perms; } /** * Implementation of hook_menu(). */ function content_menu($may_cache) { $items = array(); $access = user_access('administer content types'); // Only include administrative callbacks if we are viewing an admin page. if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types') { include_once(drupal_get_path('module', 'content') .'/content_admin.inc'); } 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, ); $types = content_types(); foreach ($types as $type_name => $type) { $items[] = array( 'path' => 'node/add/'. $type_name, 'title' => t($type['label']), 'access' => user_access('create '. $type_name .' content'), ); } } else { if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types' && arg(3)) { $type = content_types(arg(3)); if ($type) { $items[] = array( 'path' => 'admin/node/types/'. arg(3), 'title' => t($type['label']), 'callback' => '_content_admin_type_edit', 'access' => $access, 'callback arguments' => array(arg(3)), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/delete', 'title' => t('delete'), 'callback' => '_content_admin_type_delete', 'access' => $access, 'callback arguments' => array(arg(3)), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/duplicate', 'title' => t('duplicate'), 'callback' => '_content_admin_type_edit', 'access' => $access, 'callback arguments' => array('', arg(3)), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/edit', 'title' => t('edit'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/fields', 'title' => t('manage fields'), 'callback' => '_content_admin_field_overview', 'access' => $access, 'callback arguments' => array(arg(3)), 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/add_field', 'title' => t('add field'), 'callback' => '_content_admin_field_add', 'access' => $access, 'callback arguments' => array(arg(3)), 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); if (arg(4) == 'fields' && arg(5) && isset($type['fields'][arg(5)])) { $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/fields/'. arg(5), 'title' => t($type['fields'][arg(5)]['widget']['label']), 'callback' => '_content_admin_field', 'access' => $access, 'callback arguments' => array(arg(3), arg(5)), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/node/types/'. arg(3) .'/fields/'. arg(5) .'/remove', 'title' => t('remove field'), 'callback' => '_content_admin_field_remove', 'access' => $access, 'callback arguments' => array(arg(3), arg(5)), 'type' => MENU_CALLBACK, ); } } } } return $items; } /** * Implementation of hook_node_info(). */ function content_node_info() { $types = array(); foreach (content_types() as $name => $type) { $types[$name] = array('name' => t($type['label']), 'base' => 'content'); } 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)) { return TRUE; } } } /** * Implementation of hook_load(). * * 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)) { return unserialize($cached->data); } 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, serialize($default_additions), CACHE_PERMANENT); return $default_additions; } } /** * Implementation of hook_form(). * * Each field defines its own component of the content entry form, via its * chosen widget. */ function content_form(&$node, &$param) { $form = array(); $type = content_types($node->type); // Set form parameters so we can accept file uploads. $form['#attributes'] = array("enctype" => "multipart/form-data"); $form['title'] = array( '#type' => 'textfield', '#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)); return $form; } /** * Implementation of hook_validate(). * * 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); } /** * Implementation of hook_submit(). * * 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); } /** * Implementation of hook_insert(). */ function content_insert(&$node) { _content_field_invoke('insert', $node); _content_field_invoke_default('insert', $node); } /** * Implementation of hook_update(). */ function content_update(&$node) { _content_field_invoke('update', $node); _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); _content_field_invoke_default('delete', $node); cache_clear_all('content:'. $node->nid); } /** * Implementation of hook_view(). */ function content_view(&$node, $teaser = FALSE, $page = FALSE) { $node->body = implode('', _content_field_invoke('view', $node, FALSE, $page)); $node->teaser = implode('', _content_field_invoke('view', $node, TRUE, $page)); $node->readmore = ($node->body != $node->teaser); } /** * 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': 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); } break; } } /** * 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; } if ($field['multiple']) { $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))); } 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']; 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']; 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(); 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; } } if (isset($node->$field['field_name'])) { $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; } } 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(); 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)) { $return = array_merge($return, $result); } else if (isset($result)) { $return[] = $result; } } if (is_object($node) && 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($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]; } 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 (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; } } 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. */ 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_types = module_invoke($module, 'field_info'); if ($module_types) { foreach ($module_types as $name => $field_info) { $info['field types'][$name] = $field_info; $info['field types'][$name]['module'] = $module; } } $module_types = module_invoke($module, 'widget_info'); if ($module_types) { foreach ($module_types as $name => $widget_info) { $info['widget types'][$name] = $widget_info; $info['widget types'][$name]['module'] = $module; } } } $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']); $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} nt ORDER BY nt.type_name ASC'); 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.description FROM {node_field_instance} nfi WHERE nfi.type_name = '%s' ORDER BY nfi.weight ASC, nfi.label ASC", $type['type_name']); 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']; $type['fields'][$field['field_name']] = $field; } $info['content types'][$type['type_name']] = $type; } cache_set('content_type_info', serialize($info), CACHE_PERMANENT); } } return $info; } /** * Clear the cache of content_types; called in several places when content * information is changed. */ function content_clear_type_cache() { cache_clear_all('content_type_info'); _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); $db_info = array(); if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { $db_info['table'] = 'node_data_'. $field['field_name']; } else { $db_info['table'] = 'node_'. strtr($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; } } 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; } /** * Implementation of hook_views_tables(). * * Exposes all fields to the views system if the field has delegated its database * storage to content.module. */ function content_views_tables() { $field_types = _content_field_types(); $tables = array(); foreach (content_fields() as $field) { $db_info = content_database_info($field); $module = $field_types[$field['type']]['module']; if (count($db_info['columns'])) { $table = array(); $table['name'] = $db_info['table']; $table['join'] = array( 'left' => array( 'table' => 'node', 'field' => 'vid', ), 'right' => array( 'field' => 'vid', ), ); $columns = $db_info['columns']; $main_column = array_shift($columns); $addlfields = array(); foreach ($columns as $column => $attributes) { $addlfields[] = $attributes['column']; } $table['fields'] = array(); $table['fields'][$main_column['column']] = array( 'name' => $field_types[$field['type']]['label'] .': '. $field['widget']['label'] .' ('. $field['field_name'] .')', 'addlfields' => $addlfields, 'sortable' => isset($main_column['sortable']) ? $main_column['sortable'] : FALSE, 'handler' => 'content_views_field_handler', 'content_db_info' => $db_info, 'content_field' => $field, 'content_field_module' => $module, ); if (isset($main_column['sortable']) && $main_column['sortable']) { $table['sorts'] = array(); $table['sorts'][$main_column['column']] = array( 'name' => $field_types[$field['type']]['label'] .': '. $field['widget']['label'] .' ('. $field['field_name'] .')', 'field' => $main_column['column'], ); } $operators = module_invoke($module, 'field_settings', 'filter operators', $field); if (is_array($operators) && count($operators)) { $table['filters'] = array(); $table['filters'][$main_column['column']] = array( 'name' => $field_types[$field['type']]['label'] .': '. $field['widget']['label'] .' ('. $field['field_name'] .')', 'operator' => $operators, ); } // We don't use $db_info['table'] for the key, since that may change during // the lifetime of the field and we don't want to require users to redefine // their views. $tables['node_data_'. $field['field_name']] = $table; } } return $tables; } function content_views_field_handler($field_info, $field_data, $value, $data) { $field = $field_info['content_field']; $node_field_item = array(); foreach ($field_info['content_db_info']['columns'] as $column => $attributes) { $view_column_name = 'node_data_'. $field['field_name'] .'_'. $attributes['column']; $node_field_item[$column] = $data->$view_column_name; } $function = $field_info['content_field_module'] .'_field_view_item'; if (function_exists($function)) { $result = $function($field, $node_field_item); } else { $result = implode("\n", $node_field_item); } return $result; } /** * Implementation of hook_views_arguments(). * * Exposes all fields as filterable arguments if the field has delegated its database * storage to content.module. */ function content_views_arguments() { $field_types = _content_field_types(); $arguments = array(); foreach (content_fields() as $field) { $db_info = content_database_info($field); $module = $field_types[$field['type']]['module']; if (count($db_info['columns'])) { $main_column = reset($db_info['columns']); $default_arguments = isset($main_column['default arguments']) ? $main_column['default arguments'] : TRUE; $argument = array(); $argument['name'] = $field_types[$field['type']]['label'] .': '. $field['widget']['label'] .' ('. $field['field_name'] .')'; $argument['handler'] = 'content_views_argument_handler'; $arguments['content: '. $field['field_name']] = $argument; } } return $arguments; } /** * Perform filtering by an argument for field data stored via content.module. */ function content_views_argument_handler($op, &$query, $argtype, $arg = '') { if ($op == 'filter') { $field_name = substr($argtype['type'], 9); } else { $field_name = substr($argtype, 9); } $field = content_fields($field_name); $db_info = content_database_info($field); $main_column = reset($db_info['columns']); // The table name used here is the Views alias for the table, not the actual // table name. $table = 'node_data_'. $field['field_name']; switch ($op) { case 'summary': $query->ensure_table($table); $query->add_field($main_column['column'], $table); return array('field' => $table .'.'. $main_column['column']); break; case 'sort': break; case 'filter': $query->ensure_table($table); switch ($main_column['type']) { case 'int': case 'mediumint': case 'tinyint': case 'bigint': $column_placeholder = '%d'; break; case 'float': $column_placeholder = '%f'; break; default: $column_placeholder = "'%s'"; } $query->add_where($table .'.'. $main_column['column'] .' = '. $column_placeholder, $arg); break; case 'link': $field_types = _content_field_types(); $module = $field_types[$field['type']]['module']; $item = array(); foreach ($db_info['columns'] as $column => $attributes) { $view_column_name = $attributes['column']; $item[$column] = $query->$view_column_name; } $view = module_invoke($module, 'field_view_item', $field, $item); return l($view, $arg .'/'. $query->$main_column['column'], array(), NULL, NULL, FALSE, TRUE); case 'title': $field_types = _content_field_types(); $module = $field_types[$field['type']]['module']; $item = array(key($db_info['columns']) => $query); return module_invoke($module, 'field_view_item', $field, $item); } } /** * 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) { $output = ''; $output .= '
'; $output .= '

'. $field['widget']['label'] .'

'; $output .= '
'; foreach ($items as $item) { $output .= '
'. $item['view'] .'
'; } $output .= '
'; $output .= '
'; return $output; }