4 * Allows administrators to associate custom fields to content types.
7 define('CONTENT_DB_STORAGE_PER_FIELD', 0);
8 define('CONTENT_DB_STORAGE_PER_CONTENT_TYPE', 1);
10 define('CONTENT_CALLBACK_NONE', 0x0001);
11 define('CONTENT_CALLBACK_DEFAULT', 0x0002);
12 define('CONTENT_CALLBACK_CUSTOM', 0x0004);
14 define('CONTENT_HANDLE_CORE', 0x0001);
15 define('CONTENT_HANDLE_MODULE', 0x0002);
17 function content_help($path, $arg) {
19 case
'admin/help#content':
20 $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>';
21 $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>';
22 $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>';
24 $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>';
25 $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>';
26 $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>';
27 $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>';
28 $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>';
30 $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>';
36 * Implementation of hook_flush_caches.
38 function content_flush_caches() {
39 return array(content_cache_tablename());
43 * Implementation of hook_init().
45 function content_init() {
46 drupal_add_css(drupal_get_path('module', 'content') .
'/theme/content-module.css');
47 if (module_exists('token') && !function_exists('content_token_values')) {
48 module_load_include('inc', 'content', 'includes/content.token');
50 if (module_exists('diff') && !function_exists('content_diff')) {
51 module_load_include('inc', 'content', 'includes/content.diff');
56 * Implementation of hook_perm().
58 function content_perm() {
59 return array('Use PHP input for field settings (dangerous - grant with care)');
63 * Implementation of hook_menu_alter().
65 function content_menu_alter(&$items) {
66 // Customize the content types page with our own callback
67 $items['admin/content/types']['page callback'] = 'content_types_overview';
68 $items['admin/content/types']['file'] = 'content.admin.inc';
69 $items['admin/content/types']['file path'] = drupal_get_path('module', 'content') .
'/includes';
73 * Implementation of hook_menu().
75 function content_menu() {
77 $items['admin/content/types/fields'] = array(
79 'page callback' => 'content_fields_list',
80 'access arguments' => array('administer content types'),
81 'file' => 'includes/content.admin.inc',
82 'type' => MENU_LOCAL_TASK
,
84 // Callback for AHAH add more buttons.
85 $items['content/js_add_more'] = array(
86 'page callback' => 'content_add_more_js',
87 'access arguments' => array('access content'),
88 'file' => 'includes/content.node_form.inc',
89 'type' => MENU_CALLBACK
,
92 // Make sure this doesn't fire until content_types is working,
93 // and tables are updated, needed to avoid errors on initial installation.
94 if (!defined('MAINTENANCE_MODE') && variable_get('content_schema_version', -1) >= 6007) {
95 foreach (node_get_types() as
$type) {
96 $type_name = $type->type
;
97 $content_type = content_types($type_name);
98 $type_url_str = $content_type['url_str'];
99 $items['admin/content/node-type/'.
$type_url_str .
'/fields'] = array(
100 'title' => 'Manage fields',
101 'page callback' => 'drupal_get_form',
102 'page arguments' => array('content_field_overview_form', $type_name),
103 'access arguments' => array('administer content types'),
104 'file' => 'includes/content.admin.inc',
105 'type' => MENU_LOCAL_TASK
,
108 $items['admin/content/node-type/'.
$type_url_str .
'/display'] = array(
109 'title' => 'Display fields',
110 'page callback' => 'drupal_get_form',
111 'page arguments' => array('content_display_overview_form', $type_name),
112 'access arguments' => array('administer content types'),
113 'file' => 'includes/content.admin.inc',
114 'type' => MENU_LOCAL_TASK
,
117 $contexts = content_build_modes('_tabs');
118 foreach ($contexts as
$key => $tab) {
119 $items['admin/content/node-type/'.
$type_url_str .
'/display/'.
$key] = array(
120 'title' => $tab['title'],
121 'page arguments' => array('content_display_overview_form', $type_name, $key),
122 'access arguments' => array('administer content types'),
123 'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK
: MENU_LOCAL_TASK
,
124 'weight' => $key == 'basic' ?
0 : 1,
127 // Cast as an array in case this is called before any fields have
128 // been added, like when a new content type is created.
129 foreach ((array) $content_type['fields'] as
$field) {
130 $field_name = $field['field_name'];
131 $items['admin/content/node-type/'.
$type_url_str .
'/fields/'.
$field_name] = array(
132 'title' => $field['widget']['label'],
133 'page callback' => 'drupal_get_form',
134 'page arguments' => array('content_field_edit_form', $type_name, $field_name),
135 'access arguments' => array('administer content types'),
136 'file' => 'includes/content.admin.inc',
137 'type' => MENU_LOCAL_TASK
,
139 $items['admin/content/node-type/'.
$type_url_str .
'/fields/'.
$field_name .
'/remove'] = array(
140 'title' => 'Remove field',
141 'page callback' => 'drupal_get_form',
142 'page arguments' => array('content_field_remove_form', $type_name, $field_name),
143 'access arguments' => array('administer content types'),
144 'file' => 'includes/content.admin.inc',
145 'type' => MENU_CALLBACK
,
156 * Used to add multiple value processing, validation, and themes.
158 * FAPI callbacks can be declared here, and the element will be
159 * passed to those callbacks.
161 * Drupal will automatically theme the element using a theme with
162 * the same name as the hook_elements key.
164 function content_elements() {
166 'content_multiple_values' => array(),
167 'content_field' => array(),
172 * Implementation of hook_theme().
174 function content_theme() {
175 $path = drupal_get_path('module', 'content') .
'/theme';
176 require_once
"./$path/theme.inc";
179 'content_field' => array(
180 'template' => 'content-field',
181 'arguments' => array('element' => NULL
),
184 'content_overview_links' => array(
185 'arguments' => array(),
187 'content_field_overview_form' => array(
188 'template' => 'content-admin-field-overview-form',
189 'file' => 'theme.inc',
191 'arguments' => array('form' => NULL
),
193 'content_display_overview_form' => array(
194 'template' => 'content-admin-display-overview-form',
195 'file' => 'theme.inc',
197 'arguments' => array('form' => NULL
),
199 'content_exclude' => array(
200 'arguments' => array('content' => NULL
, 'object' => array(), 'context' => NULL
),
202 'content_view_multiple_field' => array(
203 'arguments' => array('items' => NULL
, 'field' => NULL
, 'data' => NULL
),
205 'content_multiple_values' => array(
206 'arguments' => array('element' => NULL
),
212 * Implementation of hook_views_api().
214 function content_views_api() {
217 'path' => drupal_get_path('module', 'content') .
'/includes/views',
222 * Implementation of hook_ctools_plugin_directory().
224 function content_ctools_plugin_directory($module, $plugin) {
225 if ($module == 'ctools' && $plugin == 'content_types') {
226 return 'includes/panels/' .
$plugin;
231 * Load data for a node type's fields.
232 * Implementation of hook_nodeapi 'load' op.
234 * When loading one of the content.module nodes, we need to let each field handle
235 * its own loading. This can make for a number of queries in some cases, so we
236 * cache the loaded object structure and invalidate it during the update process.
238 function content_load(&$node) {
239 $cid = 'content:'.
$node->nid .
':'.
$node->vid
;
240 if ($cached = cache_get($cid, content_cache_tablename())) {
241 foreach ($cached->data as
$key => $value) {
242 $node->$key = $value;
246 $default_additions = _content_field_invoke_default('load', $node);
247 if ($default_additions) {
248 foreach ($default_additions as
$key => $value) {
249 $node->$key = $value;
252 $additions = _content_field_invoke('load', $node);
254 foreach ($additions as
$key => $value) {
255 $node->$key = $value;
256 $default_additions[$key] = $value;
259 cache_set($cid, $default_additions, content_cache_tablename());
264 * Implementation of hook_nodeapi 'validate' op.
267 function content_validate(&$node, $form = NULL
) {
268 _content_field_invoke_default('validate', $node, $form);
269 _content_field_invoke('validate', $node, $form);
273 * Implementation of hook_nodeapi 'presave' op.
276 function content_presave(&$node) {
277 _content_field_invoke('presave', $node);
278 _content_field_invoke_default('presave', $node);
282 * Implementation of hook_nodeapi 'insert' op.
284 * Insert node type fields.
286 function content_insert(&$node) {
287 _content_field_invoke('insert', $node);
288 _content_field_invoke_default('insert', $node);
292 * Implementation of hook_nodeapi 'update' op.
294 * Update node type fields.
296 function content_update(&$node) {
297 _content_field_invoke('update', $node);
298 _content_field_invoke_default('update', $node);
299 cache_clear_all('content:'.
$node->nid .
':'.
$node->vid
, content_cache_tablename());
303 * Implementation of hook_nodeapi 'delete' op.
305 * Delete node type fields.
307 function content_delete(&$node) {
308 _content_field_invoke('delete', $node);
309 _content_field_invoke_default('delete', $node);
310 cache_clear_all('content:'.
$node->nid .
':', content_cache_tablename(), TRUE
);
314 * Implementation of hook_nodeapi 'delete_revision' op.
316 * Delete node type fields for a revision.
318 function content_delete_revision(&$node) {
319 _content_field_invoke('delete revision', $node);
320 _content_field_invoke_default('delete revision', $node);
321 cache_clear_all('content:'.
$node->nid .
':'.
$node->vid
, content_cache_tablename());
325 * Implementation of hook_nodeapi 'view' op.
327 * Generate field render arrays.
329 function content_view(&$node, $teaser = FALSE
, $page = FALSE
) {
330 // Let field modules sanitize their data for output.
331 _content_field_invoke('sanitize', $node, $teaser, $page);
334 $additions = _content_field_invoke_default('view', $node, $teaser, $page);
335 $node->content
= array_merge((array) $node->content
, $additions);
339 * Render a single field, fully themed with label and multiple values.
341 * To be used by third-party code (Views, Panels...) that needs to output
342 * an isolated field. Do *not* use inside node templates, use the
343 * $FIELD_NAME_rendered variables instead.
345 * By default, the field is displayed using the settings defined for the
346 * 'full node' or 'teaser' contexts (depending on the value of the $teaser param).
347 * Set $node->build_mode to a different value to use a different context.
349 * Different settings can be specified by adjusting $field['display_settings'].
352 * The field definition.
354 * The node containing the field to display. Can be a 'pseudo-node', containing
355 * at least 'type', 'nid', 'vid', and the field data.
358 * Similar to hook_nodeapi('view')
360 * The themed output for the field.
362 function content_view_field($field, $node, $teaser = FALSE
, $page = FALSE
) {
364 if (isset($node->$field['field_name'])) {
365 $items = $node->$field['field_name'];
367 // Use 'full'/'teaser' if not specified otherwise.
368 $node->build_mode
= isset($node->build_mode
) ?
$node->build_mode
: NODE_BUILD_NORMAL
;
370 // One-field equivalent to _content_field_invoke('sanitize').
371 $field_types = _content_field_types();
372 $module = $field_types[$field['type']]['module'];
373 $function = $module .
'_field';
374 if (function_exists($function)) {
375 $function('sanitize', $node, $field, $items, $teaser, $page);
376 $node->$field['field_name'] = $items;
379 $view = content_field('view', $node, $field, $items, $teaser, $page);
380 // content_field('view') adds a wrapper to handle variables and 'excluded'
381 // fields for node templates. We bypass it and render the actual field.
382 $output = drupal_render($view[$field['field_name']]['field']);
388 * Implementation of hook_nodeapi 'alter' op.
390 * Add back the formatted values in the 'view' element for all fields,
391 * so that node templates can use it.
393 function content_alter(&$node, $teaser = FALSE
, $page = FALSE
) {
394 _content_field_invoke_default('alter', $node, $teaser, $page);
398 * Implementation of hook_nodeapi 'prepare translation' op.
400 * Generate field render arrays.
402 function content_prepare_translation(&$node) {
403 $default_additions = _content_field_invoke_default('prepare translation', $node);
404 $additions = _content_field_invoke('prepare translation', $node);
405 // Merge module additions after the default ones to enable overriding
407 $node = (object) array_merge((array) $node, $default_additions, $additions);
411 * Implementation of hook_nodeapi().
413 function content_nodeapi(&$node, $op, $a3 = NULL
, $a4 = NULL
) {
414 // Prevent against invalid 'nodes' built by broken 3rd party code.
415 if (isset($node->type
)) {
416 $type = content_types($node->type
);
417 // Save cycles if the type has no CCK fields.
418 if (!empty($type['fields'])) {
419 $callback = 'content_'.
str_replace(' ', '_', $op);
420 if (function_exists($callback)) {
421 $callback($node, $a3, $a4);
425 // Special case for 'view' op, we want to adjust weights of non-cck fields
426 // even if there are no actual fields for this type.
428 $node->content
['#pre_render'][] = 'content_alter_extra_weights';
429 $node->content
['#content_extra_fields'] = $type['extra'];
435 * Implementation of hook_form_alter().
437 function content_form_alter(&$form, $form_state, $form_id) {
438 if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .
'_node_form' == $form_id) {
439 $type = content_types($form['#node']->type
);
440 if (!empty($type['fields'])) {
441 module_load_include('inc', 'content', 'includes/content.node_form');
442 // Merge field widgets.
443 $form = array_merge($form, content_form($form, $form_state));
445 $form['#pre_render'][] = 'content_alter_extra_weights';
446 $form['#content_extra_fields'] = $type['extra'];
451 * Pre-render callback to adjust weights of non-CCK fields.
453 function content_alter_extra_weights($elements) {
454 if (isset($elements['#content_extra_fields'])) {
455 foreach ($elements['#content_extra_fields'] as
$key => $value) {
456 // Some core 'fields' use a different key in node forms and in 'view'
457 // render arrays. Check we're not on a form first.
458 if (!isset($elements['#build_id']) && isset($value['view']) && isset($elements[$value['view']])) {
459 $elements[$value['view']]['#weight'] = $value['weight'];
461 elseif (isset($elements[$key])) {
462 $elements[$key]['#weight'] = $value['weight'];
470 * Proxy function to call content_add_more_submit(), because it might not be
471 * included yet when the form is processed and invokes the callback.
473 function content_add_more_submit_proxy($form, &$form_state) {
474 module_load_include('inc', 'content', 'includes/content.node_form');
475 content_add_more_submit($form, $form_state);
479 * Proxy function to call content_multiple_value_after_build(), because it might
480 * not be included yet when the form is processed and invokes the callback.
482 function content_multiple_value_after_build_proxy($elements, &$form_state) {
483 module_load_include('inc', 'content', 'includes/content.node_form');
484 return content_multiple_value_after_build($elements, $form_state);
488 * Theme an individual form element.
490 * Combine multiple values into a table with drag-n-drop reordering.
492 function theme_content_multiple_values($element) {
493 $field_name = $element['#field_name'];
494 $field = content_fields($field_name);
497 if ($field['multiple'] >= 1) {
498 $table_id = $element['#field_name'] .
'_values';
499 $order_class = $element['#field_name'] .
'-delta-order';
500 $required = !empty($element['#required']) ?
'<span class="form-required" title="'.
t('This field is required.') .
'">*</span>' : '';
504 'data' => t('!title: !required', array('!title' => $element['#title'], '!required' => $required)),
507 array('data' => t('Order'), 'class' => 'content-multiple-weight-header'),
509 if ($field['multiple'] == 1) {
510 $header[] = array('data' => '<span>'.
t('Remove') .
'</span>', 'class' => 'content-multiple-remove-header');
514 // Sort items according to '_weight' (needed when the form comes back after
515 // preview or failed validation)
517 foreach (element_children($element) as
$key) {
518 if ($key !== $element['#field_name'] .
'_add_more') {
519 $items[$element[$key]['#delta']] = &$element[$key];
522 uasort($items, '_content_sort_items_value_helper');
524 // Add the items as table rows.
525 foreach ($items as
$delta => $item) {
526 $item['_weight']['#attributes']['class'] = $order_class;
527 $delta_element = drupal_render($item['_weight']);
528 if ($field['multiple'] == 1) {
529 $remove_element = drupal_render($item['_remove']);
532 array('data' => '', 'class' => 'content-multiple-drag'),
533 drupal_render($item),
534 array('data' => $delta_element, 'class' => 'delta-order'),
536 $row_class = 'draggable';
537 if ($field['multiple'] == 1) {
538 if (!empty($item['_remove']['#default_value'])) {
539 $row_class .
= ' content-multiple-removed-row';
541 $cells[] = array('data' => $remove_element, 'class' => 'content-multiple-remove-cell');
543 $rows[] = array('data' => $cells, 'class' => $row_class);
546 $output .
= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'content-multiple-table'));
547 $output .
= $element['#description'] ?
'<div class="description">'.
$element['#description'] .
'</div>' : '';
548 $output .
= drupal_render($element[$element['#field_name'] .
'_add_more']);
550 drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
551 drupal_add_js(drupal_get_path('module', 'content') .
'/js/content.node_form.js');
554 foreach (element_children($element) as
$key) {
555 $output .
= drupal_render($element[$key]);
563 * Modules notify Content module when uninstalled, disabled, etc.
566 * the module operation: uninstall, install, enable, disable
567 * @param string $module
568 * the name of the affected module.
570 * figure out exactly what needs to be done by content module when
571 * field modules are installed, uninstalled, enabled or disabled.
573 function content_notify($op, $module) {
576 content_clear_type_cache();
579 module_load_include('inc', 'content', 'includes/content.crud');
580 content_module_delete($module);
583 content_associate_fields($module);
584 content_clear_type_cache();
587 // When CCK modules are disabled before content module's update is run
588 // to add the active column, we can't do this.
589 if (variable_get('content_schema_version', -1) < 6007) {
592 db_query("UPDATE {".
content_field_tablename() .
"} SET active=0 WHERE module='%s'", $module);
593 db_query("UPDATE {".
content_instance_tablename() .
"} SET widget_active=0 WHERE widget_module='%s'", $module);
594 content_clear_type_cache(TRUE
);
600 * Allows a module to update the database for fields and columns it controls.
602 * @param string $module
603 * The name of the module to update on.
605 function content_associate_fields($module) {
606 // When CCK modules are enabled before content module's update is run,
607 // to add module and active columns, we can't do this.
608 if (variable_get('content_schema_version', -1) < 6007) {
611 $module_fields = module_invoke($module, 'field_info');
612 if ($module_fields) {
613 foreach ($module_fields as
$name => $field_info) {
614 watchdog('content', 'Updating field type %type with module %module.', array('%type' => $name, '%module' => $module));
615 db_query("UPDATE {".
content_field_tablename() .
"} SET module = '%s', active = %d WHERE type = '%s'", $module, 1, $name);
618 $module_widgets = module_invoke($module, 'widget_info');
619 if ($module_widgets) {
620 foreach ($module_widgets as
$name => $widget_info) {
621 watchdog('content', 'Updating widget type %type with module %module.', array('%type' => $name, '%module' => $module));
622 db_query("UPDATE {".
content_instance_tablename() .
"} SET widget_module = '%s', widget_active = %d WHERE widget_type = '%s'", $module, 1, $name);
625 // This is called from updates and installs, so get the install-safe
626 // version of a fields array.
627 $fields_set = array();
628 module_load_include('install', 'content');
629 $types = content_types_install();
630 foreach ($types as
$type_name => $fields) {
631 foreach ($fields as
$field) {
632 if ($field['module'] == $module && !in_array($field['field_name'], $fields_set)) {
633 $columns = (array) module_invoke($field['module'], 'field_settings', 'database columns', $field);
634 db_query("UPDATE {".
content_field_tablename() .
"} SET db_columns = '%s' WHERE field_name = '%s'", serialize($columns), $field['field_name']);
635 $fields_set[] = $field['field_name'];
642 * Implementation of hook_field(). Handles common field housekeeping.
644 * This implementation is special, as content.module does not define any field
645 * types. Instead, this function gets called after the type-specific hook, and
646 * takes care of default stuff common to all field types.
648 * Db-storage ops ('load', 'insert', 'update', 'delete', 'delete revisions')
649 * are not executed field by field, and are thus handled separately in
652 * The 'view' operation constructs the $node in a way that you can use
653 * drupal_render() to display the formatted output for an individual field.
654 * i.e. print drupal_render($node->countent['field_foo']);
656 * The code now supports both single value formatters, which theme an
657 * individual item value as has been done in previous version of CCK,
658 * and multiple value formatters, which theme all values for the field
659 * in a single theme. The multiple value formatters could be used, for
660 * instance, to plot field values on a single map or display them
661 * in a graph. Single value formatters are the default, multiple value
662 * formatters can be designated as such in formatter_info().
664 * The node array will look like:
665 * $node->content['field_foo']['wrapper'] = array(
666 * '#type' => 'content_field',
667 * '#title' => 'label'
668 * '#field_name' => 'field_name',
670 * // Value of the $teaser param of hook_nodeapi('view').
671 * '#teaser' => $teaser,
672 * // Value of the $page param of hook_nodeapi('view').
674 * // The curent rendering context ('teaser', 'full', NODE_BUILD_SEARCH_INDEX...).
675 * '#context' => $context,
678 * '#item' => $items[0],
679 * // Only for 'single-value' formatters
680 * '#theme' => $theme,
681 * '#field_name' => 'field_name',
682 * '#type_name' => $node->type,
683 * '#formatter' => $formatter_name,
688 * '#item' => $items[1],
689 * // Only for 'single-value' formatters
690 * '#theme' => $theme,
691 * '#field_name' => 'field_name',
692 * '#type_name' => $node->type,
693 * '#formatter' => $formatter_name,
697 * // Only for 'multiple-value' formatters
698 * '#theme' => $theme,
699 * '#field_name' => 'field_name',
700 * '#type_name' => $node->type,
701 * '#formatter' => $formatter_name,
705 function content_field($op, &$node, $field, &$items, $teaser, $page) {
708 // If the field is configured for multiple values and these are handled
709 // by content module, we need to filter out items flagged for removal and
710 // count non-empty items to enforce field requirement settings.
711 if ($field['multiple'] >= 1 && content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE
) {
712 module_load_include('inc', 'content', 'includes/content.node_form');
713 // Note that the $teaser argument for nodeapi('validate') is the $form.
714 content_multiple_value_nodeapi_validate($node, $field, $items, $teaser);
719 if (!empty($node->devel_generate
)) {
720 include_once('./'.
drupal_get_path('module', 'content') .
'/includes/content.devel.inc');
721 content_generate_fields($node, $field);
722 $items = $node->{$field['field_name']};
725 // Manual node_save calls might not have all fields filled in.
726 // On node insert, we need to make sure all tables get at least an empty
727 // record, or subsequent edits, using drupal_write_record() in update mode,
728 // won't insert any data.
729 // Missing fields on node update are handled in content_storage().
730 if (empty($items) && !isset($node->nid
)) {
731 foreach (array_keys($field['columns']) as
$column) {
732 $items[0][$column] = NULL
;
734 $node->$field['field_name'] = $items;
737 // If there was an AHAH add more button in this field, don't save it.
738 // TODO: is it still needed ?
739 unset($items[$field['field_name'] .
'_add_more']);
741 if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE
) {
742 // Reorder items to account for drag-n-drop reordering.
743 $items = _content_sort_items($field, $items);
746 // Filter out items flagged for removal.
747 $items = content_set_empty($field, $items);
754 // Previewed nodes bypass the 'presave' op, so we need to do some massaging.
755 if ($node->build_mode
== NODE_BUILD_PREVIEW
) {
756 if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE
) {
757 // Reorder items to account for drag-n-drop reordering.
758 $items = _content_sort_items($field, $items);
761 // Filter out items flagged for removal.
762 $items = content_set_empty($field, $items);
765 // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===.
766 if ($node->build_mode
=== NODE_BUILD_NORMAL
|| $node->build_mode
== NODE_BUILD_PREVIEW
) {
767 $context = $teaser ?
'teaser' : 'full';
770 $context = $node->build_mode
;
772 // The field may be missing info for $contexts added by modules
773 // enabled after the field was last edited.
774 $formatter_name = isset($field['display_settings'][$context]) && isset($field['display_settings'][$context]['format']) ?
$field['display_settings'][$context]['format'] : 'default';
775 if ($formatter = _content_get_formatter($formatter_name, $field['type'])) {
776 $theme = $formatter['module'] .
'_formatter_'.
$formatter_name;
777 $single = (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE
);
779 $label_display = isset($field['display_settings']['label']['format']) ?
$field['display_settings']['label']['format'] : 'above';
780 // Do not include field labels when indexing content.
781 if ($context == NODE_BUILD_SEARCH_INDEX
) {
782 $label_display = 'hidden';
786 '#type' => 'content_field',
787 '#title' => check_plain(t($field['widget']['label'])),
788 '#field_name' => $field['field_name'],
789 '#access' => $formatter_name != 'hidden' && content_access('view', $field, NULL
, $node),
790 '#label_display' => $label_display,
792 '#teaser' => $teaser,
794 '#context' => $context,
795 '#single' => $single,
800 foreach (array_keys($items) as
$weight => $delta) {
801 $element['items'][$delta] = array(
802 '#item' => $items[$delta],
803 '#weight' => $weight,
807 // Append formatter information either on each item ('single-value' formatter)
808 // or at the upper 'items' level ('multiple-value' formatter)
809 $format_info = array(
811 '#field_name' => $field['field_name'],
812 '#type_name' => $node->type
,
813 '#formatter' => $formatter_name,
817 foreach ($items as
$delta => $item) {
818 $element['items'][$delta] += $format_info;
819 $element['items'][$delta]['#item']['#delta'] = $delta;
823 $element['items'] += $format_info;
826 // The wrapper lets us get the themed output for the whole field
827 // to populate the $FIELD_NAME_rendered variable for node templates,
828 // and hide it from the $content variable if needed.
829 // See 'preprocess_node' op and theme_content_field_wrapper()?
832 '#weight' => $field['widget']['weight'],
833 '#post_render' => array('content_field_wrapper_post_render'),
834 '#field_name' => $field['field_name'],
835 '#type_name' => $node->type
,
836 '#context' => $context,
839 $addition = array($field['field_name'] => $wrapper);
844 // Add back the formatted values in the 'view' element,
845 // so that tokens and node templates can use it.
846 // Note: Doing this in 'preprocess_node' breaks token integration.
848 // The location of the field's rendered output depends on whether the
849 // field is in a fieldgroup or not.
850 $wrappers = content_get_nested_elements($node->content
, $field['field_name']);
851 foreach ($wrappers as
$wrapper) {
852 $element = $wrapper['field'];
853 // '#single' is not set if the field is hidden or inaccessible.
854 if (isset($element['#single'])) {
855 if (!empty($element['#single'])) {
856 // Single value formatter.
857 foreach (element_children($element['items']) as
$delta) {
858 // '#children' is not set if the field is empty.
859 $items[$delta]['view'] = isset($element['items'][$delta]['#children']) ?
$element['items'][$delta]['#children'] : '';
862 elseif (isset($element['items']['#children'])) {
863 // Multiple values formatter.
864 $items[0]['view'] = $element['items']['#children'];
868 // Hidden or inaccessible field.
869 $items[0]['view'] = '';
874 case
'preprocess_node':
875 // Add $FIELD_NAME_rendered variables.
878 // The location of the field's rendered output depends on whether the
879 // field is in a fieldgroup or not.
880 $wrappers = content_get_nested_elements($node->content
, $field['field_name']);
881 foreach ($wrappers as
$wrapper) {
882 // '#children' is not set if the field is empty.
883 $addition[$field['field_name'] .
'_rendered'] .
= isset($wrapper['#children']) ?
$wrapper['#children'] : '';
887 case
'prepare translation':
889 if (isset($node->translation_source
->$field['field_name'])) {
890 $addition[$field['field_name']] = $node->translation_source
->$field['field_name'];
897 * Helper function to filter out items flagged for removal.
899 * On order to keep marker rows in the database, the function ensures
900 * that the right number of 'all columns NULL' values is kept.
902 * @param array $field
903 * @param array $items
905 * returns filtered and adjusted item array
907 function content_set_empty($field, $items) {
908 // Prepare an empty item.
910 foreach (array_keys($field['columns']) as
$column) {
911 $empty[$column] = NULL
;
914 // Filter out items flagged for removal.
916 $function = $field['module'] .
'_content_is_empty';
917 foreach ((array) $items as
$delta => $item) {
918 if (empty($item['_remove'])) {
919 $filtered[] = ($function($item, $field) ?
$empty : $item);
923 // Make sure we store the right number of 'empty' values.
924 $pad = $field['multiple'] > 1 ?
$field['multiple'] : 1;
925 $filtered = array_pad($filtered, $pad, $empty);
931 * Helper function to sort items in a field according to
932 * user drag-n-drop reordering.
934 function _content_sort_items($field, $items) {
935 if ($field['multiple'] >= 1 && isset($items[0]['_weight'])) {
936 usort($items, '_content_sort_items_helper');
937 foreach ($items as
$delta => $item) {
938 if (is_array($item) && isset($item['_weight'])) {
939 unset($items[$delta]['_weight']);
947 * Sort function for items order.
948 * (copied form element_sort(), which acts on #weight keys)
950 function _content_sort_items_helper($a, $b) {
951 $a_weight = (is_array($a) && isset($a['_weight'])) ?
$a['_weight'] : 0;
952 $b_weight = (is_array($b) && isset($b['_weight'])) ?
$b['_weight'] : 0;
953 if ($a_weight == $b_weight) {
956 return ($a_weight < $b_weight) ?
-1 : 1;
960 * Same as above, using ['_weight']['#value']
962 function _content_sort_items_value_helper($a, $b) {
963 $a_weight = (is_array($a) && isset($a['_weight']['#value'])) ?
$a['_weight']['#value'] : 0;
964 $b_weight = (is_array($b) && isset($b['_weight']['#value'])) ?
$b['_weight']['#value'] : 0;
965 if ($a_weight == $b_weight) {
968 return ($a_weight < $b_weight) ?
-1 : 1;
972 * Handle storage ops for _content_field_invoke_default().
974 function content_storage($op, $node) {
975 // Don't try this before content module's update is run to add
976 // the active and module columns.
977 if (variable_get('content_schema_version', -1) < 6007) {
981 $type_name = $node->type
;
982 $type = content_types($type_name);
986 // OPTIMIZE: load all non multiple fields in a single JOIN query ?
987 // warning: 61-join limit in MySQL ?
988 $additions = array();
989 // For each table used by this content type,
990 foreach ($type['tables'] as
$table) {
991 $schema = drupal_get_schema($table);
992 // The per-type table might not have any fields actually stored in it.
993 if (!$schema['content fields']) {
996 $query = 'SELECT * FROM {'.
$table .
'} WHERE vid = %d';
998 // If we're loading a table for a multiple field,
999 // we fetch all rows (values) ordered by delta,
1000 // else we only fetch one row.
1001 $result = isset($schema['fields']['delta']) ?
db_query($query .
' ORDER BY delta', $node->vid
) : db_query_range($query, $node->vid
, 0, 1);
1003 // For each table row, populate the fields.
1004 while ($row = db_fetch_array($result)) {
1005 // For each field stored in the table, add the field item.
1006 foreach ($schema['content fields'] as
$field_name) {
1008 $field = content_fields($field_name, $type_name);
1009 $db_info = content_database_info($field);
1010 // For each column declared by the field, populate the item.
1011 foreach ($db_info['columns'] as
$column => $attributes) {
1012 $item[$column] = $row[$attributes['column']];
1015 // Add the item to the field values for the node.
1016 if (!isset($additions[$field_name])) {
1017 $additions[$field_name] = array();
1020 // Preserve deltas when loading items from database.
1021 if (isset($row['delta'])) {
1022 // Make sure multiple value fields have consecutive deltas.
1023 if ($row['delta'] > 0 && !isset($additions[$field_name][$row['delta']-1])) {
1025 foreach (array_keys($db_info['columns']) as
$column) {
1026 $empty[$column] = NULL
;
1028 $next_delta = !empty($additions[$field_name]) ?
(max(array_keys($additions[$field_name])) + 1) : 0;
1029 for ($delta = $next_delta; $delta < $row['delta']; $delta++) {
1030 if (!isset($additions[$field_name][$delta])) {
1031 $additions[$field_name][$delta] = $empty;
1035 $additions[$field_name][$row['delta']] = $item;
1038 $additions[$field_name][] = $item;
1047 foreach ($type['tables'] as
$table) {
1048 $schema = drupal_get_schema($table);
1050 foreach ($schema['content fields'] as
$field_name) {
1051 if (isset($node->$field_name)) {
1052 $field = content_fields($field_name, $type_name);
1053 // Multiple fields need specific handling, we'll deal with them later on.
1054 if ($field['multiple']) {
1057 $db_info = content_database_info($field);
1058 foreach ($db_info['columns'] as
$column => $attributes) {
1059 $record[$attributes['column']] = $node->{$field_name}[0][$column];
1063 // $record might be empty because
1064 // - the table stores a multiple field :
1065 // we do nothing, this is handled later on
1066 // - this is the per-type table and no field is actually stored in it :
1067 // we still store the nid and vid
1068 if (count($record) || empty($schema['content fields'])) {
1069 $record['nid'] = $node->nid
;
1070 $record['vid'] = $node->vid
;
1071 // Can't rely on the insert/update op of the node to decide if this
1072 // is an insert or an update, a node or revision may have existed
1073 // before any fields were created, so there may not be an entry here.
1075 // TODO - should we auto create an entry for all existing nodes when
1076 // fields are added to content types -- either a NULL value
1077 // or the default value? May need to offer the user an option of
1078 // how to handle that.
1079 if (db_result(db_query("SELECT COUNT(*) FROM {".
$table .
"} WHERE vid = %d", $node->vid
))) {
1080 content_write_record($table, $record, array('vid'));
1083 content_write_record($table, $record);
1088 // Handle multiple fields.
1089 foreach ($type['fields'] as
$field) {
1090 if ($field['multiple'] && isset($node->$field['field_name'])) {
1091 $db_info = content_database_info($field);
1092 // Delete and insert, rather than update, in case a value was added.
1093 if ($op == 'update') {
1094 db_query('DELETE FROM {'.
$db_info['table'] .
'} WHERE vid = %d', $node->vid
);
1096 // Collect records for non-empty items.
1097 $function = $field['module'] .
'_content_is_empty';
1099 foreach ($node->$field['field_name'] as
$delta => $item) {
1100 if (!$function($item, $field)) {
1102 foreach ($db_info['columns'] as
$column => $attributes) {
1103 $record[$attributes['column']] = $item[$column];
1105 $record['nid'] = $node->nid
;
1106 $record['vid'] = $node->vid
;
1107 $record['delta'] = $delta;
1108 $records[] = $record;
1111 // If there was no non-empty item, insert delta 0 with NULL values.
1112 if (empty($records)) {
1114 foreach ($db_info['columns'] as
$column => $attributes) {
1115 $record[$attributes['column']] = NULL
;
1117 $record['nid'] = $node->nid
;
1118 $record['vid'] = $node->vid
;
1119 $record['delta'] = 0;
1120 $records[] = $record;
1122 // Insert the collected records for this field into database.
1123 foreach ($records as
$record) {
1124 content_write_record($db_info['table'], $record);
1131 foreach ($type['tables'] as
$table) {
1132 db_query('DELETE FROM {'.
$table .
'} WHERE nid = %d', $node->nid
);
1136 case
'delete revision':
1137 foreach ($type['tables'] as
$table) {
1138 db_query('DELETE FROM {'.
$table .
'} WHERE vid = %d', $node->vid
);
1145 * Save a record to the database based upon the schema.
1147 * Directly copied from core's drupal_write_record, which can't update a
1148 * column to NULL. See http://drupal.org/node/227677 and
1149 * http://drupal.org/node/226264 for more details about that problem.
1151 * TODO - get rid of this function and change references back to
1152 * drupal_write_record() if the patch gets into core. Will need a method
1153 * of protecting people on older versions, though.
1155 * Default values are filled in for missing items, and 'serial' (auto increment)
1156 * types are filled in with IDs.
1159 * The name of the table; this must exist in schema API.
1161 * The object to write. This is a reference, as defaults according to
1162 * the schema may be filled in on the object, as well as ID on the serial
1163 * type(s). Both array an object types may be passed.
1165 * If this is an update, specify the primary keys' field names. It is the
1166 * caller's responsibility to know if a record for this object already
1167 * exists in the database. If there is only 1 key, you may pass a simple string.
1169 * Failure to write a record will return FALSE. Otherwise SAVED_NEW or
1170 * SAVED_UPDATED is returned depending on the operation performed. The
1171 * $object parameter contains values for any serial fields defined by
1172 * the $table. For example, $object->nid will be populated after inserting
1175 function content_write_record($table, &$object, $update = array()) {
1176 // Standardize $update to an array.
1177 if (is_string($update)) {
1178 $update = array($update);
1181 // Convert to an object if needed.
1182 if (is_array($object)) {
1183 $object = (object) $object;
1190 $schema = drupal_get_schema($table);
1191 if (empty($schema)) {
1195 $fields = $defs = $values = $serials = $placeholders = array();
1197 // Go through our schema, build SQL, and when inserting, fill in defaults for
1198 // fields that are not set.
1199 foreach ($schema['fields'] as
$field => $info) {
1200 // Special case -- skip serial types if we are updating.
1201 if ($info['type'] == 'serial' && count($update)) {
1205 // For inserts, populate defaults from Schema if not already provided
1206 if (!isset($object->$field) && !count($update) && isset($info['default'])) {
1207 $object->$field = $info['default'];
1210 // Track serial fields so we can helpfully populate them after the query.
1211 if ($info['type'] == 'serial') {
1212 $serials[] = $field;
1213 // Ignore values for serials when inserting data. Unsupported.
1214 unset($object->$field);
1217 // Build arrays for the fields, placeholders, and values in our query.
1218 if (isset($object->$field) || array_key_exists($field, $object)) {
1220 if (isset($object->$field)) {
1221 $placeholders[] = db_type_placeholder($info['type']);
1223 if (empty($info['serialize'])) {
1224 $values[] = $object->$field;
1227 $values[] = serialize($object->$field);
1231 $placeholders[] = 'NULL';
1238 if (!count($update)) {
1239 $query = "INSERT INTO {".
$table .
"} (".
implode(', ', $fields) .
') VALUES ('.
implode(', ', $placeholders) .
')';
1240 $return = SAVED_NEW
;
1244 foreach ($fields as
$id => $field) {
1248 $query .
= $field .
' = '.
$placeholders[$id];
1251 foreach ($update as
$key) {
1252 $conditions[] = "$key = ".
db_type_placeholder($schema['fields'][$key]['type']);
1253 $values[] = $object->$key;
1256 $query = "UPDATE {".
$table .
"} SET $query WHERE ".
implode(' AND ', $conditions);
1257 $return = SAVED_UPDATED
;
1261 if (db_query($query, $values)) {
1263 // Get last insert ids and fill them in.
1264 foreach ($serials as
$field) {
1265 $object->$field = db_last_insert_id($table, $field);
1269 // If we began with an array, convert back so we don't surprise the caller.
1271 $object = (array) $object;
1281 * Invoke a field hook.
1283 * For each operation, both this function and _content_field_invoke_default() are
1284 * called so that the default database handling can occur.
1286 function _content_field_invoke($op, &$node, $teaser = NULL
, $page = NULL
) {
1287 $type_name = is_string($node) ?
$node : (is_array($node) ?
$node['type'] : $node->type
);
1288 $type = content_types($type_name);
1289 $field_types = _content_field_types();
1292 foreach ($type['fields'] as
$field) {
1293 $items = isset($node->$field['field_name']) ?
$node->$field['field_name'] : array();
1295 // Make sure AHAH 'add more' button isn't sent to the fields for processing.
1296 unset($items[$field['field_name'] .
'_add_more']);
1298 $module = $field_types[$field['type']]['module'];
1299 $function = $module .
'_field';
1300 if (function_exists($function)) {
1301 $result = $function($op, $node, $field, $items, $teaser, $page);
1302 if (is_array($result)) {
1303 $return = array_merge($return, $result);
1305 else if (isset($result)) {
1306 $return[] = $result;
1309 // test for values in $items in case modules added items on insert
1310 if (isset($node->$field['field_name']) || count($items)) {
1311 $node->$field['field_name'] = $items;
1318 * Invoke content.module's version of a field hook.
1320 function _content_field_invoke_default($op, &$node, $teaser = NULL
, $page = NULL
) {
1321 $type_name = is_string($node) ?
$node : (is_array($node) ?
$node['type'] : $node->type
);
1322 $type = content_types($type_name);
1323 $field_types = _content_field_types();
1326 // The operations involving database queries are better off handled by table
1327 // rather than by field.
1328 if (in_array($op, array('load', 'insert', 'update', 'delete', 'delete revision'))) {
1329 return content_storage($op, $node);
1332 foreach ($type['fields'] as
$field) {
1333 $items = isset($node->$field['field_name']) ?
$node->$field['field_name'] : array();
1334 $result = content_field($op, $node, $field, $items, $teaser, $page);
1335 if (is_array($result)) {
1336 $return = array_merge($return, $result);
1338 else if (isset($result)) {
1339 $return[] = $result;
1341 if (isset($node->$field['field_name'])) {
1342 $node->$field['field_name'] = $items;
1350 * Return a list of all content types.
1352 * @param $content_type_name
1353 * If set, return information on just this type.
1355 * Do some type checking and set up empty arrays for missing
1356 * info to avoid foreach errors elsewhere in the code.
1358 function content_types($type_name = NULL
) {
1359 // handle type name with either an underscore or a dash
1360 $type_name = !empty($type_name) ?
str_replace('-', '_', $type_name) : NULL
;
1362 $info = _content_type_info();
1363 if (isset($info['content types'])) {
1364 if (!isset($type_name)) {
1365 return $info['content types'];
1367 if (isset($info['content types'][$type_name])) {
1368 return $info['content types'][$type_name];
1371 return array('tables' => array(), 'fields' => array(), 'extra' => array());
1375 * Return a list of all fields.
1377 * @param $field_name
1378 * If not empty, return information on just this field.
1379 * @param $content_type_name
1380 * If not empty, return information of the field within the context of this content
1383 * Be sure to check empty() instead of isset() on field_name and
1384 * content_type_name to avoid bad results when the value is set
1385 * but empty, as sometimes happens in the formatter.
1387 function content_fields($field_name = NULL
, $content_type_name = NULL
) {
1388 $info = _content_type_info();
1389 if (isset($info['fields'])) {
1390 if (empty($field_name)) {
1391 return $info['fields'];
1393 if (isset($info['fields'][$field_name])) {
1394 if (empty($content_type_name)) {
1395 return $info['fields'][$field_name];
1397 if (isset($info['content types'][$content_type_name]['fields'][$field_name])) {
1398 return $info['content types'][$content_type_name]['fields'][$field_name];
1405 * Return a list of field types.
1407 function _content_field_types() {
1408 $info = _content_type_info();
1409 return isset($info['field types']) ?
$info['field types'] : array();
1413 * Return a list of widget types.
1415 function _content_widget_types() {
1416 $info = _content_type_info();
1417 return isset($info['widget types']) ?
$info['widget types'] : array();
1421 * Return the formatter description corresponding to a formatter name,
1422 * defaulting to 'default' if none is found.
1424 function _content_get_formatter($formatter_name, $field_type) {
1425 $field_types = _content_field_types();
1426 $formatters = $field_types[$field_type]['formatters'];
1428 if (!isset($formatters[$formatter_name]) && $formatter_name != 'hidden') {
1429 // This might happen when the selected formatter has been renamed in the
1430 // module, or if the module has been disabled since then.
1431 $formatter_name = 'default';
1434 return isset($formatters[$formatter_name]) ?
$formatters[$formatter_name] : FALSE
;
1438 * Collate all information on content types, fields, and related structures.
1441 * If TRUE, clear the cache and fetch the information from the database again.
1443 function _content_type_info($reset = FALSE
) {
1447 if ($reset || !isset($info)) {
1448 // Make sure this function doesn't run until the tables have been created,
1449 // For instance: when first enabled and called from content_menu(),
1450 // or when uninstalled and some subsequent field module uninstall
1451 // attempts to refresh the data.
1453 // Don't try this before content module's update is run
1454 // to add module and active columns to the table.
1455 if (variable_get('content_schema_version', -1) < 6007) {
1459 if (!$reset && $cached = cache_get('content_type_info:'.
$language->language
, content_cache_tablename())) {
1460 $info = $cached->data
;
1464 'field types' => array(),
1465 'widget types' => array(),
1466 'fields' => array(),
1467 'content types' => array(),
1470 // Populate field types.
1471 foreach (module_list() as
$module) {
1472 $module_field_types = module_invoke($module, 'field_info');
1473 if ($module_field_types) {
1474 foreach ($module_field_types as
$name => $field_info) {
1475 // Truncate names to match the value that is stored in the database.
1476 $db_name = substr($name, 0, 32);
1477 $info['field types'][$db_name] = $field_info;
1478 $info['field types'][$db_name]['module'] = $module;
1479 $info['field types'][$db_name]['formatters'] = array();
1484 // Populate widget types and formatters for known field types.
1485 foreach (module_list() as
$module) {
1486 if ($module_widgets = module_invoke($module, 'widget_info')) {
1487 foreach ($module_widgets as
$name => $widget_info) {
1488 // Truncate names to match the value that is stored in the database.
1489 $db_name = substr($name, 0, 32);
1490 $info['widget types'][$db_name] = $widget_info;
1491 $info['widget types'][$db_name]['module'] = $module;
1492 // Replace field types with db_compatible version of known field types.
1493 $info['widget types'][$db_name]['field types'] = array();
1494 foreach ($widget_info['field types'] as
$field_type) {
1495 $field_type_db_name = substr($field_type, 0, 32);
1496 if (isset($info['field types'][$field_type_db_name])) {
1497 $info['widget types'][$db_name]['field types'][] = $field_type_db_name;
1503 if ($module_formatters = module_invoke($module, 'field_formatter_info')) {
1504 foreach ($module_formatters as
$name => $formatter_info) {
1505 foreach ($formatter_info['field types'] as
$field_type) {
1506 // Truncate names to match the value that is stored in the database.
1507 $db_name = substr($field_type, 0, 32);
1508 if (isset($info['field types'][$db_name])) {
1509 $info['field types'][$db_name]['formatters'][$name] = $formatter_info;
1510 $info['field types'][$db_name]['formatters'][$name]['module'] = $module;
1517 // Populate actual field instances.
1518 module_load_include('inc', 'content', 'includes/content.crud');
1519 foreach (node_get_types('types', NULL
, TRUE
) as
$type_name => $data) {
1520 $type = (array) $data;
1521 $type['url_str'] = str_replace('_', '-', $type['type']);
1522 $type['fields'] = array();
1523 $type['tables'] = array();
1524 if ($fields = content_field_instance_read(array('type_name' => $type_name))) {
1525 foreach ($fields as
$field) {
1526 $db_info = content_database_info($field);
1527 $type['tables'][$db_info['table']] = $db_info['table'];
1529 // Allow external modules to translate field strings.
1530 $field_strings = array(
1531 'widget_label' => $field['widget']['label'],
1532 'widget_description' => $field['widget']['description'],
1534 drupal_alter('content_field_strings', $field_strings, $field['type_name'], $field['field_name']);
1535 $field['widget']['label'] = $field_strings['widget_label'];
1536 $field['widget']['description'] = $field_strings['widget_description'];
1538 $type['fields'][$field['field_name']] = $field;
1539 // This means that content_fields($field_name) (no type name)
1540 // returns the last instance loaded.
1541 $info['fields'][$field['field_name']] = $field;
1543 // Make sure the per-type table is added, even if no field is actually
1545 $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE
);
1546 $type['tables'][$table] = $table;
1549 // Gather information about non-CCK 'fields'.
1550 $extra = module_invoke_all('content_extra_fields', $type_name);
1551 drupal_alter('content_extra_fields', $extra, $type_name);
1552 // Add saved weights.
1553 foreach (variable_get('content_extra_weights_'.
$type_name, array()) as
$key => $value) {
1554 // Some stored entries might not exist anymore, for instance if uploads
1555 // have been disabled, or vocabularies removed...
1556 if (isset($extra[$key])) {
1557 $extra[$key]['weight'] = $value;
1560 $type['extra'] = $extra;
1562 $info['content types'][$type_name] = $type;
1565 cache_set('content_type_info:'.
$language->language
, $info, content_cache_tablename());
1572 * Implementation of hook_node_type()
1573 * React to change in node types
1575 function content_node_type($op, $info) {
1578 module_load_include('inc', 'content', 'includes/content.crud');
1579 content_type_create($info);
1582 module_load_include('inc', 'content', 'includes/content.crud');
1583 content_type_update($info);
1586 module_load_include('inc', 'content', 'includes/content.crud');
1587 content_type_delete($info);
1593 * Clear the cache of content_types; called in several places when content
1594 * information is changed.
1596 function content_clear_type_cache($rebuild_schema = FALSE
) {
1597 cache_clear_all('*', content_cache_tablename(), TRUE
);
1598 _content_type_info(TRUE
);
1600 // Refresh the schema to pick up new information.
1601 if ($rebuild_schema) {
1602 $schema = drupal_get_schema(NULL
, TRUE
);
1605 if (module_exists('views')) {
1606 // Needed because this can be called from .install files
1607 module_load_include('module', 'views');
1608 views_invalidate_cache();
1613 * Retrieve the database storage location(s) for a field.
1615 * TODO: add a word about why it's not included in the global _content_type_info array.
1618 * The field whose database information is requested.
1620 * An array with the keys:
1621 * "table": The name of the database table where the field data is stored.
1622 * "columns": An array of columns stored for this field. Each is a collection
1623 * of information returned from hook_field_settings('database columns'),
1624 * with the addition of a "column" attribute which holds the name of the
1625 * database column that stores the data.
1627 function content_database_info($field) {
1629 if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD
) {
1630 $db_info['table'] = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD
);
1633 $db_info['table'] = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE
);
1636 $db_info['columns'] = (array) $field['columns'];
1637 // Generate column names for this field from generic column names.
1638 foreach ($db_info['columns'] as
$column_name => $attributes) {
1639 $db_info['columns'][$column_name]['column'] = $field['field_name'] .
'_'.
$column_name;
1646 * Helper function for identifying the storage type for a field.
1648 function content_storage_type($field) {
1649 if ($field['multiple'] > 0) {
1650 return CONTENT_DB_STORAGE_PER_FIELD
;
1653 module_load_include('inc', 'content', 'includes/content.crud');
1654 $instances = content_field_instance_read(array('field_name' => $field['field_name']));
1655 if (count($instances) > 1) {
1656 return CONTENT_DB_STORAGE_PER_FIELD
;
1659 return CONTENT_DB_STORAGE_PER_CONTENT_TYPE
;
1663 * Manipulate a 2D array to reverse rows and columns.
1665 * The default data storage for fields is delta first, column names second.
1666 * This is sometimes inconvenient for field modules, so this function can be
1667 * used to present the data in an alternate format.
1670 * The array to be transposed. It must be at least two-dimensional, and
1671 * the subarrays must all have the same keys or behavior is undefined.
1673 * The transposed array.
1675 function content_transpose_array_rows_cols($array) {
1677 if (is_array($array)) {
1678 foreach ($array as
$key1 => $value1) {
1679 if (is_array($value1)) {
1680 foreach ($value1 as
$key2 => $value2) {
1681 if (!isset($result[$key2])) {
1682 $result[$key2] = array();
1684 $result[$key2][$key1] = $value2;
1693 * Helper function to flatten an array of allowed values.
1696 * A single or multidimensional array.
1698 * A flattened array.
1700 function content_array_flatten($array) {
1702 if (is_array($array)) {
1703 foreach ($array as
$key => $value) {
1704 if (is_array($value)) {
1705 $result += content_array_flatten($value);
1708 $result[$key] = $value;
1716 * Create an array of the allowed values for this field.
1718 * Used by number and text fields, expects to find either
1719 * PHP code that will return the correct value, or a string
1720 * with keys and labels separated with '|' and with each
1721 * new value on its own line.
1724 * The field whose allowed values are requested.
1726 * Optional. Use TRUE to return a flattened array (default).
1727 * FALSE can be used to support optgroups for select widgets
1728 * when allowed values list is generated using PHP code.
1730 function content_allowed_values($field, $flatten = TRUE
) {
1731 static
$allowed_values;
1733 $cid = $field['field_name'] .
':'.
($flatten ?
'1' : '0');
1734 if (isset($allowed_values[$cid])) {
1735 return $allowed_values[$cid];
1738 $allowed_values[$cid] = array();
1740 if (isset($field['allowed_values_php'])) {
1742 $result = eval($field['allowed_values_php']);
1743 if (is_array($result)) {
1745 $result = content_array_flatten($result);
1747 $allowed_values[$cid] = $result;
1752 if (empty($allowed_values[$cid]) && isset($field['allowed_values'])) {
1753 $list = explode("\n", $field['allowed_values']);
1754 $list = array_map('trim', $list);
1755 $list = array_filter($list, 'strlen');
1756 foreach ($list as
$opt) {
1757 // Sanitize the user input with a permissive filter.
1758 $opt = content_filter_xss($opt);
1759 if (strpos($opt, '|') !== FALSE
) {
1760 list($key, $value) = explode('|', $opt);
1761 $allowed_values[$cid][$key] = (isset($value) && $value !=='') ?
$value : $key;
1764 $allowed_values[$cid][$opt] = $opt;
1767 // Allow external modules to translate allowed values list.
1768 drupal_alter('content_allowed_values', $allowed_values[$cid], $field);
1770 return $allowed_values[$cid];
1774 * Filter out HTML from allowed values array while leaving entities unencoded.
1776 * @see content_allowed_values()
1777 * @see optionwidgets_select_process()
1778 * @see content_handler_filter_many_to_one::allowed_values()
1780 function content_allowed_values_filter_html(&$options) {
1781 foreach ($options as
$key => $opt) {
1782 if (is_array($opt)) {
1783 content_allowed_values_filter_html($options[$key]);
1786 $options[$key] = html_entity_decode(strip_tags($opt), ENT_QUOTES
);
1792 * Like filter_xss_admin(), but with a shorter list of allowed tags.
1794 * Used for items entered by administrators, like field descriptions,
1795 * allowed values, where some (mainly inline) mark-up may be desired
1796 * (so check_plain() is not acceptable).
1798 function content_filter_xss($string) {
1799 return filter_xss($string, _content_filter_xss_allowed_tags());
1803 * List of tags allowed by content_filter_xss().
1805 function _content_filter_xss_allowed_tags() {
1806 return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img');
1810 * Human-readable list of allowed tags, for display in help texts.
1812 function _content_filter_xss_display_allowed_tags() {
1813 return '<'.
implode('> <', _content_filter_xss_allowed_tags()) .
'>';
1817 * Format a field item for display.
1819 * Used to display a field's values outside the context of the $node, as
1820 * when fields are displayed in Views, or to display a field in a template
1821 * using a different formatter than the one set up on the Display Fields tab
1822 * for the node's context.
1825 * Either a field array or the name of the field.
1827 * The field item(s) to be formatted (such as $node->field_foo[0],
1828 * or $node->field_foo if the formatter handles multiple values itself)
1829 * @param $formatter_name
1830 * The name of the formatter to use.
1832 * Optionally, the containing node object for context purposes and
1833 * field-instance options.
1836 * A string containing the contents of the field item(s) sanitized for display.
1837 * It will have been passed through the necessary check_plain() or check_markup()
1838 * functions as necessary.
1840 function content_format($field, $item, $formatter_name = 'default', $node = NULL
) {
1841 if (!is_array($field)) {
1842 $field = content_fields($field);
1845 if (content_access('view', $field, NULL
, $node) && $formatter = _content_get_formatter($formatter_name, $field['type'])) {
1846 $theme = $formatter['module'] .
'_formatter_'.
$formatter_name;
1850 '#field_name' => $field['field_name'],
1851 '#type_name' => isset($node->type
) ?
$node->type
:'',
1852 '#formatter' => $formatter_name,
1854 '#delta' => isset($item['#delta']) ?
$item['#delta'] : NULL
,
1857 if (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE
) {
1858 // Single value formatter.
1860 // hook_field('sanitize') expects an array of items, so we build one.
1861 $items = array($item);
1862 $function = $field['module'] .
'_field';
1863 if (function_exists($function)) {
1864 $function('sanitize', $node, $field, $items, FALSE
, FALSE
);
1867 $element['#item'] = $items[0];
1870 // Multiple values formatter.
1872 $function = $field['module'] .
'_field';
1873 if (function_exists($function)) {
1874 $function('sanitize', $node, $field, $items, FALSE
, FALSE
);
1877 foreach ($items as
$delta => $item) {
1878 $element[$delta] = array(
1880 '#weight' => $delta,
1885 return theme($theme, $element);
1890 * Registry of available node build modes.
1893 * Determines what information should be returned.
1895 * Depending on the value of the $selector parameter:
1896 * - NULL: a flat list of all available build modes.
1897 * The other two options are mainly used internally by CCK's UI:
1898 * - '_tabs': the list of tabs to be shown on the 'Display fields' screens.
1899 * - a string tab id: the build modes in this tab.
1901 function content_build_modes($selector = NULL
) {
1904 if (!isset($info)) {
1906 foreach (module_implements('content_build_modes') as
$module) {
1907 $function = $module .
'_content_build_modes';
1908 $data = array_merge($data, (array) $function());
1911 foreach ($data as
$tab) {
1912 // Use the + operator to preserve numeric indexes (core build modes).
1913 $flat += (array) $tab['build modes'];
1915 $info = array('tabs' => $data, 'build modes' => $flat);
1918 if ($selector === '_tabs') {
1919 return $info['tabs'];
1921 elseif (isset($selector) && isset($info['tabs'][$selector])) {
1922 return isset($info['tabs'][$selector]) ?
$info['tabs'][$selector]['build modes'] : array();
1925 return $info['build modes'];
1930 * Implementations of hook_content_build_modes
1931 * on behalf of core modules.
1934 * An array describing the build modes used by the module.
1935 * They are grouped by secondary tabs on CCK's 'Display fields' screens.
1939 * // The first level keys (tab1_url, tab2_url) will be used to generate
1940 * // the url of the tab: admin/content/node-type/[type_name]/display/[tab1_url]
1941 * // A module can add its render modes to a tab defined by another module.
1942 * // In this case, there's no need to provide a 'title' for this tab.
1943 * 'tab1_url' => array(
1944 * 'title' => t('The human-readable title of the tab'),
1945 * 'build modes' => array(
1946 * // The keys of the 'context' array are the values used in $node->build_mode.
1947 * 'mymodule_mode1' => array(
1948 * 'title' => t('The human-readable name of the build mode'),
1949 * // The 'views style' property determines if the render mode should be
1950 * // available as an option in Views' 'node' row style (not implemented yet).
1951 * 'views style' => TRUE,
1953 * 'mymodule_mode2' => array(
1954 * 'title' => t('Mode 2'),
1955 * 'views style' => TRUE,
1959 * 'tab2_url' => array(
1964 function node_content_build_modes() {
1967 'title' => t('Basic'),
1968 'build modes' => array(
1970 'title' => t('Teaser'),
1971 'views style' => TRUE
,
1974 'title' => t('Full node'),
1975 'views style' => TRUE
,
1980 'title' => t('RSS'),
1981 'build modes' => array(
1982 NODE_BUILD_RSS
=> array(
1983 'title' => t('RSS'),
1984 'views style' => FALSE
,
1990 function search_content_build_modes() {
1993 'title' => t('Search'),
1994 'build modes' => array(
1995 NODE_BUILD_SEARCH_INDEX
=> array(
1996 'title' => t('Search Index'),
1997 'views style' => FALSE
,
1999 NODE_BUILD_SEARCH_RESULT
=> array(
2000 'title' => t('Search Result'),
2001 'views style' => FALSE
,
2007 function book_content_build_modes() {
2010 'title' => t('Print'),
2011 'build modes' => array(
2012 NODE_BUILD_PRINT
=> array(
2013 'title' => t('Print'),
2014 'views style' => TRUE
,
2022 * Generate a table name for a field or a content type.
2025 * The name of the content type or content field
2027 * CONTENT_DB_STORAGE_PER_FIELD or CONTENT_DB_STORAGE_PER_CONTENT_TYPE
2029 * A string containing the generated name for the database table
2031 function _content_tablename($name, $storage, $version = NULL
) {
2032 if (is_null($version)) {
2033 $version = variable_get('content_schema_version', 0);
2036 if ($version < 1003) {
2043 $name = str_replace('-', '_', $name);
2044 switch ("$version-$storage") {
2045 case
'0-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE
:
2046 return "node_$name";
2047 case
'0-'. CONTENT_DB_STORAGE_PER_FIELD
:
2048 return "node_data_$name";
2049 case
'1003-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE
:
2050 return "content_type_$name";
2051 case
'1003-'. CONTENT_DB_STORAGE_PER_FIELD
:
2052 return "content_$name";
2057 * Generate table name for the content field table.
2059 * Needed because the table name changes depending on version.
2060 * Using 'content_node_field' instead of 'content_field'
2061 * to avoid conflicts with field tables that will be prefixed
2062 * with 'content_field'.
2064 function content_field_tablename($version = NULL
) {
2065 if (is_null($version)) {
2066 $version = variable_get('content_schema_version', 0);
2068 return $version < 6001 ?
'node_field' : 'content_node_field';
2072 * Generate table name for the content field instance table.
2074 * Needed because the table name changes depending on version.
2076 function content_instance_tablename($version = NULL
) {
2077 if (is_null($version)) {
2078 $version = variable_get('content_schema_version', 0);
2080 return $version < 6001 ?
'node_field_instance' : 'content_node_field_instance';
2084 * Generate table name for the content cache table.
2086 * Needed because the table name changes depending on version. Because of
2087 * a new database column, the content_cache table will be unusable until
2088 * update 6000 runs, so the cache table will be used instead.
2090 function content_cache_tablename() {
2091 if (variable_get('content_schema_version', -1) < 6000) {
2095 return 'cache_content';
2100 * A basic schema used by all field and type tables.
2102 * This will only add the columns relevant for the specified field.
2103 * Leave $field['columns'] empty to get only the base schema,
2104 * otherwise the function will return the whole thing.
2106 function content_table_schema($field = NULL
) {
2109 'vid' => array('type' => 'int', 'unsigned' => TRUE
, 'not null' => TRUE
, 'default' => 0),
2110 'nid' => array('type' => 'int', 'unsigned' => TRUE
, 'not null' => TRUE
, 'default' => 0)
2112 'primary key' => array('vid'),
2114 'nid' => array('nid'),
2118 // Add delta column if needed.
2119 if (!empty($field['multiple'])) {
2120 $schema['fields']['delta'] = array('type' => 'int', 'unsigned' => TRUE
, 'not null' => TRUE
, 'default' => 0);
2121 $schema['primary key'][] = 'delta';
2123 $schema['content fields'] = array();
2125 // Add field columns column if needed.
2126 // This function is called from install files where it is not safe
2127 // to use content_fields() or content_database_info(), so we
2128 // just used the column values stored in the $field.
2129 // We also need the schema to include fields from disabled modules
2130 // or there will be no way to delete those fields.
2132 if (!empty($field['columns'])) {
2133 foreach ($field['columns'] as
$column => $attributes) {
2134 $column_name = $field['field_name'] .
'_'.
$column;
2135 if (isset($attributes['index']) && $attributes['index']) {
2136 $schema['indexes'][$column_name] = array($column_name);
2137 unset($attributes['index']);
2139 unset($attributes['column']);
2140 unset($attributes['sortable']);
2141 $schema['fields'][$column_name] = $attributes;
2143 $schema['content fields'][] = $field['field_name'];
2149 * Checks if an index exists.
2151 * @todo: May we remove this funcion when implemented by Drupal core itself?
2152 * @link http://drupal.org/node/360854
2153 * @link http://dev.mysql.com/doc/refman/5.0/en/extended-show.html
2156 * Name of the table.
2158 * Name of the index.
2160 * TRUE if the table exists. Otherwise FALSE.
2162 function content_db_index_exists($table, $name) {
2164 if ($db_type == 'mysql' || $db_type == 'mysqli') {
2165 if (version_compare(db_version(), '5.0.3') < 0) {
2166 // Earlier versions of MySQL don't support a WHERE clause for SHOW.
2167 $result = db_query('SHOW INDEX FROM {'.
$table .
'}');
2168 while ($row = db_fetch_array($result)) {
2169 if ($row['Key_name'] == $name) {
2175 return (bool
)db_result(db_query("SHOW INDEX FROM {".
$table .
"} WHERE key_name = '$name'"));
2177 elseif ($db_type == 'pgsql') {
2178 // Note that the index names in Schema API for PostgreSQL are prefixed by
2179 // the table name and suffixed by '_idx'.
2180 return (bool
)db_result(db_query("SELECT COUNT(indexname) FROM pg_indexes WHERE indexname = '{".
$table .
"}_{$name}_idx'"));
2186 * Helper function for determining the behavior of a field or a widget
2187 * with respect to a given operation. (currently used for field 'view',
2188 * and widget 'default values' and 'multiple values')
2191 * 'field' or 'widget'
2193 * the name of the operation ('view', 'default value'...)
2195 * The field array, including widget info.
2197 * CONTENT_CALLBACK_NONE - do nothing for this operation
2198 * CONTENT_CALLBACK_CUSTOM - use the module's callback function.
2199 * CONTENT_CALLBACK_DEFAULT - use content module default behavior
2202 function content_callback($entity, $op, $field) {
2205 $info = module_invoke($field['module'], "field_info");
2206 return isset($info[$field['type']]['callbacks'][$op]) ?
$info[$field['type']]['callbacks'][$op] : CONTENT_CALLBACK_DEFAULT
;
2209 $info = module_invoke($field['widget']['module'], "widget_info");
2210 return isset($info[$field['widget']['type']]['callbacks'][$op]) ?
$info[$field['widget']['type']]['callbacks'][$op] : CONTENT_CALLBACK_DEFAULT
;
2215 * Helper function for determining the handling of a field, widget or
2216 * formatter with respect to a given operation.
2218 * Currently used for widgets and formatters 'multiple values'.
2221 * 'field', 'widget' or 'formatter'
2223 * the name of the operation ('default values'...)
2225 * - if $entity is 'field' or 'widget': the field array,
2226 * including widget info.
2227 * - if $entity is 'formater': the formatter array.
2229 * CONTENT_HANDLE_CORE - the content module handles this operation.
2230 * CONTENT_HANDLE_MODULE - the implementing module handles this operation.
2232 function content_handle($entity, $op, $object) {
2235 $info = module_invoke($object['module'], "field_info");
2236 return isset($info[$object['type']][$op]) ?
$info[$object['type']][$op] : CONTENT_HANDLE_CORE
;
2239 $info = module_invoke($object['widget']['module'], "widget_info");
2240 return isset($info[$object['widget']['type']][$op]) ?
$info[$object['widget']['type']][$op] : CONTENT_HANDLE_CORE
;
2243 // Much simpler, formatters arrays *are* the 'formatter_info' itself.
2244 // We let content_handle deal with them only for code consistency.
2245 return isset($object[$op]) ?
$object[$op] : CONTENT_HANDLE_CORE
;
2250 * Helper function to return the correct default value for a field.
2257 * The value of the field in the node.
2259 * The default value for that field.
2261 function content_default_value(&$form, &$form_state, $field, $delta) {
2262 $widget_types = _content_widget_types();
2263 $module = $widget_types[$field['widget']['type']]['module'];
2265 $default_value = array();
2266 if (!empty($field['widget']['default_value_php'])) {
2268 $result = eval($field['widget']['default_value_php']);
2270 if (is_array($result)) {
2271 $default_value = $result;
2274 elseif (!empty($field['widget']['default_value'])) {
2275 $default_value = $field['widget']['default_value'];
2277 return (array) $default_value;
2281 * Determine whether the user has access to a given field.
2284 * The operation to be performed. Possible values:
2288 * The field on which the operation is to be performed.
2290 * (optional) The account to check, if not given use currently logged in user.
2292 * (optional) The node on which the operation is to be performed.
2294 * TRUE if the operation is allowed;
2295 * FALSE if the operation is denied.
2297 function content_access($op, $field, $account = NULL
, $node = NULL
) {
2300 if (is_null($account)) {
2303 // Check for valid field data.
2304 if (!isset($field['field_name'])) {
2307 $access = module_invoke_all('field_access', $op, $field, $account, $node);
2308 foreach ($access as
$value) {
2309 if ($value === FALSE
) {
2317 * Hide specified fields from the $content variable in node templates.
2319 function content_field_wrapper_post_render($content, $element) {
2320 $field = content_fields($element['#field_name'], $element['#type_name']);
2321 if (theme('content_exclude', $content, $field, $element['#context'])) {
2329 * 'Theme' function for a field's addition to $content.
2331 * Adapts the all-inclusive $content variable in node templates to allow
2332 * some field content to be excluded. This is a theme function, so it can be
2333 * overridden in different themes to produce different results.
2335 * The html for individual fields and groups are available in the
2336 * $FIELD_NAME_rendered and $GROUP_NAME_rendered variables.
2338 * This allows more flexibility in node templates : you can use custom markup
2339 * around a few specific fields, and print the rest of the node with $content.
2342 * The themed content for this field or group.
2345 * The field or group array for this item.
2346 * $object['#type_name'] holds the content type.
2347 * $object['#field_name'] holds the field name (if a field).
2348 * $object['#group_name'] holds the group name (if a group).
2349 * $object['display_settings'] holds the display settings
2350 * for all contexts, in an array like:
2351 * $object['display_settings'] => array(
2353 * 'format' => 'default',
2356 * 'teaser' => array(
2357 * 'format' => 'default',
2363 * The context for which the node is being rendered.
2364 * Can be one of the following values :
2367 * - NODE_BUILD_SEARCH_INDEX
2368 * - NODE_BUILD_SEARCH_RESULT
2370 * - NODE_BUILD_PRINT
2371 * - ... any other custom build mode exposed by 3rd party modules using
2372 * hook_content_build_modes().
2375 * Whether or not content is to be added to $content in this context.
2376 * Uses the value of the 'Exclude' checkbox for this field
2377 * as set on the Manage fields screen.
2379 function theme_content_exclude($content, $object, $context) {
2380 // The field may be missing info for $contexts added by modules
2381 // enabled after the field was last edited.
2382 if (empty($object['display_settings'])
2383 || empty($object['display_settings'][$context])
2384 || !is_array($object['display_settings'][$context])
2385 || empty($object['display_settings'][$context]['exclude'])) {
2394 * Theme preprocess function for field.tpl.php.
2396 * The $variables array contains the following arguments:
2403 * @see field.tpl.php
2405 * TODO : this should live in theme/theme.inc, but then the preprocessor
2406 * doesn't get called when the theme overrides the template. Bug in theme layer ?
2408 function template_preprocess_content_field(&$variables) {
2409 $element = $variables['element'];
2410 $field = content_fields($element['#field_name'], $element['#node']->type
);
2412 $variables['node'] = $element['#node'];
2413 $variables['field'] = $field;
2414 $variables['items'] = array();
2416 if ($element['#single']) {
2417 // Single value formatter.
2418 foreach (element_children($element['items']) as
$delta) {
2419 $variables['items'][$delta] = $element['items'][$delta]['#item'];
2420 // Use isset() to avoid undefined index message on #children when field values are empty.
2421 $variables['items'][$delta]['view'] = isset($element['items'][$delta]['#children']) ?
$element['items'][$delta]['#children'] : '';
2425 // Multiple values formatter.
2426 // We display the 'all items' output as $items[0], as if it was the
2427 // output of a single valued field.
2428 // Raw values are still exposed for all items.
2429 foreach (element_children($element['items']) as
$delta) {
2430 $variables['items'][$delta] = $element['items'][$delta]['#item'];
2432 $variables['items'][0]['view'] = $element['items']['#children'];
2435 $variables['teaser'] = $element['#teaser'];
2436 $variables['page'] = $element['#page'];
2438 $field_empty = TRUE
;
2440 foreach ($variables['items'] as
$delta => $item) {
2441 if (!isset($item['view']) || (empty($item['view']) && (string)$item['view'] !== '0')) {
2442 $variables['items'][$delta]['empty'] = TRUE
;
2445 $field_empty = FALSE
;
2446 $variables['items'][$delta]['empty'] = FALSE
;
2451 'field_type' => $field['type'],
2452 'field_name' => $field['field_name'],
2453 'field_type_css' => strtr($field['type'], '_', '-'),
2454 'field_name_css' => strtr($field['field_name'], '_', '-'),
2455 'label' => check_plain(t($field['widget']['label'])),
2456 'label_display' => $element['#label_display'],
2457 'field_empty' => $field_empty,
2458 'template_files' => array(
2460 'content-field-'.
$element['#field_name'],
2461 'content-field-'.
$element['#node']->type
,
2462 'content-field-'.
$element['#field_name'] .
'-'.
$element['#node']->type
,
2465 $variables = array_merge($variables, $additions);
2469 * Theme preprocess function for node.
2471 * - Adds $FIELD_NAME_rendered variables
2472 * containing the themed output for the whole field.
2473 * - Adds the formatted values in the 'view' key of the items.
2475 function content_preprocess_node(&$vars) {
2476 $additions = _content_field_invoke_default('preprocess_node', $vars['node']);
2477 $vars = array_merge($vars, $additions);
2481 * Debugging using hook_content_fieldapi.
2483 * @TODO remove later
2488 function content_content_fieldapi($op, $field) {
2489 if (module_exists('devel')) {
2496 * Implementation of hook_content_extra_fields.
2498 * Informations for non-CCK 'node fields' defined in core.
2500 function content_content_extra_fields($type_name) {
2501 $type = node_get_types('type', $type_name);
2504 if ($type->has_title
) {
2505 $extra['title'] = array(
2506 'label' => $type->title_label
,
2507 'description' => t('Node module form.'),
2511 if ($type->has_body
) {
2512 $extra['body_field'] = array(
2513 'label' => $type->body_label
,
2514 'description' => t('Node module form.'),
2519 $extra['revision_information'] = array(
2520 'label' => t('Revision information'),
2521 'description' => t('Node module form.'),
2524 $extra['author'] = array(
2525 'label' => t('Authoring information'),
2526 'description' => t('Node module form.'),
2529 $extra['options'] = array(
2530 'label' => t('Publishing options'),
2531 'description' => t('Node module form.'),
2534 if (module_exists('comment')) {
2535 $extra['comment_settings'] = array(
2536 'label' => t('Comment settings'),
2537 'description' => t('Comment module form.'),
2541 if (module_exists('locale') && variable_get("language_content_type_$type_name", 0)) {
2542 $extra['language'] = array(
2543 'label' => t('Language'),
2544 'description' => t('Locale module form.'),
2548 if (module_exists('translation') && translation_supported_type($type_name)) {
2549 $extra['translation'] = array(
2550 'label' => t('Translation settings'),
2551 'description' => t('Translation module form.'),
2555 if (module_exists('menu')) {
2556 $extra['menu'] = array(
2557 'label' => t('Menu settings'),
2558 'description' => t('Menu module form.'),
2562 if (module_exists('taxonomy') && taxonomy_get_vocabularies($type_name)) {
2563 $extra['taxonomy'] = array(
2564 'label' => t('Taxonomy'),
2565 'description' => t('Taxonomy module form.'),
2569 if (module_exists('book')) {
2570 $extra['book'] = array(
2571 'label' => t('Book'),
2572 'description' => t('Book module form.'),
2576 if (module_exists('path')) {
2577 $extra['path'] = array(
2578 'label' => t('Path settings'),
2579 'description' => t('Path module form.'),
2583 if ($type_name == 'poll' && module_exists('poll')) {
2584 $extra['title'] = array(
2585 'label' => t('Poll title'),
2586 'description' => t('Poll module title.'),
2589 $extra['choice_wrapper'] = array(
2590 'label' => t('Poll choices'),
2591 'description' => t('Poll module choices.'),
2594 $extra['settings'] = array(
2595 'label' => t('Poll settings'),
2596 'description' => t('Poll module settings.'),
2600 if (module_exists('upload') && variable_get("upload_$type_name", TRUE
)) {
2601 $extra['attachments'] = array(
2602 'label' => t('File attachments'),
2603 'description' => t('Upload module form.'),
2613 * Retrieve the user-defined weight for non-CCK node 'fields'.
2615 * CCK's 'Manage fields' page lets users reorder node fields, including non-CCK
2616 * items (body, taxonomy, other hook_nodeapi-added elements by contrib modules...).
2617 * Contrib modules that want to have their 'fields' supported need to expose
2618 * them with hook_content_extra_fields, and use this function to retrieve the
2619 * user-defined weight.
2622 * The content type name.
2623 * @param $pseudo_field_name
2624 * The name of the 'field'.
2626 * The weight for the 'field', respecting the user settings stored
2627 * by content.module.
2629 function content_extra_field_weight($type_name, $pseudo_field_name) {
2630 $type = content_types($type_name);
2632 // If we don't have the requested item, this may be because the cached
2633 // information for 'extra' fields hasn't been refreshed yet.
2634 if (!isset($type['extra'][$pseudo_field_name])) {
2635 content_clear_type_cache();
2636 $type = content_types($type_name);
2639 if (isset($type['extra'][$pseudo_field_name])) {
2640 return $type['extra'][$pseudo_field_name]['weight'];
2645 * Find max delta value actually in use for a field.
2647 * Helper function to do things like tell when we should prevent a
2648 * change in multiple value settings that would result in data loss,
2649 * or know if content actually exists for a field.
2651 * @param $field_name
2652 * The field name to examine.
2654 * If provided, search only for existing data in that type,
2655 * otherwise search for all instances of field data in all types.
2657 * NULL if field is not in use, or the maximum delta value in use.
2660 * Go back to the field settings validation and use this function
2661 * to prevent (or confirm) changes in multiple values that
2662 * would destroy data.
2664 * Fields with only NULL data will show up as being in use.
2665 * Do we want to eliminate them from the results?
2667 function content_max_delta($field_name, $type_name = NULL
) {
2668 $fields = content_fields();
2669 $field = $fields[$field_name];
2671 // Non-multiple value fields don't use the delta column,
2672 // but could exist in multiple databases. If any value
2673 // exists in any examined table, the max delta will be zero.
2674 if (empty($field['multiple'])) {
2675 $content_types = content_types();
2676 foreach ($content_types as
$content_type) {
2677 if (empty($type_name) || $content_type['type'] == $type_name) {
2678 foreach ($content_type['fields'] as
$field) {
2679 $db_info = content_database_info($field);
2680 if (db_result(db_query("SELECT COUNT(*) FROM {".
$db_info['table'] .
"}")) >= 1) {
2687 // Multiple value fields always share the same table and use the delta.
2688 // If we want to find delta values for a particular type, we join
2689 // in the node table to limit the type.
2691 $db_info = content_database_info($field);
2692 if (!empty($type_name)) {
2693 $delta = db_result(db_query("SELECT MAX(delta) FROM {".
$db_info['table'] .
"} f LEFT JOIN {node} n ON f.vid = n.vid WHERE n.type = '%s'", $type_name));
2696 $delta = db_result(db_query("SELECT MAX(delta) FROM {".
$db_info['table'] .
"}"));
2702 // If we got this far, there is no data for this field.
2707 * Helper function to identify inactive fields.
2709 function content_inactive_fields($type_name = NULL
) {
2710 module_load_include('inc', 'content', 'includes/content.crud');
2711 if (!empty($type_name)) {
2712 $param = array('type_name' => $type_name);
2713 $inactive = array($type_name => array());
2717 $inactive = array();
2719 $all = content_field_instance_read($param, TRUE
);
2720 $active = array_keys(content_fields());
2721 foreach ($all as
$field) {
2722 if (!in_array($field['field_name'], $active)) {
2723 $inactive[$field['type_name']][$field['field_name']] = content_field_instance_expand($field);
2726 if (!empty($type_name)) {
2727 return $inactive[$type_name];
2733 * Helper function to identify inactive instances.
2734 * This will be the same results as content_inactive_fields(),
2735 * EXCEPT that his function will return inactive instances even
2736 * if the fields have other (shared) instances that are still active.
2738 function content_inactive_instances($type_name = NULL
) {
2739 module_load_include('inc', 'content', 'includes/content.crud');
2740 if (!empty($type_name)) {
2741 $param = array('type_name' => $type_name);
2742 $inactive = array($type_name => array());
2746 $inactive = array();
2748 $all = content_field_instance_read($param, TRUE
);
2749 foreach ($all as
$field) {
2750 $inactive[$field['type_name']][$field['field_name']] = content_field_instance_expand($field);
2752 if (!empty($type_name)) {
2753 return $inactive[$type_name];
2759 * Return the nested form elements for a field by name.
2760 * This can be used either to retrieve the entire sub-element
2761 * for a field by name, no matter how deeply nested it is within
2762 * fieldgroups or multigroups, or to find the multiple value
2763 * sub-elements within a field element by name (i.e. 'value' or
2764 * 'nid'). You can also use this function to see if an item exists
2765 * in a form (the return will be an empty array if it does not exist).
2767 * A field/group will generally only exist once in a form but the
2768 * function can also be used to locate all the 'value' elements
2769 * within a multiple value field if you pass the multiple value field
2770 * as the $form argument;
2772 * For example, for a field named field_custom, the following will
2773 * pluck out the form element for this field from the node form,
2774 * no matter how deeply it is nested within fieldgroups or fieldsets:
2776 * $element = array_shift(content_get_nested_elements($node_form, 'field_custom'));
2778 * You can prefix the function with '&' to retrieve the element by
2779 * reference to alter it directly:
2781 * $elements = &content_get_nested_elements($form, 'field_custom');
2782 * foreach ($elements as $element) {
2783 * $element['#after_build'][] = 'my_field_afterbuild';
2786 * During the #after_build you could then do something like the
2787 * following to alter each individual part of a multiple value field:
2789 * $sub_elements = &content_get_nested_elements($element, 'value', 'all');
2790 * foreach ($sub_elements as $sub_element) {
2791 * $sub_element['#element_validate'][] = 'custom_validation';
2795 * The form array to search.
2796 * @param $field_name
2797 * The name or key of the form elements to return. Can be a field name, a group name, or a sub-field.
2799 * An array of all matching form elements, returned by reference.
2801 function &content_get_nested_elements(&$form, $field_name) {
2802 $elements = array();
2803 foreach (element_children($form) as
$key) {
2804 if ($key === $field_name) {
2805 $elements[] = &$form[$key];
2807 else if (is_array($form[$key])) {
2808 $nested_form = &$form[$key];
2809 if ($sub_elements = &content_get_nested_elements($nested_form, $field_name)) {
2810 $elements = array_merge($elements, $sub_elements);
2818 * Helper function to set a value in a form element,
2819 * no matter how deeply nested it is in the form.
2822 * The form array to search.
2823 * @param $field_name
2824 * The name or key of the form elements to return. Can be a field name, a group name, or a sub-field.
2826 * The value to set the element to. Should be an array that matches the part of the form that is being altered.
2828 * TRUE or FALSE, depending on whether the element was discovered and set.
2830 function content_set_nested_elements(&$form, $field_name, $value) {
2832 $elements = &content_get_nested_elements($form, $field_name);
2833 if (!empty($elements)) {
2834 foreach ($elements as
&$element) {