Simpletest Coverage - modules/field/field.module

1 <?php
2 // $Id: field.module,v 1.22 2009/08/11 14:59:40 dries Exp $
3 /**
4 * @file
5 * Attach custom data fields to Drupal objects.
6 */
7
8 /*
9 * Load all public Field API functions. Drupal currently has no
10 * mechanism for auto-loading core APIs, so we have to load them on
11 * every page request.
12 */
13 require(DRUPAL_ROOT . '/modules/field/field.crud.inc');
14 require(DRUPAL_ROOT . '/modules/field/field.info.inc');
15 require(DRUPAL_ROOT . '/modules/field/field.attach.inc');
16
17 /**
18 * @defgroup field Field API
19 * @{
20 * Attach custom data fields to Drupal objects.
21 *
22 * The Field API allows custom data fields to be attached to Drupal
23 * objects and takes care of storing, loading, editing, and rendering
24 * field data. Any object type (node, user, etc.) can use the Field
25 * API to make itself "fieldable" and thus allow fields to be attached
26 * to it. Other modules can provide a user interface for managing custom
27 * fields via a web browser as well as a wide and flexible variety of
28 * data type, form element, and display format capabilities.
29 *
30 * - @link field_structs Data structures: Field, Instance, Bundle @endlink.
31 *
32 * - @link field_types Field Types API @endlink. Defines field types,
33 * widget types, and display formatters. Field modules use this API
34 * to provide field types like Text and Node Reference along with the
35 * associated form elements and display formatters.
36 *
37 * - @link field_crud Field CRUD API @endlink. Create, updates, and
38 * deletes fields, bundles (a.k.a. "content types"), and instances.
39 * Modules use this API, often in hook_install(), to create
40 * custom data structures. The Content Construction Kit
41 * user-interface module uses this API for its major functionality.
42 *
43 * - @link field_attach Field Attach API @endlink. Connects object
44 * types to the Field API. Field Attach API functions load, store,
45 * generate Form API structures, display, and perform a vareity of
46 * other functions for field data connected to individual objects.
47 * Fieldable object types like node and user use this API to make
48 * themselves fieldable.
49 *
50 * - @link field_info Field Info API @endlink. Exposes information
51 * about all fields, instances, widgets, and related information
52 * defined by or with the Field API.
53 *
54 * - @link field_storage Field Storage API @endlink. Provides a
55 * pluggable back-end storage system for actual field data. The
56 * default implementation, field_sql_storage.module, stores field data
57 * in the local SQL database.
58
59 * - @link field_purge Field API bulk data deletion @endlink. Cleans
60 * up after bulk deletion operations such as field_delete_field()
61 * and field_delete_instance().
62 */
63
64 /**
65 * Value for $field['cardinality'] property to indicate it can hold an
66 * unlimited number of values.
67 */
68 define('FIELD_CARDINALITY_UNLIMITED', -1);
69
70 /**
71 * TODO
72 */
73 define('FIELD_BEHAVIOR_NONE', 0x0001);
74 /**
75 * TODO
76 */
77 define('FIELD_BEHAVIOR_DEFAULT', 0x0002);
78 /**
79 * TODO
80 */
81 define('FIELD_BEHAVIOR_CUSTOM', 0x0004);
82
83 /**
84 * Age argument for loading the most recent version of an object's
85 * field data with field_attach_load().
86 */
87 define('FIELD_LOAD_CURRENT', 'FIELD_LOAD_CURRENT');
88 /**
89 * Age argument for loading the version of an object's field data
90 * specified in the object with field_attach_load().
91 */
92 define('FIELD_LOAD_REVISION', 'FIELD_LOAD_REVISION');
93
94 /**
95 * @name Field query flags
96 * @{
97 * Flags for field_attach_query().
98 */
99
100 /**
101 * Limit argument for field_attach_query() to request all available
102 * objects instead of a limited number.
103 */
104 define('FIELD_QUERY_NO_LIMIT', 'FIELD_QUERY_NO_LIMIT');
105
106 /**
107 * Cursor return value for field_attach_query() to indicate that no
108 * more data is available.
109 */
110 define('FIELD_QUERY_COMPLETE', 'FIELD_QUERY_COMPLETE');
111
112 /**
113 * @} End of "Field query flags".
114 */
115
116 /**
117 * Base class for all exceptions thrown by Field API functions.
118 *
119 * This class has no functionality of its own other than allowing all
120 * Field API exceptions to be caught by a single catch block.
121 */
122 class FieldException extends Exception {}
123
124 /**
125 * Implement hook_flush_caches.
126 */
127 function field_flush_caches() {
128 return array('cache_field');
129 }
130
131 /**
132 * Implement hook_help().
133 */
134 function field_help($path, $arg) {
135 switch ($path) {
136 case 'admin/help#field':
137 $output = '<p>' . t('The Field API allows custom data fields to be attached to Drupal objects and takes care of storing, loading, editing, and rendering field data. Any object type (node, user, etc.) can use the Field API to make itself "fieldable" and thus allow fields to be attached to it.') . '</p>';
138 $output .= '<p>' . t('The Field API provides no user interface on its own. Use the Content Construction Kit (CCK) contrib module to manage custom fields via a web browser.') . '</p>';
139 return $output;
140 }
141 }
142
143 /**
144 * Implement hook_init().
145 */
146 function field_init() {
147 drupal_add_css(drupal_get_path('module', 'field') . '/theme/field.css');
148 }
149
150 /**
151 * Implement hook_menu().
152 */
153 function field_menu() {
154 $items = array();
155
156 // Callback for AHAH add more buttons.
157 $items['field/js_add_more'] = array(
158 'page callback' => 'field_add_more_js',
159 'access arguments' => array('access content'),
160 'type' => MENU_CALLBACK,
161 );
162
163 return $items;
164 }
165
166 /**
167 * Implement hook_theme().
168 */
169 function field_theme() {
170 $path = drupal_get_path('module', 'field') . '/theme';
171
172 return array(
173 'field' => array(
174 'template' => 'field',
175 'arguments' => array('element' => NULL),
176 'path' => $path,
177 ),
178 'field_multiple_value_form' => array(
179 'arguments' => array('element' => NULL),
180 ),
181 );
182 }
183
184 /**
185 * Implement hook_cron().
186 *
187 * Purges some deleted Field API data, if any exists.
188 */
189 function field_cron() {
190 $limit = variable_get('field_purge_batch_size', 10);
191 field_purge_batch($limit);
192 }
193
194 /**
195 * Implement hook_modules_installed().
196 */
197 function field_modules_installed($modules) {
198 field_cache_clear();
199 }
200
201 /**
202 * Implement hook_modules_uninstalled().
203 */
204 function field_modules_uninstalled($modules) {
205 module_load_include('inc', 'field', 'field.crud');
206 foreach ($modules as $module) {
207 // TODO D7: field_module_delete is not yet implemented
208 // field_module_delete($module);
209 }
210 }
211
212 /**
213 * Implement hook_modules_enabled().
214 */
215 function field_modules_enabled($modules) {
216 foreach ($modules as $module) {
217 field_associate_fields($module);
218 }
219 field_cache_clear();
220 }
221
222 /**
223 * Implement hook_modules_disabled().
224 */
225 function field_modules_disabled($modules) {
226 foreach ($modules as $module) {
227 db_update('field_config')
228 ->fields(array('active' => 0))
229 ->condition('module', $module)
230 ->execute();
231 db_update('field_config_instance')
232 ->fields(array('widget_active' => 0))
233 ->condition('widget_module', $module)
234 ->execute();
235 field_cache_clear(TRUE);
236 }
237 }
238
239 /**
240 * Allows a module to update the database for fields and columns it controls.
241 *
242 * @param string $module
243 * The name of the module to update on.
244 */
245 function field_associate_fields($module) {
246 $module_fields = module_invoke($module, 'field_info');
247 if ($module_fields) {
248 foreach ($module_fields as $name => $field_info) {
249 watchdog('field', 'Updating field type %type with module %module.', array('%type' => $name, '%module' => $module));
250 db_update('field_config')
251 ->fields(array('module' => $module, 'active' => 1))
252 ->condition('type', $name)
253 ->execute();
254 }
255 }
256 $module_widgets = module_invoke($module, 'widget_info');
257 if ($module_widgets) {
258 foreach ($module_widgets as $name => $widget_info) {
259 watchdog('field', 'Updating widget type %type with module %module.', array('%type' => $name, '%module' => $module));
260 db_update('field_config_instance')
261 ->fields(array('widget_module' => $module, 'widget_active' => 1))
262 ->condition('widget_type', $name)
263 ->execute();
264 }
265 }
266 }
267
268 /**
269 * Helper function to filter out empty values.
270 *
271 * On order to keep marker rows in the database, the function ensures
272 * that the right number of 'all columns NULL' values is kept.
273 *
274 * @param array $field
275 * @param array $items
276 * @return array
277 * returns filtered and adjusted item array
278 *
279 * TODO D7: poorly named...
280 */
281 function field_set_empty($field, $items) {
282 // Filter out empty values.
283 $filtered = array();
284 $function = $field['module'] . '_field_is_empty';
285 foreach ((array) $items as $delta => $item) {
286 if (!$function($item, $field)) {
287 $filtered[] = $item;
288 }
289 }
290 return $filtered;
291 }
292
293 /**
294 * Helper function to sort items in a field according to
295 * user drag-n-drop reordering.
296 */
297 function _field_sort_items($field, $items) {
298 if (($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) && isset($items[0]['_weight'])) {
299 usort($items, '_field_sort_items_helper');
300 foreach ($items as $delta => $item) {
301 if (is_array($items[$delta])) {
302 unset($items[$delta]['_weight']);
303 }
304 }
305 }
306 return $items;
307 }
308
309 /**
310 * Sort function for items order.
311 * (copied form element_sort(), which acts on #weight keys)
312 */
313 function _field_sort_items_helper($a, $b) {
314 $a_weight = (is_array($a) && isset($a['_weight'])) ? $a['_weight'] : 0;
315 $b_weight = (is_array($b) && isset($b['_weight'])) ? $b['_weight'] : 0;
316 if ($a_weight == $b_weight) {
317 return 0;
318 }
319 return ($a_weight < $b_weight) ? -1 : 1;
320 }
321
322 /**
323 * Same as above, using ['_weight']['#value']
324 */
325 function _field_sort_items_value_helper($a, $b) {
326 $a_weight = (is_array($a) && isset($a['_weight']['#value'])) ? $a['_weight']['#value'] : 0;
327 $b_weight = (is_array($b) && isset($b['_weight']['#value'])) ? $b['_weight']['#value'] : 0;
328 if ($a_weight == $b_weight) {
329 return 0;
330 }
331 return ($a_weight < $b_weight) ? -1 : 1;
332 }
333
334 /**
335 * Registry of available build modes.
336 */
337 function field_build_modes($obj_type) {
338 static $info;
339
340 if (!isset($info[$obj_type])) {
341 $info[$obj_type] = module_invoke_all('field_build_modes', $obj_type);
342 }
343 return $info[$obj_type];
344 }
345
346 /**
347 * Clear the cached information; called in several places when field
348 * information is changed.
349 */
350 function field_cache_clear($rebuild_schema = FALSE) {
351 cache_clear_all('*', 'cache_field', TRUE);
352
353 module_load_include('inc', 'field', 'field.info');
354 _field_info_cache_clear();
355
356 // Refresh the schema to pick up new information.
357 // TODO : if db storage gets abstracted out, we'll need to revisit how and when
358 // we refresh the schema...
359 if ($rebuild_schema) {
360 $schema = drupal_get_schema(NULL, TRUE);
361 }
362 }
363
364 /**
365 * Like filter_xss_admin(), but with a shorter list of allowed tags.
366 *
367 * Used for items entered by administrators, like field descriptions,
368 * allowed values, where some (mainly inline) mark-up may be desired
369 * (so check_plain() is not acceptable).
370 */
371 function field_filter_xss($string) {
372 return filter_xss($string, _field_filter_xss_allowed_tags());
373 }
374
375 /**
376 * List of tags allowed by field_filter_xss().
377 */
378 function _field_filter_xss_allowed_tags() {
379 return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img');
380 }
381
382 /**
383 * Human-readable list of allowed tags, for display in help texts.
384 */
385 function _field_filter_xss_display_allowed_tags() {
386 return '<' . implode('> <', _field_filter_xss_allowed_tags()) . '>';
387 }
388
389 /**
390 * Format a field item for display.
391 *
392 * TODO D7 : do we still need field_format ?
393 * - backwards compatibility of templates - check what fallbacks we can propose...
394 * - used by Views integration in CCK D6
395 * At least needs a little rehaul/update...
396 *
397 * Used to display a field's values outside the context of the $node, as
398 * when fields are displayed in Views, or to display a field in a template
399 * using a different formatter than the one set up on the Display Fields tab
400 * for the node's context.
401 *
402 * @param $field
403 * Either a field array or the name of the field.
404 * @param $item
405 * The field item(s) to be formatted (such as $node->field_foo[0],
406 * or $node->field_foo if the formatter handles multiple values itself)
407 * @param $formatter_type
408 * The name of the formatter type to use.
409 * @param $node
410 * Optionally, the containing node object for context purposes and
411 * field-instance options.
412 *
413 * @return
414 * A string containing the contents of the field item(s) sanitized for display.
415 * It will have been passed through the necessary check_plain() or check_markup()
416 * functions as necessary.
417 */
418 function field_format($obj_type, $object, $field, $item, $formatter_type = NULL, $formatter_settings = array()) {
419 if (!is_array($field)) {
420 $field = field_info_field($field);
421 }
422
423 if (field_access('view', $field)) {
424 $field_type = field_info_field_types($field['type']);
425
426 // We need $field, $instance, $obj_type, $object to be able to display a value...
427 list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
428 $instance = field_info_instance($field['field_name'], $bundle);
429
430 $display = array(
431 'type' => $formatter_type ? $formatter_type : $field_type['default_formatter'],
432 'settings' => $formatter_settings,
433 );
434 $display['settings'] += field_info_formatter_settings($display['type']);
435
436 if ($display['type'] !== 'hidden') {
437 $theme = $formatter['module'] . '_formatter_' . $display['type'];
438
439 $element = array(
440 '#theme' => $theme,
441 '#field_name' => $field['field_name'],
442 '#bundle' => $bundle,
443 '#formatter' => $display['type'],
444 '#settings' => $display['settings'],
445 '#object' => $object,
446 '#object_type' => $obj_type,
447 '#delta' => isset($item['#delta']) ? $item['#delta'] : NULL,
448 );
449
450 if (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT) {
451 // Single value formatter.
452
453 // hook_field('sanitize') expects an array of items, so we build one.
454 $items = array($item);
455 $function = $field['module'] . '_field_sanitize';
456 if (function_exists($function)) {
457 $function($obj_type, $object, $field, $instance, $items);
458 }
459
460 $element['#item'] = $items[0];
461 }
462 else {
463 // Multiple values formatter.
464 $items = $item;
465 $function = $field['module'] . '_field_sanitize';
466 if (function_exists($function)) {
467 $function($obj_type, $object, $field, $instance, $items);
468 }
469
470 foreach ($items as $delta => $item) {
471 $element[$delta] = array(
472 '#item' => $item,
473 '#weight' => $delta,
474 );
475 }
476 }
477
478 return theme($theme, $element);
479 }
480 }
481 }
482
483 /**
484 * Return a single field, fully themed with label and multiple values.
485 *
486 * To be used by third-party code (Views, Panels...) that needs to output
487 * an isolated field. Do *not* use inside node templates, use
488 * render($content[FIELD_NAME]) instead.
489 *
490 * The field will be displayed using the display options (label display,
491 * formatter settings...) specified in the $instance structure for the given
492 * build mode: $instance['display'][$build_mode].
493 *
494 * @param $object
495 * The object containing the field to display. Must at least contain the id key,
496 * revision key (if applicable), bundle key, and the field data.
497 * @param $field
498 * The field structure.
499 * @param $instance
500 * The instance structure for $field on $object's bundle.
501 * @param $build_mode
502 * Build mode, e.g. 'full', 'teaser'...
503 * @return
504 * The themed output for the field.
505 */
506 function field_view_field($obj_type, $object, $field, $instance, $build_mode = 'full') {
507 $output = '';
508 if (isset($object->$field['field_name'])) {
509 $items = $object->$field['field_name'];
510
511 // One-field equivalent to _field_invoke('sanitize').
512 $function = $field['module'] . '_field_sanitize';
513 if (drupal_function_exists($function)) {
514 $function($obj_type, $object, $field, $instance, $items);
515 $object->$field['field_name'] = $items;
516 }
517
518 $view = field_default_view($obj_type, $object, $field, $instance, $items, $build_mode);
519 // TODO : what about hook_field_attach_view ?
520
521 $output = $view[$field['field_name']];
522 }
523 return $output;
524 }
525
526 /**
527 * Determine whether the user has access to a given field.
528 *
529 * @param $op
530 * The operation to be performed. Possible values:
531 * - "edit"
532 * - "view"
533 * @param $field
534 * The field on which the operation is to be performed.
535 * @param $account
536 * (optional) The account to check, if not given use currently logged in user.
537 * @return
538 * TRUE if the operation is allowed;
539 * FALSE if the operation is denied.
540 */
541 function field_access($op, $field, $account = NULL) {
542 global $user;
543
544 if (is_null($account)) {
545 $account = $user;
546 }
547
548 $field_access = module_invoke_all('field_access', $op, $field, $account);
549 foreach ($field_access as $value) {
550 if ($value === FALSE) {
551 return FALSE;
552 }
553 }
554 return TRUE;
555 }
556
557 /**
558 * Theme preprocess function for field.tpl.php.
559 *
560 * The $variables array contains the following arguments:
561 * - $object
562 * - $field
563 * - $items
564 * - $teaser
565 * - $page
566 *
567 * @see field.tpl.php
568 */
569 function template_preprocess_field(&$variables) {
570 $element = $variables['element'];
571 list(, , $bundle) = field_attach_extract_ids($element['#object_type'], $element['#object']);
572 $instance = field_info_instance($element['#field_name'], $bundle);
573 $field = field_info_field($element['#field_name']);
574
575 $variables['object'] = $element['#object'];
576 $variables['field'] = $field;
577 $variables['instance'] = $instance;
578 $variables['items'] = array();
579
580 if ($element['#single']) {
581 // Single value formatter.
582 foreach (element_children($element['items']) as $delta) {
583 $variables['items'][$delta] = $element['items'][$delta]['#item'];
584 $variables['items'][$delta]['view'] = drupal_render_children($element['items'], array($delta));
585 }
586 }
587 else {
588 // Multiple values formatter.
589 // We display the 'all items' output as $items[0], as if it was the
590 // output of a single valued field.
591 // Raw values are still exposed for all items.
592 foreach (element_children($element['items']) as $delta) {
593 $variables['items'][$delta] = $element['items'][$delta]['#item'];
594 }
595 $variables['items'][0]['view'] = drupal_render_children($element, array('items'));
596 }
597
598 $variables['build_mode'] = $element['#build_mode'];
599 $variables['page'] = (bool)menu_get_object();
600
601 $field_empty = TRUE;
602
603 foreach ($variables['items'] as $delta => $item) {
604 if (!isset($item['view']) || (empty($item['view']) && (string)$item['view'] !== '0')) {
605 $variables['items'][$delta]['empty'] = TRUE;
606 }
607 else {
608 $field_empty = FALSE;
609 $variables['items'][$delta]['empty'] = FALSE;
610 }
611 }
612
613 $additions = array(
614 'field_type' => $field['type'],
615 'field_name' => $field['field_name'],
616 'field_type_css' => strtr($field['type'], '_', '-'),
617 'field_name_css' => strtr($field['field_name'], '_', '-'),
618 'label' => check_plain(t($instance['label'])),
619 'label_display' => $element['#label_display'],
620 'field_empty' => $field_empty,
621 'template_files' => array(
622 'field',
623 'field-' . $element['#field_name'],
624 'field-' . $bundle,
625 'field-' . $element['#field_name'] . '-' . $bundle,
626 ),
627 );
628 $variables = array_merge($variables, $additions);
629 }
630
631 /**
632 * @} End of "defgroup field"
633 */

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.