diff options
Diffstat (limited to 'modules/field')
-rw-r--r-- | modules/field/field.api.php | 30 | ||||
-rw-r--r-- | modules/field/field.attach.inc | 18 | ||||
-rw-r--r-- | modules/field/field.crud.inc | 18 | ||||
-rw-r--r-- | modules/field/field.info.inc | 11 | ||||
-rw-r--r-- | modules/field/field.multilingual.inc | 3 | ||||
-rw-r--r-- | modules/field/tests/field.test | 120 | ||||
-rw-r--r-- | modules/field/tests/field_test.entity.inc | 4 | ||||
-rw-r--r-- | modules/field/tests/field_test.field.inc | 40 | ||||
-rw-r--r-- | modules/field/tests/field_test.module | 59 |
9 files changed, 220 insertions, 83 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php index 74eae62ab..329cf16d1 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -1,7 +1,7 @@ <?php /** - * @ingroup field_fieldable_type + * @ingroup hooks * @{ */ @@ -74,17 +74,13 @@ function hook_field_extra_fields() { function hook_field_extra_fields_alter(&$info) { // Force node title to always be at the top of the list by default. foreach (node_type_get_types() as $bundle) { - if (isset($info['node'][$bundle->type]['title'])) { - $info['node'][$bundle->type]['title']['weight'] = -20; + if (isset($info['node'][$bundle->type]['form']['title'])) { + $info['node'][$bundle->type]['form']['title']['weight'] = -20; } } } /** - * @} End of "ingroup field_fieldable_type" - */ - -/** * @defgroup field_types Field Types API * @{ * Define field types, widget types, display formatter types, storage types. @@ -119,6 +115,9 @@ function hook_field_extra_fields_alter(&$info) { * * A third kind of pluggable handlers, storage backends, is defined by the * @link field_storage Field Storage API @endlink. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -706,6 +705,7 @@ function hook_field_is_empty($item, $field) { * @see hook_field_widget_form_alter() * @see hook_field_widget_WIDGET_TYPE_form_alter() * @see hook_field_widget_error() + * @see hook_field_widget_settings_form() */ function hook_field_widget_info() { return array( @@ -1505,10 +1505,6 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) { * @} End of "ingroup field_attach" */ -/********************************************************************** - * Field Storage API - **********************************************************************/ - /** * @ingroup field_storage * @{ @@ -2346,10 +2342,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) { * @} End of "ingroup field_storage" */ -/********************************************************************** - * Field CRUD API - **********************************************************************/ - /** * @ingroup field_crud * @{ @@ -2603,10 +2595,6 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) { * @} End of "ingroup field_crud" */ -/********************************************************************** - * TODO: I'm not sure where these belong yet. - **********************************************************************/ - /** * Determine whether the user has access to a given field. * @@ -2633,3 +2621,7 @@ function hook_field_access($op, $field, $entity_type, $entity, $account) { } return TRUE; } + +/** + * @} End of "ingroup hooks" + */ diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 2419201de..bd2934b48 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -44,6 +44,9 @@ class FieldValidationException extends FieldException { * * Each field defines which storage backend it uses. The Drupal system variable * 'field_storage_default' identifies the storage backend used by default. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -117,6 +120,12 @@ define('FIELD_STORAGE_INSERT', 'insert'); * The pre-operation hooks do not make the Field Storage API irrelevant. The * Field Storage API is essentially the "fallback mechanism" for any fields * that aren't being intercepted explicitly by pre-operation hooks. + * + * @link field_language Field Language API @endlink provides information about + * the structure of field objects. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -185,8 +194,10 @@ function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $opti // Iterate through the instances and collect results. $return = array(); foreach ($instances as $instance) { - $field_name = $instance['field_name']; - $field = field_info_field($field_name); + // field_info_field() is not available for deleted fields, so use + // field_info_field_by_id(). + $field = field_info_field_by_id($instance['field_id']); + $field_name = $field['field_name']; $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op; if (function_exists($function)) { // Determine the list of languages to iterate on. @@ -694,7 +705,8 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $ } // Invoke field-type module's hook_field_load(). - _field_invoke_multiple('load', $entity_type, $queried_entities, $age, $options); + $null = NULL; + _field_invoke_multiple('load', $entity_type, $queried_entities, $age, $null, $options); // Invoke hook_field_attach_load(): let other modules act on loading the // entitiy. diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index e34c0c528..6df32352b 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -15,6 +15,9 @@ * * The Field CRUD API uses * @link field Field API data structures @endlink. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -42,10 +45,11 @@ * system variable. * - settings: each omitted setting is given the default value specified in * hook_field_storage_info(). + * * @return * The $field array with the id property filled in. - * @throw - * FieldException + * + * @throws FieldException * * See: @link field Field API data structures @endlink. */ @@ -442,8 +446,8 @@ function field_delete_field($field_name) { * * @return * The $instance array with the id property filled in. - * @throw - * FieldException + * + * @throws FieldException * * See: @link field Field API data structures @endlink. */ @@ -510,8 +514,7 @@ function field_create_instance($instance) { * properties specified in $instance overwrite the existing values for * the instance. * - * @throw - * FieldException + * @throws FieldException * * @see field_create_instance() */ @@ -824,6 +827,9 @@ function field_delete_instance($instance, $field_cleanup = TRUE) { * ), * ); * @endcode + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 6b172dd34..3b36c0355 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -13,6 +13,9 @@ * The Field Info API exposes information about field types, fields, * instances, bundles, widget types, display formatters, behaviors, * and settings defined by or with the Field API. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -24,6 +27,7 @@ */ function field_info_cache_clear() { drupal_static_reset('field_view_mode_settings'); + drupal_static_reset('field_available_languages'); // @todo: Remove this when field_attach_*_bundle() bundle management // functions are moved to the entity API. @@ -602,8 +606,9 @@ function field_info_fields() { * * @param $field_name * The name of the field to retrieve. $field_name can only refer to a - * non-deleted, active field. Use field_read_fields() to retrieve information - * on deleted or inactive fields. + * non-deleted, active field. For deleted fields, use + * field_info_field_by_id(). To retrieve information about inactive fields, + * use field_read_fields(). * * @return * The field array, as returned by field_read_fields(), with an @@ -624,7 +629,7 @@ function field_info_field($field_name) { * * @param $field_id * The id of the field to retrieve. $field_id can refer to a - * deleted field. + * deleted field, but not an inactive one. * * @return * The field array, as returned by field_read_fields(), with an diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index 5373d9708..4b4f01d7d 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -57,6 +57,9 @@ * even disabled by modules implementing hook_field_language_alter(), making * it possible to choose the first approach. The display language for each * field is returned by field_language(). + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 669fc37cf..657f1f364 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -72,12 +72,13 @@ class FieldTestCase extends DrupalWebTestCase { } class FieldAttachTestCase extends FieldTestCase { - function setUp($modules = array()) { + function setUp() { // Since this is a base class for many test cases, support the same // flexibility that DrupalWebTestCase::setUp() has for the modules to be // passed in as either an array or a variable number of string arguments. - if (!is_array($modules)) { - $modules = func_get_args(); + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; } if (!in_array('field_test', $modules)) { $modules[] = 'field_test'; @@ -2674,7 +2675,7 @@ class FieldTranslationsTestCase extends FieldTestCase { // Test field_available_languages() behavior for untranslatable fields. $this->field['translatable'] = FALSE; - $this->field_name = $this->field['field_name'] = $this->instance['field_name'] = drupal_strtolower($this->randomName() . '_field_name'); + field_update_field($this->field); $available_languages = field_available_languages($this->entity_type, $this->field); $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE, t('For untranslatable fields only LANGUAGE_NONE is available.')); } @@ -2989,22 +2990,54 @@ class FieldBulkDeleteTestCase extends FieldTestCase { */ function _generateStubEntities($entity_type, $entities, $field_name = NULL) { $stubs = array(); - foreach ($entities as $entity) { + foreach ($entities as $id => $entity) { $stub = entity_create_stub_entity($entity_type, entity_extract_ids($entity_type, $entity)); if (isset($field_name)) { $stub->{$field_name} = $entity->{$field_name}; } - $stubs[] = $stub; + $stubs[$id] = $stub; } return $stubs; } + /** + * Tests that the expected hooks have been invoked on the expected entities. + * + * @param $expected_hooks + * An array keyed by hook name, with one entry per expected invocation. + * Each entry is the value of the "$entity" parameter the hook is expected + * to have been passed. + * @param $actual_hooks + * The array of actual hook invocations recorded by field_test_memorize(). + */ + function checkHooksInvocations($expected_hooks, $actual_hooks) { + foreach ($expected_hooks as $hook => $invocations) { + $actual_invocations = $actual_hooks[$hook]; + + // Check that the number of invocations is correct. + $this->assertEqual(count($actual_invocations), count($invocations), "$hook() was called the expected number of times."); + + // Check that the hook was called for each expected argument. + foreach ($invocations as $argument) { + $found = FALSE; + foreach ($actual_invocations as $actual_arguments) { + if ($actual_arguments[1] == $argument) { + $found = TRUE; + break; + } + } + $this->assertTrue($found, "$hook() was called on expected argument"); + } + } + } + function setUp() { parent::setUp('field_test'); - // Clean up data from previous test cases. $this->fields = array(); $this->instances = array(); + $this->entities = array(); + $this->entities_by_bundles = array(); // Create two bundles. $this->bundles = array('bb_1' => 'bb_1', 'bb_2' => 'bb_2'); @@ -3040,7 +3073,10 @@ class FieldBulkDeleteTestCase extends FieldTestCase { foreach ($this->fields as $field) { $entity->{$field['field_name']}[LANGUAGE_NONE] = $this->_generateTestFieldValues($field['cardinality']); } + $this->entities[$id] = $entity; + // Also keep track of the entities per bundle. + $this->entities_by_bundles[$bundle][$id] = $entity; field_attach_insert($this->entity_type, $entity); $id++; } @@ -3105,6 +3141,7 @@ class FieldBulkDeleteTestCase extends FieldTestCase { * instance is deleted. */ function testPurgeInstance() { + // Start recording hook invocations. field_test_memorize(); $bundle = reset($this->bundles); @@ -3119,7 +3156,7 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertEqual(count($mem), 0, 'No field hooks were called'); $batch_size = 2; - for ($count = 8; $count >= 0; $count -= 2) { + for ($count = 8; $count >= 0; $count -= $batch_size) { // Purge two entities. field_purge_batch($batch_size); @@ -3133,19 +3170,21 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertEqual($count ? count($found['test_entity']) : count($found), $count, 'Correct number of entities found after purging 2'); } - // hook_field_delete() was called on a pseudo-entity for each entity. Each - // pseudo entity has a $field property that matches the original entity, - // but no others. - $mem = field_test_memorize(); - $this->assertEqual(count($mem['field_test_field_delete']), 10, 'hook_field_delete was called for the right number of entities'); - $stubs = $this->_generateStubEntities($this->entity_type, $this->entities, $field['field_name']); - $count = count($stubs); - foreach ($mem['field_test_field_delete'] as $args) { - $entity = $args[1]; - $this->assertEqual($stubs[$entity->ftid], $entity, 'hook_field_delete() called with the correct stub'); - unset($stubs[$entity->ftid]); + // Check hooks invocations. + // - hook_field_load() (multiple hook) should have been called on all + // entities by pairs of two. + // - hook_field_delete() should have been called once for each entity in the + // bundle. + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + foreach (array_chunk($stubs, $batch_size, TRUE) as $chunk) { + $hooks['field_test_field_load'][] = $chunk; } - $this->assertEqual(count($stubs), $count-10, 'hook_field_delete was called with each entity once'); + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); // The instance still exists, deleted. $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1)); @@ -3168,15 +3207,37 @@ class FieldBulkDeleteTestCase extends FieldTestCase { * instances are deleted and purged. */ function testPurgeField() { + // Start recording hook invocations. + field_test_memorize(); + $field = reset($this->fields); // Delete the first instance. - $instance = field_info_instance($this->entity_type, $field['field_name'], 'bb_1'); + $bundle = reset($this->bundles); + $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); field_delete_instance($instance); + // Assert that hook_field_delete() was not called yet. + $mem = field_test_memorize(); + $this->assertEqual(count($mem), 0, 'No field hooks were called.'); + // Purge the data. field_purge_batch(10); + // Check hooks invocations. + // - hook_field_load() (multiple hook) should have been called once, for all + // entities in the bundle. + // - hook_field_delete() should have been called once for each entity in the + // bundle. + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + $hooks['field_test_field_load'][] = $stubs; + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); + // Purge again to purge the instance. field_purge_batch(0); @@ -3185,12 +3246,27 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertTrue(isset($fields[$field['id']]) && !$fields[$field['id']]['deleted'], 'The field exists and is not deleted'); // Delete the second instance. - $instance = field_info_instance($this->entity_type, $field['field_name'], 'bb_2'); + $bundle = next($this->bundles); + $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); field_delete_instance($instance); + // Assert that hook_field_delete() was not called yet. + $mem = field_test_memorize(); + $this->assertEqual(count($mem), 0, 'No field hooks were called.'); + // Purge the data. field_purge_batch(10); + // Check hooks invocations (same as above, for the 2nd bundle). + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + $hooks['field_test_field_load'][] = $stubs; + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); + // The field still exists, deleted. $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1)); $this->assertTrue(isset($fields[$field['id']]) && $fields[$field['id']]['deleted'], 'The field exists and is deleted'); diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc index b7c70a677..52ed3fcd0 100644 --- a/modules/field/tests/field_test.entity.inc +++ b/modules/field/tests/field_test.entity.inc @@ -58,7 +58,7 @@ function field_test_entity_info() { 'id' => 'ftid', 'bundle' => 'fttype', ), - 'bundles' => array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2')), + 'bundles' => array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2')) + $bundles, 'view modes' => $test_entity_modes, ), // In this case, the bundle key is not stored in the database. @@ -72,7 +72,7 @@ function field_test_entity_info() { 'id' => 'ftid', 'bundle' => 'fttype', ), - 'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')), + 'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')) + $bundles, 'view modes' => $test_entity_modes, ), // @see EntityPropertiesTestCase::testEntityLabel() diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index b8a2939d6..cc76a998d 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -58,6 +58,9 @@ function field_test_field_update_forbid($field, $prior_field, $has_data) { * Implements hook_field_load(). */ function field_test_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); + foreach ($items as $id => $item) { // To keep the test non-intrusive, only act for instances with the // test_hook_field_load setting explicitly set to TRUE. @@ -73,12 +76,39 @@ function field_test_field_load($entity_type, $entities, $field, $instances, $lan } /** + * Implements hook_field_insert(). + */ +function field_test_field_insert($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Implements hook_field_update(). + */ +function field_test_field_update($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Implements hook_field_delete(). + */ +function field_test_field_delete($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** * Implements hook_field_validate(). * * Possible error codes: * - 'field_test_invalid': The value is invalid. */ function field_test_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); + foreach ($items as $delta => $item) { if ($item['value'] == -1) { $errors[$field['field_name']][$langcode][$delta][] = array( @@ -350,11 +380,13 @@ function field_test_field_formatter_view($entity_type, $entity, $field, $instanc break; case 'field_test_multiple': - $array = array(); - foreach ($items as $delta => $item) { - $array[] = $delta . ':' . $item['value']; + if (!empty($items)) { + $array = array(); + foreach ($items as $delta => $item) { + $array[] = $delta . ':' . $item['value']; + } + $element[0] = array('#markup' => $settings['test_formatter_setting_multiple'] . '|' . implode('|', $array)); } - $element[0] = array('#markup' => $settings['test_formatter_setting_multiple'] . '|' . implode('|', $array)); break; } diff --git a/modules/field/tests/field_test.module b/modules/field/tests/field_test.module index 7f43fbf09..0015cd905 100644 --- a/modules/field/tests/field_test.module +++ b/modules/field/tests/field_test.module @@ -183,30 +183,6 @@ function field_test_field_create_field($field) { } /** - * Memorize calls to hook_field_insert(). - */ -function field_test_field_insert($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_update(). - */ -function field_test_field_update($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_delete(). - */ -function field_test_field_delete($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** * Implements hook_entity_query_alter(). */ function field_test_entity_query_alter(&$query) { @@ -248,3 +224,38 @@ function field_test_field_attach_view_alter(&$output, $context) { $output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter'); } } + +/** + * Implements hook_field_widget_properties_alter(). + */ +function field_test_field_widget_properties_alter(&$widget, $context) { + // Make the alter_test_text field 42 characters for nodes and comments. + if (in_array($context['entity_type'], array('node', 'comment')) && ($context['field']['field_name'] == 'alter_test_text')) { + $widget['settings']['size'] = 42; + } +} + +/** + * Implements hook_field_widget_properties_ENTITY_TYPE_alter(). + */ +function field_test_field_widget_properties_user_alter(&$widget, $context) { + // Always use buttons for the alter_test_options field on user forms. + if ($context['field']['field_name'] == 'alter_test_options') { + $widget['type'] = 'options_buttons'; + } +} + +/** + * Implements hook_field_widget_form_alter(). + */ +function field_test_field_widget_form_alter(&$element, &$form_state, $context) { + switch ($context['field']['field_name']) { + case 'alter_test_text': + drupal_set_message('Field size: ' . $context['instance']['widget']['settings']['size']); + break; + + case 'alter_test_options': + drupal_set_message('Widget type: ' . $context['instance']['widget']['type']); + break; + } +} |