diff --git a/modules/field/field.info b/modules/field/field.info index 04c516027468d84936f1fd1a94c074f094b6c6fa..e136b4a66b7b4ecfad29542a345450dfc4b0927f 100644 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -12,6 +12,6 @@ files[] = field.default.inc files[] = field.multilingual.inc files[] = field.attach.inc files[] = field.form.inc -files[] = field.test +files[] = tests/field.test dependencies[] = field_sql_storage required = TRUE diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.test b/modules/field/modules/field_sql_storage/field_sql_storage.test index 2a64c55dad3d0aecd6e94707520c5554d9c227f3..842bcf850babe47c91111a6ba43d0ce23c29f62f 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.test +++ b/modules/field/modules/field_sql_storage/field_sql_storage.test @@ -17,7 +17,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase { return array( 'name' => 'Field SQL storage tests', 'description' => "Test field SQL storage module.", - 'group' => 'Field' + 'group' => 'Field API' ); } @@ -303,7 +303,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase { // Create a decimal 5.2 field and add some data. $field = array('field_name' => 'decimal52', 'type' => 'number_decimal', 'settings' => array('precision' => 5, 'scale' => 2)); $field = field_create_field($field); - $instance = array('field_name' => 'decimal52', 'object_type' => 'test_entity', 'bundle' => FIELD_TEST_BUNDLE); + $instance = array('field_name' => 'decimal52', 'object_type' => 'test_entity', 'bundle' => 'test_bundle'); $instance = field_create_instance($instance); $entity = field_test_create_stub_entity(0, 0, $instance['bundle']); $entity->decimal52[FIELD_LANGUAGE_NONE][0]['value'] = '1.235'; @@ -331,7 +331,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase { $field_name = 'testfield'; $field = array('field_name' => $field_name, 'type' => 'text'); $field = field_create_field($field); - $instance = array('field_name' => $field_name, 'object_type' => 'test_entity', 'bundle' => FIELD_TEST_BUNDLE); + $instance = array('field_name' => $field_name, 'object_type' => 'test_entity', 'bundle' => 'test_bundle'); $instance = field_create_instance($instance); $tables = array(_field_sql_storage_tablename($field), _field_sql_storage_revision_tablename($field)); diff --git a/modules/field/modules/list/list.test b/modules/field/modules/list/list.test index 5ed96a04f0331864b7a58528e46df8447f5001bb..7f8a1dc62cade2fce69fc744a87a8ec98d3ab278 100644 --- a/modules/field/modules/list/list.test +++ b/modules/field/modules/list/list.test @@ -6,7 +6,7 @@ class ListFieldTestCase extends DrupalWebTestCase { return array( 'name' => 'List field', 'description' => "Test the List field type.", - 'group' => 'Field' + 'group' => 'Field Types' ); } @@ -26,7 +26,7 @@ class ListFieldTestCase extends DrupalWebTestCase { $this->instance_1 = array( 'field_name' => $this->card_1['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_buttons', ), @@ -40,7 +40,7 @@ class ListFieldTestCase extends DrupalWebTestCase { */ function testUpdateAllowedValues() { // All three options appear. - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $form = drupal_get_form('field_test_entity_form', $entity); $this->assertTrue(!empty($form['card_1'][FIELD_LANGUAGE_NONE][1]), t('Option 1 exists')); $this->assertTrue(!empty($form['card_1'][FIELD_LANGUAGE_NONE][2]), t('Option 2 exists')); @@ -49,7 +49,7 @@ class ListFieldTestCase extends DrupalWebTestCase { // Removed options do not appear. $this->card_1['settings']['allowed_values'] = "2|Two"; field_update_field($this->card_1); - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $form = drupal_get_form('field_test_entity_form', $entity); $this->assertTrue(empty($form['card_1'][FIELD_LANGUAGE_NONE][1]), t('Option 1 does not exist')); $this->assertTrue(!empty($form['card_1'][FIELD_LANGUAGE_NONE][2]), t('Option 2 exists')); @@ -73,13 +73,13 @@ class ListFieldTestCase extends DrupalWebTestCase { $this->instance_1 = array( 'field_name' => $this->card_1['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_buttons', ), ); $this->instance_1 = field_create_instance($this->instance_1); - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $form = drupal_get_form('field_test_entity_form', $entity); $this->assertTrue(!empty($form['card_1'][FIELD_LANGUAGE_NONE][1]), t('Option 1 exists')); $this->assertTrue(!empty($form['card_1'][FIELD_LANGUAGE_NONE][2]), t('Option 2 exists')); diff --git a/modules/field/modules/options/options.test b/modules/field/modules/options/options.test index f0471d4eca14c26b6bdb9c39eafcdeb80d9a12bd..4d343b880af9df88b60cde4264460245fe51620c 100644 --- a/modules/field/modules/options/options.test +++ b/modules/field/modules/options/options.test @@ -6,7 +6,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { return array( 'name' => 'Options widgets', 'description' => "Test the Options widgets.", - 'group' => 'Field' + 'group' => 'Field Types' ); } @@ -62,7 +62,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { $instance = array( 'field_name' => $this->card_1['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_buttons', ), @@ -118,7 +118,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { $instance = array( 'field_name' => $this->card_2['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_buttons', ), @@ -204,7 +204,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { $instance = array( 'field_name' => $this->card_1['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_select', ), @@ -258,7 +258,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { $instance = array( 'field_name' => $this->card_2['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_select', ), @@ -341,7 +341,7 @@ class OptionsWidgetsTestCase extends DrupalWebTestCase { $instance = array( 'field_name' => $this->bool['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_onoff', ), diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index 2f903e201e229511d6bb1e5914570a92ee6b2660..d6dc58acf1c1c52c755b8fddcc7d2a96be1b9e0e 100644 --- a/modules/field/modules/text/text.test +++ b/modules/field/modules/text/text.test @@ -10,7 +10,7 @@ class TextFieldTestCase extends DrupalWebTestCase { return array( 'name' => 'Text Field', 'description' => "Test the creation of text fields.", - 'group' => 'Field' + 'group' => 'Field Types' ); } @@ -41,7 +41,7 @@ class TextFieldTestCase extends DrupalWebTestCase { $this->instance = array( 'field_name' => $this->field['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'text_textfield', ), @@ -53,7 +53,7 @@ class TextFieldTestCase extends DrupalWebTestCase { ); field_create_instance($this->instance); // Test valid and invalid values with field_attach_validate(). - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $langcode = FIELD_LANGUAGE_NONE; for ($i = 0; $i <= $max_length + 2; $i++) { $entity->{$this->field['field_name']}[$langcode][0]['value'] = str_repeat('x', $i); @@ -87,7 +87,7 @@ class TextFieldTestCase extends DrupalWebTestCase { $this->instance = array( 'field_name' => $this->field_name, 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'label' => $this->randomName() . '_label', 'settings' => array( 'text_processing' => TRUE, @@ -141,7 +141,7 @@ class TextFieldTestCase extends DrupalWebTestCase { $this->instance = array( 'field_name' => $this->field_name, 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'label' => $this->randomName() . '_label', 'settings' => array( 'text_processing' => TRUE, @@ -224,7 +224,7 @@ class TextSummaryTestCase extends DrupalWebTestCase { return array( 'name' => 'Text summary', 'description' => 'Test text_summary() with different strings and lengths.', - 'group' => 'Field', + 'group' => 'Field Types', ); } diff --git a/modules/field/field.test b/modules/field/tests/field.test similarity index 99% rename from modules/field/field.test rename to modules/field/tests/field.test index 49a1a2452f93bcd41b767062dbec5f8849f36f28..5fffe3a39f7c0ad336458233532340fd18f0b254 100644 --- a/modules/field/field.test +++ b/modules/field/tests/field.test @@ -82,7 +82,7 @@ class FieldAttachStorageTestCase extends FieldAttachTestCase { return array( 'name' => 'Field attach tests (storage-related)', 'description' => 'Test storage-related Field Attach API functions.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -400,7 +400,7 @@ class FieldAttachStorageTestCase extends FieldAttachTestCase { * Test insert with missing or NULL fields, with default value. */ function testFieldAttachSaveMissingDataDefaultValue() { - // Add a default value. + // Add a default value function. $this->instance['default_value_function'] = 'field_test_default_value'; field_update_instance($this->instance); @@ -802,7 +802,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { return array( 'name' => 'Field attach tests (other)', 'description' => 'Test other Field Attach API functions.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -1129,7 +1129,7 @@ class FieldInfoTestCase extends FieldTestCase { return array( 'name' => 'Field info tests', 'description' => 'Get information about existing fields, instances and bundles.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -1181,7 +1181,7 @@ class FieldInfoTestCase extends FieldTestCase { // Verify that no unexpected instances exist. $core_fields = field_info_fields(); - $instances = field_info_instances('test_entity', FIELD_TEST_BUNDLE); + $instances = field_info_instances('test_entity', 'test_bundle'); $this->assertTrue(empty($instances), t('With no instances, info bundles is empty.')); // Create a field, verify it shows up. @@ -1206,7 +1206,7 @@ class FieldInfoTestCase extends FieldTestCase { $instance = array( 'field_name' => $field['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'label' => $this->randomName(), 'description' => $this->randomName(), 'weight' => mt_rand(0, 127), @@ -1265,7 +1265,7 @@ class FieldInfoTestCase extends FieldTestCase { $instance_definition = array( 'field_name' => $field_definition['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', ); field_create_instance($instance_definition); @@ -1334,7 +1334,7 @@ class FieldFormTestCase extends FieldTestCase { return array( 'name' => 'Field form tests', 'description' => 'Test Field form handling.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -1690,7 +1690,7 @@ class FieldCrudTestCase extends FieldTestCase { return array( 'name' => 'Field CRUD tests', 'description' => 'Test field create, read, update, and delete.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -1923,7 +1923,7 @@ class FieldCrudTestCase extends FieldTestCase { $this->instance_definition = array( 'field_name' => $this->field['field_name'], 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'test_field_widget', ), @@ -2020,7 +2020,7 @@ class FieldCrudTestCase extends FieldTestCase { // Create a decimal 5.2 field. $field = array('field_name' => 'decimal53', 'type' => 'number_decimal', 'cardinality' => 3, 'settings' => array('precision' => 5, 'scale' => 2)); $field = field_create_field($field); - $instance = array('field_name' => 'decimal53', 'object_type' => 'test_entity', 'bundle' => FIELD_TEST_BUNDLE); + $instance = array('field_name' => 'decimal53', 'object_type' => 'test_entity', 'bundle' => 'test_bundle'); $instance = field_create_instance($instance); // Update it to a deciaml 5.3 field. @@ -2137,7 +2137,7 @@ class FieldInstanceCrudTestCase extends FieldTestCase { return array( 'name' => 'Field instance CRUD tests', 'description' => 'Create field entities by attaching fields to entities.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -2153,7 +2153,7 @@ class FieldInstanceCrudTestCase extends FieldTestCase { 'field_name' => $this->field['field_name'], 'object_type' => 'test_entity', 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', ); } @@ -2329,7 +2329,7 @@ class FieldTranslationsTestCase extends FieldTestCase { return array( 'name' => 'Field translations tests', 'description' => 'Test multilanguage fields logic.', - 'group' => 'Field', + 'group' => 'Field API', ); } @@ -2563,7 +2563,7 @@ class FieldBulkDeleteTestCase extends FieldTestCase { return array( 'name' => 'Field bulk delete tests', 'description'=> 'Bulk delete fields and instances, and clean up afterwards.', - 'group' => 'Field', + 'group' => 'Field API', ); } diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc new file mode 100644 index 0000000000000000000000000000000000000000..fec43fcdf9dc2262ff710e2743936622371a5387 --- /dev/null +++ b/modules/field/tests/field_test.entity.inc @@ -0,0 +1,322 @@ +<?php +// $Id$ + +/** + * @file + * Defines an entity type. + */ + +/** + * Implements hook_entity_info(). + */ +function field_test_entity_info() { + $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); + return array( + 'test_entity' => array( + 'name' => t('Test Entity'), + 'object keys' => array( + 'id' => 'ftid', + 'revision' => 'ftvid', + 'bundle' => 'fttype', + ), + 'cacheable' => FALSE, + 'bundles' => $bundles, + 'fieldable' => TRUE, + ), + // This entity type doesn't get form handling for now... + 'test_cacheable_entity' => array( + 'name' => t('Test Entity, cacheable'), + 'object keys' => array( + 'id' => 'ftid', + 'revision' => 'ftvid', + 'bundle' => 'fttype', + ), + 'fieldable' => TRUE, + 'cacheable' => TRUE, + 'bundles' => $bundles, + ), + ); +} + +/** + * Implements hook_entity_info_alter(). + */ +function field_test_entity_info_alter(&$entity_info) { + // Enable/disable field_test as a translation handler. + foreach (field_test_entity_info_translatable() as $obj_type => $translatable) { + $entity_info[$obj_type]['translation']['field_test'] = $translatable; + } + // Disable locale as a translation handler. + foreach ($entity_info as $obj_type => $info) { + $entity_info[$obj_type]['translation']['locale'] = FALSE; + } +} + +/** + * Creates a new bundle for test_entity objects. + * + * @param $bundle + * The machine-readable name of the bundle. + * @param $text + * The human-readable name of the bundle. If none is provided, the machine + * name will be used. + */ +function field_test_create_bundle($bundle, $text = NULL) { + $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); + $bundles += array($bundle => array('label' => $text ? $text : $bundle)); + variable_set('field_test_bundles', $bundles); + + $info = field_test_entity_info(); + foreach ($info as $type => $type_info) { + field_attach_create_bundle($type, $bundle); + } +} + +/** + * Renames a bundle for test_entity objects. + * + * @param $bundle_old + * The machine-readable name of the bundle to rename. + * @param $bundle_new + * The new machine-readable name of the bundle. + */ +function field_test_rename_bundle($bundle_old, $bundle_new) { + $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); + $bundles[$bundle_new] = $bundles[$bundle_old]; + unset($bundles[$bundle_old]); + variable_set('field_test_bundles', $bundles); + + $info = field_test_entity_info(); + foreach ($info as $type => $type_info) { + field_attach_rename_bundle($type, $bundle_old, $bundle_new); + } +} + +/** + * Deletes a bundle for test_entity objects. + * + * @param $bundle + * The machine-readable name of the bundle to delete. + */ +function field_test_delete_bundle($bundle) { + $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); + unset($bundles[$bundle]); + variable_set('field_test_bundles', $bundles); + + $info = field_test_entity_info(); + foreach ($info as $type => $type_info) { + field_attach_delete_bundle($type, $bundle); + } +} + +/** + * Implements hook_field_build_modes(). + */ +function field_test_field_build_modes($obj_type) { + $modes = array(); + if ($obj_type == 'test_entity' || $obj_type == 'test_cacheable_entity') { + $modes = array( + 'full' => t('Full node'), + 'teaser' => t('Teaser'), + ); + } + return $modes; +} + +/** + * Creates a basic test_entity object. + */ +function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = 'test_bundle') { + $entity = new stdClass(); + // Only set id and vid properties if they don't come as NULL (creation form). + if (isset($id)) { + $entity->ftid = $id; + } + if (isset($vid)) { + $entity->ftvid = $vid; + } + $entity->fttype = $bundle; + + return $entity; +} + +/** + * Loads a test_entity. + * + * @param $ftid + * The id of the entity to load. + * @param $ftvid + * (Optional) The revision id of the entity to load. If not specified, the + * current revision will be used. + * @return + * The loaded entity. + */ +function field_test_entity_test_load($ftid, $ftvid = NULL) { + // Load basic strucure. + $query = db_select('test_entity', 'fte', array()) + ->fields('fte') + ->condition('ftid', $ftid); + if ($ftvid) { + $query->condition('ftvid', $ftvid); + } + $entities = $query->execute()->fetchAllAssoc('ftid'); + + // Attach fields. + if ($ftvid) { + field_attach_load_revision('test_entity', $entities); + } + else { + field_attach_load('test_entity', $entities); + } + + return $entities[$ftid]; +} + +/** + * Saves a test_entity. + * + * A new entity is created if $entity->ftid and $entity->is_new are both empty. + * A new revision is created if $entity->revision is not empty. + * + * @param $entity + * The entity to save. + */ +function field_test_entity_save(&$entity) { + field_attach_presave('test_entity', $entity); + + if (!isset($entity->is_new)) { + $entity->is_new = empty($entity->ftid); + } + + if (!$entity->is_new && !empty($entity->revision)) { + $entity->old_ftvid = $entity->ftvid; + unset($entity->ftvid); + } + + $update_entity = TRUE; + if ($entity->is_new) { + drupal_write_record('test_entity', $entity); + drupal_write_record('test_entity_revision', $entity); + $op = 'insert'; + } + else { + drupal_write_record('test_entity', $entity, 'ftid'); + if (!empty($entity->revision)) { + drupal_write_record('test_entity_revision', $entity); + } + else { + drupal_write_record('test_entity_revision', $entity, 'ftvid'); + $update_entity = FALSE; + } + $op = 'update'; + } + if ($update_entity) { + db_update('test_entity') + ->fields(array('ftvid' => $entity->ftvid)) + ->condition('ftid', $entity->ftid) + ->execute(); + } + + // Save fields. + $function = "field_attach_$op"; + $function('test_entity', $entity); +} + +/** + * Menu callback: displays the 'Add new test_entity' form. + */ +function field_test_entity_add($fttype) { + $fttype = str_replace('-', '_', $fttype); + $entity = (object)array('fttype' => $fttype); + drupal_set_title(t('Create test_entity @bundle', array('@bundle' => $fttype)), PASS_THROUGH); + return drupal_get_form('field_test_entity_form', $entity, TRUE); +} + +/** + * Menu callback: displays the 'Edit exiisting test_entity' form. + */ +function field_test_entity_edit($entity) { + drupal_set_title(t('test_entity @ftid revision @ftvid', array('@ftid' => $entity->ftid, '@ftvid' => $entity->ftvid)), PASS_THROUGH); + return drupal_get_form('field_test_entity_form', $entity); +} + +/** + * Test_entity form. + */ +function field_test_entity_form($form, &$form_state, $entity, $add = FALSE) { + if (isset($form_state['test_entity'])) { + $entity = $form_state['test_entity'] + (array)$entity; + } + $entity = (object)$entity; + + foreach (array('ftid', 'ftvid', 'fttype') as $key) { + $form[$key] = array( + '#type' => 'value', + '#value' => isset($entity->$key) ? $entity->$key : NULL, + ); + } + + // Add field widgets. + $form['#builder_function'] = 'field_test_entity_form_submit_builder'; + field_attach_form('test_entity', $entity, $form, $form_state); + + if (!$add) { + $form['revision'] = array( + '#access' => user_access('administer field_test content'), + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => FALSE, + '#weight' => 100, + ); + } + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#weight' => 101, + ); + + return $form; +} + +/** + * Validate handler for field_test_entity_form(). + */ +function field_test_entity_form_validate($form, &$form_state) { + $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']); + field_attach_form_validate('test_entity', $entity, $form, $form_state); +} + +/** + * Submit handler for field_test_entity_form(). + */ +function field_test_entity_form_submit($form, &$form_state) { + $entity = field_test_entity_form_submit_builder($form, $form_state); + $insert = empty($entity->ftid); + field_test_entity_save($entity); + + $message = $insert ? t('test_entity @id has been created.', array('@id' => $entity->ftid)) : t('test_entity @id has been updated.', array('@id' => $entity->ftid)); + drupal_set_message($message); + + if ($entity->ftid) { + unset($form_state['rebuild']); + $form_state['redirect'] = 'test-entity/' . $entity->ftid . '/edit'; + } + else { + // Error on save. + drupal_set_message(t('The entity could not be saved.'), 'error'); + } +} + +/** + * Builds a test_entity from submitted form values. + */ +function field_test_entity_form_submit_builder($form, &$form_state) { + $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']); + $entity->revision = !empty($form_state['values']['revision']); + field_attach_submit('test_entity', $entity, $form, $form_state); + + $form_state['test_entity'] = (array)$entity; + $form_state['rebuild'] = TRUE; + + return $entity; +} diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc new file mode 100644 index 0000000000000000000000000000000000000000..9696df3609710ed554a8c84f6bd9f8a8d745dec0 --- /dev/null +++ b/modules/field/tests/field_test.field.inc @@ -0,0 +1,311 @@ +<?php +// $Id$ + +/** + * @file + * Defines a field type and its formatters and widgets. + */ + +/** + * Implements hook_field_info(). + */ +function field_test_field_info() { + return array( + 'test_field' => array( + 'label' => t('Test Field'), + 'description' => t('Dummy field type used for tests.'), + 'settings' => array( + 'test_field_setting' => 'dummy test string', + 'changeable' => 'a changeable field setting', + 'unchangeable' => 'an unchangeable field setting', + ), + 'instance_settings' => array( + 'test_instance_setting' => 'dummy test string', + 'test_hook_field_load' => FALSE, + ), + 'default_widget' => 'test_field_widget', + 'default_formatter' => 'field_test_default', + ), + ); +} + +/** + * Implements hook_field_schema(). + */ +function field_test_field_schema($field) { + return array( + 'columns' => array( + 'value' => array( + 'type' => 'int', + 'size' => 'tiny', + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'value' => array('value'), + ), + ); +} + +/** + * Implements hook_field_update_forbid(). + */ +function field_test_field_update_forbid($field, $prior_field, $has_data) { + if ($field['type'] == 'test_field' && $field['settings']['unchangeable'] != $prior_field['settings']['unchangeable']) { + throw new FieldException("field_test 'unchangeable' setting cannot be changed'"); + } +} + +/** + * Implements hook_field_load(). + */ +function field_test_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) { + 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. + if ($instances[$id]['settings']['test_hook_field_load']) { + foreach ($item as $delta => $value) { + // Don't add anything on empty values. + if ($value) { + $items[$id][$delta]['additional_key'] = 'additional_value'; + } + } + } + } +} + +/** + * Implements hook_field_validate(). + * + * Possible error codes: + * - 'field_test_invalid': The value is invalid. + */ +function field_test_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) { + foreach ($items as $delta => $item) { + if ($item['value'] == -1) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'field_test_invalid', + 'message' => t('%name does not accept the value -1.', array('%name' => $instance['label'])), + ); + } + } +} + +/** + * Implements hook_field_sanitize(). + */ +function field_test_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) { + foreach ($items as $delta => $item) { + $value = check_plain($item['value']); + $items[$delta]['safe'] = $value; + } +} + +/** + * Implements hook_field_is_empty(). + */ +function field_test_field_is_empty($item, $field) { + return empty($item['value']); +} + +/** + * Implements hook_field_settings_form(). + */ +function field_test_field_settings_form($field, $instance, $has_data) { + $settings = $field['settings']; + + $form['test_field_setting'] = array( + '#type' => 'textfield', + '#title' => t('Field test field setting'), + '#default_value' => $settings['test_field_setting'], + '#required' => FALSE, + '#description' => t('A dummy form element to simulate field setting.'), + ); + + return $form; +} + +/** + * Implements hook_field_instance_settings_form(). + */ +function field_test_field_instance_settings_form($field, $instance) { + $settings = $instance['settings']; + + $form['test_instance_setting'] = array( + '#type' => 'textfield', + '#title' => t('Field test field instance setting'), + '#default_value' => $settings['test_instance_setting'], + '#required' => FALSE, + '#description' => t('A dummy form element to simulate field instance setting.'), + ); + + return $form; +} + +/** + * Implements hook_field_widget_info(). + */ +function field_test_field_widget_info() { + return array( + 'test_field_widget' => array( + 'label' => t('Test field'), + 'field types' => array('test_field'), + 'settings' => array('test_widget_setting' => 'dummy test string'), + ), + 'test_field_widget_multiple' => array( + 'label' => t('Test field 1'), + 'field types' => array('test_field'), + 'settings' => array('test_widget_setting_multiple' => 'dummy test string'), + 'behaviors' => array( + 'multiple values' => FIELD_BEHAVIOR_CUSTOM, + ), + ), + ); +} + +/** + * Implements hook_field_widget(). + */ +function field_test_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { + $element = array( + 'value' => $element + array( + '#type' => 'textfield', + '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '', + ), + ); + return $element; +} + +/** + * Implements hook_field_widget_error(). + */ +function field_test_field_widget_error($element, $error) { + form_error($element['value'], $error['message']); +} + +/** + * Implements hook_field_widget_settings_form(). + */ +function field_test_field_widget_settings_form($field, $instance) { + $widget = $instance['widget']; + $settings = $widget['settings']; + + $form['test_widget_setting'] = array( + '#type' => 'textfield', + '#title' => t('Field test field widget setting'), + '#default_value' => $settings['test_widget_setting'], + '#required' => FALSE, + '#description' => t('A dummy form element to simulate field widget setting.'), + ); + + return $form; +} + +/** + * Implements hook_field_formatter_info(). + */ +function field_test_field_formatter_info() { + return array( + 'field_test_default' => array( + 'label' => t('Default'), + 'description' => t('Default formatter'), + 'field types' => array('test_field'), + 'settings' => array( + 'test_formatter_setting' => 'dummy test string', + ), + ), + 'field_test_multiple' => array( + 'label' => t('Multiple'), + 'description' => t('Multiple formatter'), + 'field types' => array('test_field'), + 'settings' => array( + 'test_formatter_setting_multiple' => 'dummy test string', + ), + 'behaviors' => array( + 'multiple values' => FIELD_BEHAVIOR_CUSTOM, + ), + ), + 'field_test_needs_additional_data' => array( + 'label' => t('Tests hook_field_formatter_prepare_view()'), + 'field types' => array('test_field'), + 'settings' => array( + 'test_formatter_setting_additional' => 'dummy test string', + ), + ), + ); +} + +/** + * Implement hook_field_formatter_prepare_view(). + */ +function field_test_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) { + foreach ($items as $id => $item) { + // To keep the test non-intrusive, only act on the + // 'field_test_needs_additional_data' formatter. + if ($instances[$id]['display'][$build_mode]['type'] == 'field_test_needs_additional_data') { + foreach ($item as $delta => $value) { + // Don't add anything on empty values. + if ($value) { + $items[$id][$delta]['additional_formatter_value'] = $value['value'] + 1; + } + } + } + } +} + +/** + * Theme function for 'field_test_default' formatter. + */ +function theme_field_formatter_field_test_default($variables) { + $element = $variables['element']; + + $value = $element['#item']['value']; + $settings = $element['#settings']; + + return $settings['test_formatter_setting'] . '|' . $value; +} + +/** + * Theme function for 'field_test_multiple' formatter. + */ +function theme_field_formatter_field_test_multiple($variables) { + $element = $variables['element']; + + $settings = $element['#settings']; + + $items = array(); + foreach (element_children($element) as $key) { + $items[$key] = $key .':'. $element[$key]['#item']['value']; + } + $output = implode('|', $items); + return $settings['test_formatter_setting_multiple'] . '|' . $output; +} + +/** + * Theme function for 'field_test_needs_additional_data' formatter. + */ +function theme_field_formatter_field_test_needs_additional_data($variables) { + $element = $variables['element']; + $value = $element['#item']['value']; + $additional = $element['#item']['additional_formatter_value']; + $settings = $element['#settings']; + + return $settings['test_formatter_setting_additional'] . '|' . $value . '|' . $additional; +} + +/** + * Sample 'default vale' callback. + */ +function field_test_default_value($obj_type, $object, $field, $instance) { + return array(array('value' => 99)); +} + +/** + * Implements hook_field_access(). + */ +function field_test_field_access($op, $field, $obj_type, $object, $account) { + if ($field['field_name'] == "field_no_{$op}_access") { + return FALSE; + } + return TRUE; +} diff --git a/modules/simpletest/tests/field_test.info b/modules/field/tests/field_test.info similarity index 68% rename from modules/simpletest/tests/field_test.info rename to modules/field/tests/field_test.info index 88c67264cdb2f60f92d611a5d6af57459a374b16..b875e59f52e0f6f9deda58cde43f16b7aadf0861 100644 --- a/modules/simpletest/tests/field_test.info +++ b/modules/field/tests/field_test.info @@ -4,6 +4,9 @@ description = "Support module for the Field API tests." core = 7.x package = Testing files[] = field_test.module +files[] = field_test.entity.inc +files[] = field_test.field.inc +files[] = field_test.storage.inc files[] = field_test.install version = VERSION hidden = TRUE diff --git a/modules/simpletest/tests/field_test.install b/modules/field/tests/field_test.install similarity index 100% rename from modules/simpletest/tests/field_test.install rename to modules/field/tests/field_test.install diff --git a/modules/field/tests/field_test.module b/modules/field/tests/field_test.module new file mode 100644 index 0000000000000000000000000000000000000000..dd85c172961bb04eefe9313852c98edc5ede8152 --- /dev/null +++ b/modules/field/tests/field_test.module @@ -0,0 +1,198 @@ +<?php +// $Id$ + +/** + * @file + * Helper module for the Field API tests. + * + * The module defines + * - an entity type (field_test.entity.inc) + * - a field type and its formatters and widgets (field_test.field.inc) + * - a field storage backend (field_test.storage.inc) + * + * The main field_test.module file implements generic hooks and provides some + * test helper functions + */ + +module_load_include('inc', 'field_test', 'field_test.entity'); +module_load_include('inc', 'field_test', 'field_test.field'); +module_load_include('inc', 'field_test', 'field_test.storage'); + +/** + * Implements hook_permission(). + */ +function field_test_permission() { + $perms = array( + 'access field_test content' => array( + 'title' => t('Access field_test content'), + 'description' => t('View published field_test content.'), + ), + 'administer field_test content' => array( + 'title' => t('Administer field_test content'), + 'description' => t('Manage field_test content'), + ), + ); + return $perms; +} + +/** + * Implements hook_menu(). + */ +function field_test_menu() { + $items = array(); + $bundles = field_info_bundles('test_entity'); + + foreach ($bundles as $bundle_name => $bundle_info) { + $bundle_url_str = str_replace('_', '-', $bundle_name); + $items['test-entity/add/' . $bundle_url_str] = array( + 'title' => t('Add %bundle test_entity', array('%bundle' => $bundle_info['label'])), + 'page callback' => 'field_test_entity_add', + 'page arguments' => array(2), + 'access arguments' => array('administer field_test content'), + 'type' => MENU_NORMAL_ITEM, + ); + } + $items['test-entity/%field_test_entity_test/edit'] = array( + 'title' => 'Edit test entity', + 'page callback' => 'field_test_entity_edit', + 'page arguments' => array(1), + 'access arguments' => array('administer field_test content'), + 'type' => MENU_NORMAL_ITEM, + ); + + return $items; +} + +/** + * Generic op to test _field_invoke behavior. + * + * This simulates a field operation callback to be invoked by _field_invoke(). + */ +function field_test_field_test_op($obj_type, $object, $field, $instance, $langcode, &$items) { + return array($langcode => md5(serialize(array($obj_type, $object, $field['field_name'], $langcode, $items)))); +} + +/** + * Generic op to test _field_invoke_multiple behavior. + * + * This simulates a multiple field operation callback to be invoked by + * _field_invoke_multiple(). + */ +function field_test_field_test_op_multiple($obj_type, $objects, $field, $instances, $langcode, &$items) { + $result = array(); + foreach ($objects as $id => $object) { + $result[$id] = array($langcode => md5(serialize(array($obj_type, $object, $field['field_name'], $langcode, $items[$id])))); + } + return $result; +} + +/** + * Implement hook_field_languages(). + */ +function field_test_field_languages($obj_type, $field, &$languages) { + if ($field['settings']['test_hook_in']) { + // Add an unavailable language. + $languages[] = 'xx'; + // Remove an available language. + unset($languages[0]); + } +} + +/** + * Helper function to enable entity translations. + */ +function field_test_entity_info_translatable($obj_type = NULL, $translatable = NULL) { + $stored_value = &drupal_static(__FUNCTION__, array()); + if (isset($obj_type)) { + $stored_value[$obj_type] = $translatable; + drupal_static_reset('entity_get_info'); + cache_clear_all('entity_info', 'cache'); + } + return $stored_value; +} + +/** + * Store and retrieve keyed data for later verification by unit tests. + * + * This function is a simple in-memory key-value store with the + * distinction that it stores all values for a given key instead of + * just the most recently set value. field_test module hooks call + * this function to record their arguments, keyed by hook name. The + * unit tests later call this function to verify that the correct + * hooks were called and were passed the correct arguments. + * + * This function ignores all calls until the first time it is called + * with $key of NULL. Each time it is called with $key of NULL, it + * erases all previously stored data from its internal cache, but also + * returns the previously stored data to the caller. A typical usage + * scenario is: + * + * @code + * // calls to field_test_memorize() here are ignored + * + * // turn on memorization + * field_test_memorize(); + * + * // call some Field API functions that invoke field_test hooks + * $field = field_create_field(...); + * + * // retrieve and reset the memorized hook call data + * $mem = field_test_memorize(); + * + * // make sure hook_field_create_field() is invoked correctly + * assertEqual(count($mem['field_test_field_create_field']), 1); + * assertEqual($mem['field_test_field_create_field'][0], array($field)); + * @endcode + * + * @param $key + * The key under which to store to $value, or NULL as described above. + * @param $value + * A value to store for $key. + * @return + * An array mapping each $key to an array of each $value passed in + * for that key. + */ +function field_test_memorize($key = NULL, $value = NULL) { + $memorize = &drupal_static(__FUNCTION__, NULL); + + if (is_null($key)) { + $return = $memorize; + $memorize = array(); + return $return; + } + if (is_array($memorize)) { + $memorize[$key][] = $value; + } +} + +/** + * Memorize calls to hook_field_create_field(). + */ +function field_test_field_create_field($field) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Memorize calls to hook_field_insert(). + */ +function field_test_field_insert($obj_type, $object, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Memorize calls to hook_field_update(). + */ +function field_test_field_update($obj_type, $object, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Memorize calls to hook_field_delete(). + */ +function field_test_field_delete($obj_type, $object, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} diff --git a/modules/field/tests/field_test.storage.inc b/modules/field/tests/field_test.storage.inc new file mode 100644 index 0000000000000000000000000000000000000000..dd9e2eb896841bfb3161a98ce3a7b581754b134d --- /dev/null +++ b/modules/field/tests/field_test.storage.inc @@ -0,0 +1,475 @@ +<?php +// $Id$ + +/** + * @file + * Defines a field storage backend. + */ + + +/** + * Implements hook_field_storage_info(). + */ +function field_test_field_storage_info() { + return array( + 'field_test_storage' => array( + 'label' => t('Test storage'), + 'description' => t('Dummy test storage backend. Stores field values in the variable table.'), + ), + 'field_test_storage_failure' => array( + 'label' => t('Test storage failure'), + 'description' => t('Dummy test storage backend. Always fails to create fields.'), + ), + ); +} + +/** + * Implements hook_field_storage_details(). + */ +function field_test_field_storage_details($field, $instance) { + $details = array(); + + // Add field columns. + $columns = array(); + foreach ((array) $field['columns'] as $column_name => $attributes) { + $columns[$column_name] = $column_name; + } + return array( + 'drupal_variables' => array( + 'field_test_storage_data[FIELD_LOAD_CURRENT]' => $columns, + 'field_test_storage_data[FIELD_LOAD_REVISION]' => $columns, + ), + ); +} + +/** + * Implements hook_field_storage_details_alter(). + * + * @see FieldAttachStorageTestCase::testFieldStorageDetailsAlter() + */ +function field_test_field_storage_details_alter(&$details, $field, $instance) { + + // For testing, storage details are changed only because of the field name. + if ($field['field_name'] == 'field_test_change_my_details') { + $columns = array(); + foreach ((array) $field['columns'] as $column_name => $attributes) { + $columns[$column_name] = $column_name; + } + $details['drupal_variables'] = array( + FIELD_LOAD_CURRENT => array( + 'moon' => $columns, + ), + FIELD_LOAD_REVISION => array( + 'mars' => $columns, + ), + ); + } +} + +/** + * Helper function: stores or retrieves data from the 'storage backend'. + */ +function _field_test_storage_data($data = NULL) { + if (is_null($data)) { + return variable_get('field_test_storage_data', array()); + } + else { + variable_set('field_test_storage_data', $data); + } +} + +/** + * Implements hook_field_storage_load(). + */ +function field_test_field_storage_load($obj_type, $objects, $age, $fields, $options) { + $data = _field_test_storage_data(); + + $load_current = $age == FIELD_LOAD_CURRENT; + + foreach ($fields as $field_id => $ids) { + $field = field_info_field_by_id($field_id); + $field_name = $field['field_name']; + $field_data = $data[$field['id']]; + $sub_table = $load_current ? 'current' : 'revisions'; + $delta_count = array(); + foreach ($field_data[$sub_table] as $row) { + if ($row->type == $obj_type && (!$row->deleted || $options['deleted'])) { + if (($load_current && in_array($row->entity_id, $ids)) || (!$load_current && in_array($row->revision_id, $ids))) { + if (in_array($row->language, field_multilingual_available_languages($obj_type, $field))) { + if (!isset($delta_count[$row->entity_id][$row->language])) { + $delta_count[$row->entity_id][$row->language] = 0; + } + if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) { + $item = array(); + foreach ($field['columns'] as $column => $attributes) { + $item[$column] = $row->{$column}; + } + $objects[$row->entity_id]->{$field_name}[$row->language][] = $item; + $delta_count[$row->entity_id][$row->language]++; + } + } + } + } + } + } +} + +/** + * Implements hook_field_storage_write(). + */ +function field_test_field_storage_write($obj_type, $object, $op, $fields) { + $data = _field_test_storage_data(); + + list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); + + foreach ($fields as $field_id) { + $field = field_info_field_by_id($field_id); + $field_name = $field['field_name']; + $field_data = &$data[$field_id]; + + $all_languages = field_multilingual_available_languages($obj_type, $field); + $field_languages = array_intersect($all_languages, array_keys((array) $object->$field_name)); + + // Delete and insert, rather than update, in case a value was added. + if ($op == FIELD_STORAGE_UPDATE) { + // Delete languages present in the incoming $object->$field_name. + // Delete all languages if $object->$field_name is empty. + $languages = !empty($object->$field_name) ? $field_languages : $all_languages; + if ($languages) { + foreach ($field_data['current'] as $key => $row) { + if ($row->type == $obj_type && $row->entity_id == $id && in_array($row->language, $languages)) { + unset($field_data['current'][$key]); + } + } + if (isset($vid)) { + foreach ($field_data['revisions'] as $key => $row) { + if ($row->type == $obj_type && $row->revision_id == $vid) { + unset($field_data['revisions'][$key]); + } + } + } + } + } + + foreach ($field_languages as $langcode) { + $items = (array) $object->{$field_name}[$langcode]; + $delta_count = 0; + foreach ($items as $delta => $item) { + $row = (object) array( + 'field_id' => $field_id, + 'type' => $obj_type, + 'entity_id' => $id, + 'revision_id' => $vid, + 'bundle' => $bundle, + 'delta' => $delta, + 'deleted' => FALSE, + 'language' => $langcode, + ); + foreach ($field['columns'] as $column => $attributes) { + $row->{$column} = isset($item[$column]) ? $item[$column] : NULL; + } + + $field_data['current'][] = $row; + if (isset($vid)) { + $field_data['revisions'][] = $row; + } + + if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { + break; + } + } + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_storage_delete(). + */ +function field_test_field_storage_delete($obj_type, $object, $fields) { + list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); + + // Note: reusing field_test_storage_purge(), like field_sql_storage.module + // does, is highly inefficient in our case... + foreach (field_info_instances($bundle) as $instance) { + if (isset($fields[$instance['field_id']])) { + $field = field_info_field_by_id($instance['field_id']); + field_test_field_storage_purge($obj_type, $object, $field, $instance); + } + } +} + +/** + * Implements hook_field_storage_purge(). + */ +function field_test_field_storage_purge($obj_type, $object, $field, $instance) { + $data = _field_test_storage_data(); + + list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); + + $field_data = &$data[$field['id']]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as $key => $row) { + if ($row->type == $obj_type && $row->entity_id == $id) { + unset($field_data[$sub_table][$key]); + } + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_storage_delete_revision(). + */ +function field_test_field_storage_delete_revision($obj_type, $object, $fields) { + $data = _field_test_storage_data(); + + list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); + foreach ($fields as $field_id) { + $field_data = &$data[$field_id]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as $key => $row) { + if ($row->type == $obj_type && $row->entity_id == $id && $row->revision_id == $vid) { + unset($field_data[$sub_table][$key]); + } + } + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_storage_query(). + */ +function field_test_field_storage_query($field_id, $conditions, $count, &$cursor = NULL, $age) { + $data = _field_test_storage_data(); + + $load_current = $age == FIELD_LOAD_CURRENT; + + $field = field_info_field_by_id($field_id); + $field_columns = array_keys($field['columns']); + + $field_data = $data[$field['id']]; + $sub_table = $load_current ? 'current' : 'revisions'; + // We need to sort records by object type and object id. + usort($field_data[$sub_table], '_field_test_field_storage_query_sort_helper'); + + // Initialize results array. + $return = array(); + $obj_count = 0; + $rows_count = 0; + $rows_total = count($field_data[$sub_table]); + $skip = $cursor; + $skipped = 0; + + foreach ($field_data[$sub_table] as $row) { + if ($count != FIELD_QUERY_NO_LIMIT && $obj_count >= $count) { + break; + } + + if ($row->field_id == $field['id']) { + $match = TRUE; + $condition_deleted = FALSE; + // Add conditions. + foreach ($conditions as $condition) { + @list($column, $value, $operator) = $condition; + if (empty($operator)) { + $operator = is_array($value) ? 'IN' : '='; + } + switch ($operator) { + case '=': + $match = $match && $row->{$column} == $value; + break; + case '!=': + case '<': + case '<=': + case '>': + case '>=': + eval('$match = $match && '. $row->{$column} . ' ' . $operator . ' '. $value); + break; + case 'IN': + $match = $match && in_array($row->{$column}, $value); + break; + case 'NOT IN': + $match = $match && !in_array($row->{$column}, $value); + break; + case 'BETWEEN': + $match = $match && $row->{$column} >= $value[0] && $row->{$column} <= $value[1]; + break; + case 'STARTS_WITH': + case 'ENDS_WITH': + case 'CONTAINS': + // Not supported. + $match = FALSE; + break; + } + // Track condition on 'deleted'. + if ($column == 'deleted') { + $condition_deleted = TRUE; + } + } + + // Exclude deleted data unless we have a condition on it. + if (!$condition_deleted && $row->deleted) { + $match = FALSE; + } + + if ($match) { + if (is_null($skip) || $skipped >= $skip) { + $cursor++; + // If querying all revisions and the entity type has revisions, we need + // to key the results by revision_ids. + $entity_type = entity_get_info($row->type); + $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id; + + if (!isset($return[$row->type][$id])) { + $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle)); + $obj_count++; + } + } + else { + $skipped++; + } + } + } + $rows_count++; + + // The query is complete if we walked the whole array. + if ($count != FIELD_QUERY_NO_LIMIT && $rows_count >= $rows_total) { + $cursor = FIELD_QUERY_COMPLETE; + } + } + + return $return; +} + +/** + * Sort helper for field_test_field_storage_query(). + * + * Sorts by object type and object id. + */ +function _field_test_field_storage_query_sort_helper($a, $b) { + if ($a->type == $b->type) { + if ($a->entity_id == $b->entity_id) { + return 0; + } + else { + return $a->entity_id < $b->entity_id ? -1 : 1; + } + } + else { + return $a->type < $b->type ? -1 : 1; + } +} + +/** + * Implements hook_field_storage_create_field(). + */ +function field_test_field_storage_create_field($field) { + if ($field['storage']['type'] == 'field_test_storage_failure') { + throw new Exception('field_test_storage_failure engine always fails to create fields'); + } + + $data = _field_test_storage_data(); + + $data[$field['id']] = array( + 'current' => array(), + 'revisions' => array(), + ); + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_storage_delete_field(). + */ +function field_test_field_storage_delete_field($field) { + $data = _field_test_storage_data(); + + $field_data = &$data[$field['id']]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as &$row) { + $row->deleted = TRUE; + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_storage_delete_instance(). + */ +function field_test_field_storage_delete_instance($instance) { + $data = _field_test_storage_data(); + + $field = field_info_field($instance['field_name']); + $field_data = &$data[$field['id']]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as &$row) { + if ($row->bundle == $instance['bundle']) { + $row->deleted = TRUE; + } + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_attach_create_bundle(). + */ +function field_test_field_attach_create_bundle($bundle) { + // We don't need to do anything here. +} + +/** + * Implements hook_field_attach_rename_bundle(). + */ +function field_test_field_attach_rename_bundle($bundle_old, $bundle_new) { + $data = _field_test_storage_data(); + + // We need to account for deleted or inactive fields and instances. + $instances = field_read_instances(array('bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE)); + foreach ($instances as $field_name => $instance) { + $field = field_info_field_by_id($instance['field_id']); + if ($field['storage']['type'] == 'field_test_storage') { + $field_data = &$data[$field['id']]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as &$row) { + if ($row->bundle == $bundle_old) { + $row->bundle = $bundle_new; + } + } + } + } + } + + _field_test_storage_data($data); +} + +/** + * Implements hook_field_attach_delete_bundle(). + */ +function field_test_field_attach_delete_bundle($bundle, $instances) { + $data = _field_test_storage_data(); + + $instances = field_info_instances($bundle); + foreach ($instances as $field_name => $instance) { + $field = field_info_field($field_name); + if ($field['storage']['type'] == 'field_test_storage') { + $field_data = &$data[$field['id']]; + foreach (array('current', 'revisions') as $sub_table) { + foreach ($field_data[$sub_table] as &$row) { + if ($row->bundle == $bundle_old) { + $row->deleted = TRUE; + } + } + } + } + } + + _field_test_storage_data($data); +} diff --git a/modules/simpletest/tests/field_test.module b/modules/simpletest/tests/field_test.module deleted file mode 100644 index a3cd45125b071ea3d096cfb3e58b4c5f9b68a89d..0000000000000000000000000000000000000000 --- a/modules/simpletest/tests/field_test.module +++ /dev/null @@ -1,1278 +0,0 @@ -<?php -// $Id$ - -define('FIELD_TEST_ELEMENT_ID', 1); -define('FIELD_TEST_BUNDLE', 'test_bundle'); - -/** - * Implement hook_permission(). - */ -function field_test_permission() { - $perms = array( - 'access field_test content' => array( - 'title' => t('Access field_test content'), - 'description' => t('View published field_test content.'), - ), - 'administer field_test content' => array( - 'title' => t('Administer field_test content'), - 'description' => t('Manage field_test content'), - ), - ); - return $perms; -} - -/** - * Implement hook_menu(). - */ -function field_test_menu() { - $items = array(); - $bundles = field_info_bundles('test_entity'); - - foreach ($bundles as $bundle_name => $bundle_info) { - $bundle_url_str = str_replace('_', '-', $bundle_name); - $items['test-entity/add/' . $bundle_url_str] = array( - 'title' => t('Add %bundle test_entity', array('%bundle' => $bundle_info['label'])), - 'page callback' => 'field_test_entity_add', - 'page arguments' => array(2), - 'access arguments' => array('administer field_test content'), - 'type' => MENU_NORMAL_ITEM, - ); - } - $items['test-entity/%field_test_entity_test/edit'] = array( - 'title' => 'Edit test entity', - 'page callback' => 'field_test_entity_edit', - 'page arguments' => array(1), - 'access arguments' => array('administer field_test content'), - 'type' => MENU_NORMAL_ITEM, - ); - - return $items; -} - -/** - * Implements hook_field_access(). - */ -function field_test_field_access($op, $field, $obj_type, $object, $account) { - if ($field['field_name'] == "field_no_{$op}_access") { - return FALSE; - } - return TRUE; -} - -/** - * - * 'Field attach' API. - * - */ - - -/** - * Define a test fieldable entity. - */ -function field_test_entity_info() { - $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); - return array( - 'test_entity' => array( - 'name' => t('Test Entity'), - 'object keys' => array( - 'id' => 'ftid', - 'revision' => 'ftvid', - 'bundle' => 'fttype', - ), - 'cacheable' => FALSE, - 'bundles' => $bundles, - 'fieldable' => TRUE, - ), - // This entity type doesn't get form handling for now... - 'test_cacheable_entity' => array( - 'name' => t('Test Entity, cacheable'), - 'object keys' => array( - 'id' => 'ftid', - 'revision' => 'ftvid', - 'bundle' => 'fttype', - ), - 'fieldable' => TRUE, - 'cacheable' => TRUE, - 'bundles' => $bundles, - ), - ); -} - -/** - * Implement hook_entity_info_alter(). - */ -function field_test_entity_info_alter(&$entity_info) { - // Enable/disable field_test as a translation handler. - foreach (field_test_entity_info_translatable() as $obj_type => $translatable) { - $entity_info[$obj_type]['translation']['field_test'] = $translatable; - } - // Disable locale as a translation handler. - foreach ($entity_info as $obj_type => $info) { - $entity_info[$obj_type]['translation']['locale'] = FALSE; - } -} - -/** - * Implement hook_field_widget_settings_form(). - */ -function field_test_field_widget_settings_form($field, $instance) { - $widget = $instance['widget']; - $settings = $widget['settings']; - - $form['test_widget_setting'] = array( - '#type' => 'textfield', - '#title' => t('Field test field widget setting'), - '#default_value' => $settings['test_widget_setting'], - '#required' => FALSE, - '#description' => t('A dummy form element to simulate field widget setting.'), - ); - - return $form; -} - -/** - * Implement hook_field_instance_settings_form(). - */ -function field_test_field_instance_settings_form($field, $instance) { - $settings = $instance['settings']; - - $form['test_instance_setting'] = array( - '#type' => 'textfield', - '#title' => t('Field test field instance setting'), - '#default_value' => $settings['test_instance_setting'], - '#required' => FALSE, - '#description' => t('A dummy form element to simulate field instance setting.'), - ); - - return $form; -} - -/** - * Implement hook_field_settings_form(). - */ -function field_test_field_settings_form($field, $instance, $has_data) { - $settings = $field['settings']; - - $form['test_field_setting'] = array( - '#type' => 'textfield', - '#title' => t('Field test field setting'), - '#default_value' => $settings['test_field_setting'], - '#required' => FALSE, - '#description' => t('A dummy form element to simulate field setting.'), - ); - - return $form; -} - - -/** - * Create a new bundle for test_entity objects. - * - * @param $bundle - * The machine-readable name of the bundle. - * @param $text - * The human-readable name of the bundle. If none is provided, the machine - * name will be used. - */ -function field_test_create_bundle($bundle, $text = NULL) { - $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); - $bundles += array($bundle => array('label' => $text ? $text : $bundle)); - variable_set('field_test_bundles', $bundles); - - $info = field_test_entity_info(); - foreach ($info as $type => $type_info) { - field_attach_create_bundle($type, $bundle); - } -} - -/** - * Rename a bundle for test_entity objects. - * - * @param $bundle_old - * The machine-readable name of the bundle to rename. - * @param $bundle_new - * The new machine-readable name of the bundle. - */ -function field_test_rename_bundle($bundle_old, $bundle_new) { - $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); - $bundles[$bundle_new] = $bundles[$bundle_old]; - unset($bundles[$bundle_old]); - variable_set('field_test_bundles', $bundles); - - $info = field_test_entity_info(); - foreach ($info as $type => $type_info) { - field_attach_rename_bundle($type, $bundle_old, $bundle_new); - } -} - -/** - * Delete a bundle for test_entity objects. - * - * @param $bundle - * The machine-readable name of the bundle to delete. - */ -function field_test_delete_bundle($bundle) { - $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle'))); - unset($bundles[$bundle]); - variable_set('field_test_bundles', $bundles); - - $info = field_test_entity_info(); - foreach ($info as $type => $type_info) { - field_attach_delete_bundle($type, $bundle); - } -} - -/** - * Implement hook_field_build_modes(). - */ -function field_test_field_build_modes($obj_type) { - $modes = array(); - if ($obj_type == 'test_entity' || $obj_type == 'test_cacheable_entity') { - $modes = array( - 'full' => t('Full node'), - 'teaser' => t('Teaser'), - ); - } - return $modes; -} - -/** - * Helper function to create a basic 'test entity' structure. - * - * TODO : do we stil need this now that we can actualy load and save test_entities ? - */ -function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = FIELD_TEST_BUNDLE) { - $entity = new stdClass(); - // Only set id and vid properties if they don't come as NULL (creation form). - if (isset($id)) { - $entity->ftid = $id; - } - if (isset($vid)) { - $entity->ftvid = $vid; - } - $entity->fttype = $bundle; - - return $entity; -} - -function field_test_entity_test_load($ftid, $ftvid = NULL) { - // Load basic strucure. - $query = db_select('test_entity', 'fte', array()) - ->fields('fte') - ->condition('ftid', $ftid); - if ($ftvid) { - $query->condition('ftvid', $ftvid); - } - $entities = $query->execute()->fetchAllAssoc('ftid'); - - // Attach fields. - if ($ftvid) { - field_attach_load_revision('test_entity', $entities); - } - else { - field_attach_load('test_entity', $entities); - } - - return $entities[$ftid]; -} - -function field_test_entity_save(&$entity) { - field_attach_presave('test_entity', $entity); - - if (!isset($entity->is_new)) { - $entity->is_new = empty($entity->ftid); - } - - if (!$entity->is_new && !empty($entity->revision)) { - $entity->old_ftvid = $entity->ftvid; - unset($entity->ftvid); - } - - $update_entity = TRUE; - if ($entity->is_new) { - drupal_write_record('test_entity', $entity); - drupal_write_record('test_entity_revision', $entity); - $op = 'insert'; - } - else { - drupal_write_record('test_entity', $entity, 'ftid'); - if (!empty($entity->revision)) { - drupal_write_record('test_entity_revision', $entity); - } - else { - drupal_write_record('test_entity_revision', $entity, 'ftvid'); - $update_entity = FALSE; - } - $op = 'update'; - } - if ($update_entity) { - db_update('test_entity') - ->fields(array('ftvid' => $entity->ftvid)) - ->condition('ftid', $entity->ftid) - ->execute(); - } - - // Save fields. - $function = "field_attach_$op"; - $function('test_entity', $entity); -} - -function field_test_entity_add($fttype) { - $fttype = str_replace('-', '_', $fttype); - $entity = (object)array('fttype' => $fttype); - drupal_set_title(t('Create test_entity @bundle', array('@bundle' => $fttype)), PASS_THROUGH); - return drupal_get_form('field_test_entity_form', $entity, TRUE); -} - -function field_test_entity_edit($entity) { - drupal_set_title(t('test_entity @ftid revision @ftvid', array('@ftid' => $entity->ftid, '@ftvid' => $entity->ftvid)), PASS_THROUGH); - return drupal_get_form('field_test_entity_form', $entity); -} - -/** - * Form to set the value of fields attached to our entity. - */ -function field_test_entity_form($form, &$form_state, $entity, $add = FALSE) { - if (isset($form_state['test_entity'])) { - $entity = $form_state['test_entity'] + (array)$entity; - } - $entity = (object)$entity; - - foreach (array('ftid', 'ftvid', 'fttype') as $key) { - $form[$key] = array( - '#type' => 'value', - '#value' => isset($entity->$key) ? $entity->$key : NULL, - ); - } - - // Add field widgets. - $form['#builder_function'] = 'field_test_entity_form_submit_builder'; - field_attach_form('test_entity', $entity, $form, $form_state); - - if (!$add) { - $form['revision'] = array( - '#access' => user_access('administer field_test content'), - '#type' => 'checkbox', - '#title' => t('Create new revision'), - '#default_value' => FALSE, - '#weight' => 100, - ); - } - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#weight' => 101, - ); - - return $form; -} - -/** - * Validate handler for field_test_set_field_values(). - */ -function field_test_entity_form_validate($form, &$form_state) { - $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']); - field_attach_form_validate('test_entity', $entity, $form, $form_state); -} - -/** - * Submit handler for field_test_set_field_values(). - */ -function field_test_entity_form_submit($form, &$form_state) { - $entity = field_test_entity_form_submit_builder($form, $form_state); - $insert = empty($entity->ftid); - field_test_entity_save($entity); - - $message = $insert ? t('test_entity @id has been created.', array('@id' => $entity->ftid)) : t('test_entity @id has been updated.', array('@id' => $entity->ftid)); - drupal_set_message($message); - - if ($entity->ftid) { - unset($form_state['rebuild']); - $form_state['redirect'] = 'test-entity/' . $entity->ftid . '/edit'; - } - else { - // Error on save. - drupal_set_message(t('The entity could not be saved.'), 'error'); - } - -} - -/** - * Build a test_entity by processing submitted form values and prepare for a form rebuild. - */ -function field_test_entity_form_submit_builder($form, &$form_state) { - $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']); - $entity->revision = !empty($form_state['values']['revision']); - field_attach_submit('test_entity', $entity, $form, $form_state); - - $form_state['test_entity'] = (array)$entity; - $form_state['rebuild'] = TRUE; - - return $entity; -} - -/** - * - * 'Field type' API. - * - */ - -/** - * Implement hook_field_info(). - */ -function field_test_field_info() { - return array( - 'test_field' => array( - 'label' => t('Test Field'), - 'description' => t('Dummy field type used for tests.'), - 'settings' => array( - 'test_field_setting' => 'dummy test string', - 'changeable' => 'a changeable field setting', - 'unchangeable' => 'an unchangeable field setting', - ), - 'instance_settings' => array( - 'test_instance_setting' => 'dummy test string', - 'test_hook_field_load' => FALSE, - ), - 'default_widget' => 'test_field_widget', - 'default_formatter' => 'field_test_default', - ), - ); -} - -/** - * Implement hook_field_update_forbid(). - */ -function field_test_field_update_forbid($field, $prior_field, $has_data) { - if ($field['type'] == 'test_field' && $field['settings']['unchangeable'] != $prior_field['settings']['unchangeable']) { - throw new FieldException("field_test 'unchangeable' setting cannot be changed'"); - } -} - -/** - * Implement hook_field_schema(). - */ -function field_test_field_schema($field) { - return array( - 'columns' => array( - 'value' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'value' => array('value'), - ), - ); -} - -/** - * Implement hook_field_load(). - */ -function field_test_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) { - 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. - if ($instances[$id]['settings']['test_hook_field_load']) { - foreach ($item as $delta => $value) { - // Don't add anything on empty values. - if ($value) { - $items[$id][$delta]['additional_key'] = 'additional_value'; - } - } - } - } -} - -/** - * Implement hook_field_validate(). - * - * Possible error codes: - * - 'field_test_invalid': The value is invalid. - */ -function field_test_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) { - foreach ($items as $delta => $item) { - if ($item['value'] == -1) { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'field_test_invalid', - 'message' => t('%name does not accept the value -1.', array('%name' => $instance['label'])), - ); - } - } -} - -/** - * Implement hook_field_sanitize(). - */ -function field_test_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) { - foreach ($items as $delta => $item) { - $value = check_plain($item['value']); - $items[$delta]['safe'] = $value; - } -} - -/** - * Implement hook_field_is_empty(). - */ -function field_test_field_is_empty($item, $field) { - return empty($item['value']); -} - -/** - * Implement hook_field_widget_info(). - * - * Here we indicate that the field module will handle - * the default value and multiple values for these widgets. - * - * Callbacks can be omitted if default handing is used. - * They're included here just so this module can be used - * as an example for custom modules that might do things - * differently. - */ -function field_test_field_widget_info() { - return array( - 'test_field_widget' => array( - 'label' => t('Test field'), - 'field types' => array('test_field'), - 'settings' => array('test_widget_setting' => 'dummy test string'), - 'behaviors' => array( - 'multiple values' => FIELD_BEHAVIOR_DEFAULT, - 'default value' => FIELD_BEHAVIOR_DEFAULT, - ), - ), - 'test_field_widget_multiple' => array( - 'label' => t('Test field 1'), - 'field types' => array('test_field'), - 'settings' => array('test_widget_setting_multiple' => 'dummy test string'), - 'behaviors' => array( - 'multiple values' => FIELD_BEHAVIOR_CUSTOM, - 'default value' => FIELD_BEHAVIOR_DEFAULT, - ), - ), - ); -} - -/** - * Implement hook_field_widget(). - */ -function field_test_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - $element = array( - 'value' => $element + array( - '#type' => 'textfield', - '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '', - ), - ); - return $element; -} - -/** - * Implement hook_field_widget_error(). - */ -function field_test_field_widget_error($element, $error) { - form_error($element['value'], $error['message']); -} - -/** - * Implement hook_field_formatter_info(). - */ -function field_test_field_formatter_info() { - return array( - 'field_test_default' => array( - 'label' => t('Default'), - 'description' => t('Default formatter'), - 'field types' => array('test_field'), - 'settings' => array( - 'test_formatter_setting' => 'dummy test string', - ), - 'behaviors' => array( - 'multiple values' => FIELD_BEHAVIOR_DEFAULT, - ), - ), - 'field_test_multiple' => array( - 'label' => t('Multiple'), - 'description' => t('Multiple formatter'), - 'field types' => array('test_field'), - 'settings' => array( - 'test_formatter_setting_multiple' => 'dummy test string', - ), - 'behaviors' => array( - 'multiple values' => FIELD_BEHAVIOR_CUSTOM, - ), - ), - 'field_test_needs_additional_data' => array( - 'label' => t('Tests hook_field_formatter_prepare_view()'), - 'field types' => array('test_field'), - 'settings' => array( - 'test_formatter_setting_additional' => 'dummy test string', - ), - ), - ); -} - -/** - * Implement hook_field_formatter_prepare_view(). - */ -function field_test_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) { - foreach ($items as $id => $item) { - // To keep the test non-intrusive, only act on the - // 'field_test_needs_additional_data' formatter. - if ($instances[$id]['display'][$build_mode]['type'] == 'field_test_needs_additional_data') { - foreach ($item as $delta => $value) { - // Don't add anything on empty values. - if ($value) { - $items[$id][$delta]['additional_formatter_value'] = $value['value'] + 1; - } - } - } - } -} - -/** - * Theme function for 'field_test_default' formatter. - */ -function theme_field_formatter_field_test_default($variables) { - $element = $variables['element']; - - $value = $element['#item']['value']; - $settings = $element['#settings']; - - return $settings['test_formatter_setting'] . '|' . $value; -} - -/** - * Theme function for 'field_test_multiple' formatter. - */ -function theme_field_formatter_field_test_multiple($variables) { - $element = $variables['element']; - - $settings = $element['#settings']; - - $items = array(); - foreach (element_children($element) as $key) { - $items[$key] = $key .':'. $element[$key]['#item']['value']; - } - $output = implode('|', $items); - return $settings['test_formatter_setting_multiple'] . '|' . $output; -} - -/** - * Theme function for 'field_test_needs_additional_data' formatter. - */ -function theme_field_formatter_field_test_needs_additional_data($variables) { - $element = $variables['element']; - $value = $element['#item']['value']; - $additional = $element['#item']['additional_formatter_value']; - $settings = $element['#settings']; - - return $settings['test_formatter_setting_additional'] . '|' . $value . '|' . $additional; -} - -/** - * Sample function to test default value assignment. - */ -function field_test_default_value($obj_type, $object, $field, $instance) { - return array(array('value' => 99)); -} - -/** - * Generic op to test _field_invoke behavior. - */ -function field_test_field_test_op($obj_type, $object, $field, $instance, $langcode, &$items) { - return array($langcode => md5(serialize(array($obj_type, $object, $field['field_name'], $langcode, $items)))); -} - -/** - * Generic op to test _field_invoke_multiple behavior. - */ -function field_test_field_test_op_multiple($obj_type, $objects, $field, $instances, $langcode, &$items) { - $result = array(); - foreach ($objects as $id => $object) { - $result[$id] = array($langcode => md5(serialize(array($obj_type, $object, $field['field_name'], $langcode, $items[$id])))); - } - return $result; -} - -/** - * Implement hook_field_languages(). - */ -function field_test_field_languages($obj_type, $field, &$languages) { - if ($field['settings']['test_hook_in']) { - // Add an unavailable language. - $languages[] = 'xx'; - // Remove an available language. - unset($languages[0]); - } -} - -/** - * Helper function to enable entity translations. - */ -function field_test_entity_info_translatable($obj_type = NULL, $translatable = NULL) { - $stored_value = &drupal_static(__FUNCTION__, array()); - if (isset($obj_type)) { - $stored_value[$obj_type] = $translatable; - drupal_static_reset('entity_get_info'); - cache_clear_all('entity_info', 'cache'); - } - return $stored_value; -} - -/** - * - * 'Field storage' API. - * - */ - -/** - * Implement hook_field_storage_info(). - */ -function field_test_field_storage_info() { - return array( - 'field_test_storage' => array( - 'label' => t('Test storage'), - 'description' => t('Dummy test storage backend. Stores field values in the variable table.'), - ), - 'field_test_storage_failure' => array( - 'label' => t('Test storage failure'), - 'description' => t('Dummy test storage backend. Always fails to create fields.'), - ), - ); -} - -/** - * Implement hook_field_storage_details(). - */ -function field_test_field_storage_details($field, $instance) { - $details = array(); - - // Add field columns. - $columns = array(); - foreach ((array) $field['columns'] as $column_name => $attributes) { - $columns[$column_name] = $column_name; - } - return array( - 'drupal_variables' => array( - 'field_test_storage_data[FIELD_LOAD_CURRENT]' => $columns, - 'field_test_storage_data[FIELD_LOAD_REVISION]' => $columns, - ), - ); -} - -/** - * Implement hook_field_storage_details_alter(). - * - * @see FieldAttachStorageTestCase::testFieldStorageDetailsAlter() - */ -function field_test_field_storage_details_alter(&$details, $field, $instance) { - - // For testing, storage details are changed only because of the field name. - if ($field['field_name'] == 'field_test_change_my_details') { - $columns = array(); - foreach ((array) $field['columns'] as $column_name => $attributes) { - $columns[$column_name] = $column_name; - } - $details['drupal_variables'] = array( - FIELD_LOAD_CURRENT => array( - 'moon' => $columns, - ), - FIELD_LOAD_REVISION => array( - 'mars' => $columns, - ), - ); - } -} - -/** - * Helper function: store or retrieve data from the 'storage backend'. - */ -function _field_test_storage_data($data = NULL) { - if (is_null($data)) { - return variable_get('field_test_storage_data', array()); - } - else { - variable_set('field_test_storage_data', $data); - } -} - -/** - * Implement hook_field_storage_load(). - */ -function field_test_field_storage_load($obj_type, $objects, $age, $fields, $options) { - $data = _field_test_storage_data(); - - $load_current = $age == FIELD_LOAD_CURRENT; - - foreach ($fields as $field_id => $ids) { - $field = field_info_field_by_id($field_id); - $field_name = $field['field_name']; - $field_data = $data[$field['id']]; - $sub_table = $load_current ? 'current' : 'revisions'; - $delta_count = array(); - foreach ($field_data[$sub_table] as $row) { - if ($row->type == $obj_type && (!$row->deleted || $options['deleted'])) { - if (($load_current && in_array($row->entity_id, $ids)) || (!$load_current && in_array($row->revision_id, $ids))) { - if (in_array($row->language, field_multilingual_available_languages($obj_type, $field))) { - if (!isset($delta_count[$row->entity_id][$row->language])) { - $delta_count[$row->entity_id][$row->language] = 0; - } - if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) { - $item = array(); - foreach ($field['columns'] as $column => $attributes) { - $item[$column] = $row->{$column}; - } - $objects[$row->entity_id]->{$field_name}[$row->language][] = $item; - $delta_count[$row->entity_id][$row->language]++; - } - } - } - } - } - } -} - -/** - * Implement hook_field_storage_write(). - */ -function field_test_field_storage_write($obj_type, $object, $op, $fields) { - $data = _field_test_storage_data(); - - list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); - - foreach ($fields as $field_id) { - $field = field_info_field_by_id($field_id); - $field_name = $field['field_name']; - $field_data = &$data[$field_id]; - - $all_languages = field_multilingual_available_languages($obj_type, $field); - $field_languages = array_intersect($all_languages, array_keys((array) $object->$field_name)); - - // Delete and insert, rather than update, in case a value was added. - if ($op == FIELD_STORAGE_UPDATE) { - // Delete languages present in the incoming $object->$field_name. - // Delete all languages if $object->$field_name is empty. - $languages = !empty($object->$field_name) ? $field_languages : $all_languages; - if ($languages) { - foreach ($field_data['current'] as $key => $row) { - if ($row->type == $obj_type && $row->entity_id == $id && in_array($row->language, $languages)) { - unset($field_data['current'][$key]); - } - } - if (isset($vid)) { - foreach ($field_data['revisions'] as $key => $row) { - if ($row->type == $obj_type && $row->revision_id == $vid) { - unset($field_data['revisions'][$key]); - } - } - } - } - } - - foreach ($field_languages as $langcode) { - $items = (array) $object->{$field_name}[$langcode]; - $delta_count = 0; - foreach ($items as $delta => $item) { - $row = (object) array( - 'field_id' => $field_id, - 'type' => $obj_type, - 'entity_id' => $id, - 'revision_id' => $vid, - 'bundle' => $bundle, - 'delta' => $delta, - 'deleted' => FALSE, - 'language' => $langcode, - ); - foreach ($field['columns'] as $column => $attributes) { - $row->{$column} = isset($item[$column]) ? $item[$column] : NULL; - } - - $field_data['current'][] = $row; - if (isset($vid)) { - $field_data['revisions'][] = $row; - } - - if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { - break; - } - } - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_storage_delete(). - */ -function field_test_field_storage_delete($obj_type, $object, $fields) { - list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); - - // Note: reusing field_test_storage_purge(), like field_sql_storage.module - // does, is highly inefficient in our case... - foreach (field_info_instances($bundle) as $instance) { - if (isset($fields[$instance['field_id']])) { - $field = field_info_field_by_id($instance['field_id']); - field_test_field_storage_purge($obj_type, $object, $field, $instance); - } - } -} - -/** - * Implement hook_field_storage_purge(). - */ -function field_test_field_storage_purge($obj_type, $object, $field, $instance) { - $data = _field_test_storage_data(); - - list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); - - $field_data = &$data[$field['id']]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as $key => $row) { - if ($row->type == $obj_type && $row->entity_id == $id) { - unset($field_data[$sub_table][$key]); - } - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_storage_delete_revision(). - */ -function field_test_field_storage_delete_revision($obj_type, $object, $fields) { - $data = _field_test_storage_data(); - - list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object); - foreach ($fields as $field_id) { - $field_data = &$data[$field_id]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as $key => $row) { - if ($row->type == $obj_type && $row->entity_id == $id && $row->revision_id == $vid) { - unset($field_data[$sub_table][$key]); - } - } - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_storage_query(). - */ -function field_test_field_storage_query($field_id, $conditions, $count, &$cursor = NULL, $age) { - $data = _field_test_storage_data(); - - $load_current = $age == FIELD_LOAD_CURRENT; - - $field = field_info_field_by_id($field_id); - $field_columns = array_keys($field['columns']); - - $field_data = $data[$field['id']]; - $sub_table = $load_current ? 'current' : 'revisions'; - // We need to sort records by object type and object id. - usort($field_data[$sub_table], '_field_test_field_storage_query_sort_helper'); - - // Initialize results array. - $return = array(); - $obj_count = 0; - $rows_count = 0; - $rows_total = count($field_data[$sub_table]); - $skip = $cursor; - $skipped = 0; - - foreach ($field_data[$sub_table] as $row) { - if ($count != FIELD_QUERY_NO_LIMIT && $obj_count >= $count) { - break; - } - - if ($row->field_id == $field['id']) { - $match = TRUE; - $condition_deleted = FALSE; - // Add conditions. - foreach ($conditions as $condition) { - @list($column, $value, $operator) = $condition; - if (empty($operator)) { - $operator = is_array($value) ? 'IN' : '='; - } - switch ($operator) { - case '=': - $match = $match && $row->{$column} == $value; - break; - case '!=': - case '<': - case '<=': - case '>': - case '>=': - eval('$match = $match && '. $row->{$column} . ' ' . $operator . ' '. $value); - break; - case 'IN': - $match = $match && in_array($row->{$column}, $value); - break; - case 'NOT IN': - $match = $match && !in_array($row->{$column}, $value); - break; - case 'BETWEEN': - $match = $match && $row->{$column} >= $value[0] && $row->{$column} <= $value[1]; - break; - case 'STARTS_WITH': - case 'ENDS_WITH': - case 'CONTAINS': - // Not supported. - $match = FALSE; - break; - } - // Track condition on 'deleted'. - if ($column == 'deleted') { - $condition_deleted = TRUE; - } - } - - // Exclude deleted data unless we have a condition on it. - if (!$condition_deleted && $row->deleted) { - $match = FALSE; - } - - if ($match) { - if (is_null($skip) || $skipped >= $skip) { - $cursor++; - // If querying all revisions and the entity type has revisions, we need - // to key the results by revision_ids. - $entity_type = entity_get_info($row->type); - $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id; - - if (!isset($return[$row->type][$id])) { - $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle)); - $obj_count++; - } - } - else { - $skipped++; - } - } - } - $rows_count++; - - // The query is complete if we walked the whole array. - if ($count != FIELD_QUERY_NO_LIMIT && $rows_count >= $rows_total) { - $cursor = FIELD_QUERY_COMPLETE; - } - } - - return $return; -} - -/** - * Sort helper for field_test_field_storage_query(). - * - * Sort by object type and object id. - */ -function _field_test_field_storage_query_sort_helper($a, $b) { - if ($a->type == $b->type) { - if ($a->entity_id == $b->entity_id) { - return 0; - } - else { - return $a->entity_id < $b->entity_id ? -1 : 1; - } - } - else { - return $a->type < $b->type ? -1 : 1; - } -} - -/** - * Implement hook_field_storage_create_field(). - */ -function field_test_field_storage_create_field($field) { - if ($field['storage']['type'] == 'field_test_storage_failure') { - throw new Exception('field_test_storage_failure engine always fails to create fields'); - } - - $data = _field_test_storage_data(); - - $data[$field['id']] = array( - 'current' => array(), - 'revisions' => array(), - ); - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_storage_delete_field(). - */ -function field_test_field_storage_delete_field($field) { - $data = _field_test_storage_data(); - - $field_data = &$data[$field['id']]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as &$row) { - $row->deleted = TRUE; - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_storage_delete_instance(). - */ -function field_test_field_storage_delete_instance($instance) { - $data = _field_test_storage_data(); - - $field = field_info_field($instance['field_name']); - $field_data = &$data[$field['id']]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as &$row) { - if ($row->bundle == $instance['bundle']) { - $row->deleted = TRUE; - } - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_attach_create_bundle(). - */ -function field_test_field_attach_create_bundle($bundle) { - // We don't need to do anything here. -} - -/** - * Implement hook_field_attach_rename_bundle(). - */ -function field_test_field_attach_rename_bundle($bundle_old, $bundle_new) { - $data = _field_test_storage_data(); - - // We need to account for deleted or inactive fields and instances. - $instances = field_read_instances(array('bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE)); - foreach ($instances as $field_name => $instance) { - $field = field_info_field_by_id($instance['field_id']); - if ($field['storage']['type'] == 'field_test_storage') { - $field_data = &$data[$field['id']]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as &$row) { - if ($row->bundle == $bundle_old) { - $row->bundle = $bundle_new; - } - } - } - } - } - - _field_test_storage_data($data); -} - -/** - * Implement hook_field_attach_delete_bundle(). - */ -function field_test_field_attach_delete_bundle($bundle, $instances) { - $data = _field_test_storage_data(); - - $instances = field_info_instances($bundle); - foreach ($instances as $field_name => $instance) { - $field = field_info_field($field_name); - if ($field['storage']['type'] == 'field_test_storage') { - $field_data = &$data[$field['id']]; - foreach (array('current', 'revisions') as $sub_table) { - foreach ($field_data[$sub_table] as &$row) { - if ($row->bundle == $bundle_old) { - $row->deleted = TRUE; - } - } - } - } - } - - _field_test_storage_data($data); -} - -/** - * Store and retrieve keyed data for later verification by unit tests. - * - * This function is a simple in-memory key-value store with the - * distinction that it stores all values for a given key instead of - * just the most recently set value. field_test module hooks call - * this function to record their arguments, keyed by hook name. The - * unit tests later call this function to verify that the correct - * hooks were called and were passed the correct arguments. - * - * This function ignores all calls until the first time it is called - * with $key of NULL. Each time it is called with $key of NULL, it - * erases all previously stored data from its internal cache, but also - * returns the previously stored data to the caller. A typical usage - * scenario is: - * - * @code - * // calls to field_test_memorize() here are ignored - * - * // turn on memorization - * field_test_memorize(); - * - * // call some Field API functions that invoke field_test hooks - * $field = field_create_field(...); - * - * // retrieve and reset the memorized hook call data - * $mem = field_test_memorize(); - * - * // make sure hook_field_create_field() is invoked correctly - * assertEqual(count($mem['field_test_field_create_field']), 1); - * assertEqual($mem['field_test_field_create_field'][0], array($field)); - * @endcode - * - * @param $key - * The key under which to store to $value, or NULL as described above. - * @param $value - * A value to store for $key. - * @return - * An array mapping each $key to an array of each $value passed in - * for that key. - */ -function field_test_memorize($key = NULL, $value = NULL) { - $memorize = &drupal_static(__FUNCTION__, NULL); - - if (is_null($key)) { - $return = $memorize; - $memorize = array(); - return $return; - } - if (is_array($memorize)) { - $memorize[$key][] = $value; - } -} - -/** - * Memorize calls to hook_field_create_field(). - */ -function field_test_field_create_field($field) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_insert(). - */ -function field_test_field_insert($obj_type, $object, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_update(). - */ -function field_test_field_update($obj_type, $object, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_delete(). - */ -function field_test_field_delete($obj_type, $object, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index 2f1cbbae52f11a6879addcd47cb476d8d13891eb..b6aa1cd043204afcc4158e06009bb7e8d544c953 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -738,7 +738,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $this->instance = array( 'field_name' => $this->field_name, 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'widget' => array( 'type' => 'options_select', ), @@ -752,7 +752,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { // Test valid and invalid values with field_attach_validate(). $langcode = FIELD_LANGUAGE_NONE; - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $term = $this->createTerm($this->vocabulary); $entity->{$this->field_name}[$langcode][0]['value'] = $term->tid; field_attach_validate('test_entity', $entity); @@ -763,7 +763,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $this->assertTrue($entity->{$this->field_name}[$langcode][0]['value'] != $term->tid, t('Term from wrong vocabulary does not cause validation error')); } - $entity = field_test_create_stub_entity(0, 0, FIELD_TEST_BUNDLE); + $entity = field_test_create_stub_entity(); $bad_term = $this->createTerm($this->createVocabulary()); $entity->{$this->field_name}[$langcode][0]['value'] = $bad_term->tid; try { @@ -797,7 +797,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $this->instance = array( 'field_name' => $this->field_name, 'object_type' => 'test_entity', - 'bundle' => FIELD_TEST_BUNDLE, + 'bundle' => 'test_bundle', 'label' => $this->randomName() . '_label', 'widget' => array( 'type' => 'options_select',