First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
3770
modules/field/tests/field.test
Normal file
3770
modules/field/tests/field.test
Normal file
File diff suppressed because it is too large
Load diff
500
modules/field/tests/field_test.entity.inc
Normal file
500
modules/field/tests/field_test.entity.inc
Normal file
|
@ -0,0 +1,500 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines an entity type.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function field_test_entity_info() {
|
||||
// If requested, clear the field cache while this hook is being called. See
|
||||
// testFieldInfoCache().
|
||||
if (variable_get('field_test_clear_info_cache_in_hook_entity_info', FALSE)) {
|
||||
field_info_cache_clear();
|
||||
}
|
||||
|
||||
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
||||
$test_entity_modes = array(
|
||||
'full' => array(
|
||||
'label' => t('Full object'),
|
||||
'custom settings' => TRUE,
|
||||
),
|
||||
'teaser' => array(
|
||||
'label' => t('Teaser'),
|
||||
'custom settings' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
return array(
|
||||
'test_entity' => array(
|
||||
'label' => t('Test Entity'),
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => FALSE,
|
||||
'base table' => 'test_entity',
|
||||
'revision table' => 'test_entity_revision',
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'revision' => 'ftvid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'bundles' => $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
// This entity type doesn't get form handling for now...
|
||||
'test_cacheable_entity' => array(
|
||||
'label' => t('Test Entity, cacheable'),
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => TRUE,
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'revision' => 'ftvid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'bundles' => $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
'test_entity_bundle_key' => array(
|
||||
'label' => t('Test Entity with a bundle key.'),
|
||||
'base table' => 'test_entity_bundle_key',
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => FALSE,
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'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.
|
||||
'test_entity_bundle' => array(
|
||||
'label' => t('Test Entity with a specified bundle.'),
|
||||
'base table' => 'test_entity_bundle',
|
||||
'fieldable' => TRUE,
|
||||
'controller class' => 'TestEntityBundleController',
|
||||
'field cache' => FALSE,
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')) + $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
// @see EntityPropertiesTestCase::testEntityLabel()
|
||||
'test_entity_no_label' => array(
|
||||
'label' => t('Test entity without label'),
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => FALSE,
|
||||
'base table' => 'test_entity',
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'revision' => 'ftvid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'bundles' => $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
'test_entity_label' => array(
|
||||
'label' => t('Test entity label'),
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => FALSE,
|
||||
'base table' => 'test_entity',
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'revision' => 'ftvid',
|
||||
'bundle' => 'fttype',
|
||||
'label' => 'ftlabel',
|
||||
),
|
||||
'bundles' => $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
'test_entity_label_callback' => array(
|
||||
'label' => t('Test entity label callback'),
|
||||
'fieldable' => TRUE,
|
||||
'field cache' => FALSE,
|
||||
'base table' => 'test_entity',
|
||||
'label callback' => 'field_test_entity_label_callback',
|
||||
'entity keys' => array(
|
||||
'id' => 'ftid',
|
||||
'revision' => 'ftvid',
|
||||
'bundle' => 'fttype',
|
||||
),
|
||||
'bundles' => $bundles,
|
||||
'view modes' => $test_entity_modes,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $entity_type => $translatable) {
|
||||
$entity_info[$entity_type]['translation']['field_test'] = $translatable;
|
||||
}
|
||||
// Disable locale as a translation handler.
|
||||
foreach ($entity_info as $entity_type => $info) {
|
||||
$entity_info[$entity_type]['translation']['locale'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to enable entity translations.
|
||||
*/
|
||||
function field_test_entity_info_translatable($entity_type = NULL, $translatable = NULL) {
|
||||
drupal_static_reset('field_has_translation_handler');
|
||||
$stored_value = &drupal_static(__FUNCTION__, array());
|
||||
if (isset($entity_type)) {
|
||||
$stored_value[$entity_type] = $translatable;
|
||||
entity_info_cache_clear();
|
||||
}
|
||||
return $stored_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new bundle for test_entity entities.
|
||||
*
|
||||
* @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 entities.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a basic test_entity entity.
|
||||
*/
|
||||
function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $label = '') {
|
||||
$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;
|
||||
|
||||
$label = !empty($label) ? $label : $bundle . ' label';
|
||||
$entity->ftlabel = $label;
|
||||
|
||||
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())
|
||||
->condition('fte.ftid', $ftid);
|
||||
|
||||
if ($ftvid) {
|
||||
$query->join('test_entity_revision', 'fter', 'fte.ftid = fter.ftid');
|
||||
$query->addField('fte', 'ftid');
|
||||
$query->addField('fte', 'fttype');
|
||||
$query->addField('fter', 'ftvid');
|
||||
$query->condition('fter.ftvid', $ftvid);
|
||||
}
|
||||
else {
|
||||
$query->fields('fte');
|
||||
}
|
||||
|
||||
$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) {
|
||||
// During initial form build, add the entity to the form state for use during
|
||||
// form building and processing. During a rebuild, use what is in the form
|
||||
// state.
|
||||
if (!isset($form_state['test_entity'])) {
|
||||
$form_state['test_entity'] = $entity;
|
||||
}
|
||||
else {
|
||||
$entity = $form_state['test_entity'];
|
||||
}
|
||||
|
||||
foreach (array('ftid', 'ftvid', 'fttype') as $key) {
|
||||
$form[$key] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => isset($entity->$key) ? $entity->$key : NULL,
|
||||
);
|
||||
}
|
||||
|
||||
// Add field widgets.
|
||||
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_form_field_validate('test_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_build_test_entity($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) {
|
||||
$form_state['redirect'] = 'test-entity/manage/' . $entity->ftid . '/edit';
|
||||
}
|
||||
else {
|
||||
// Error on save.
|
||||
drupal_set_message(t('The entity could not be saved.'), 'error');
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the form state's entity by processing this submission's values.
|
||||
*/
|
||||
function field_test_entity_form_submit_build_test_entity($form, &$form_state) {
|
||||
$entity = $form_state['test_entity'];
|
||||
entity_form_submit_build_entity('test_entity', $entity, $form, $form_state);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form combining two separate entities.
|
||||
*/
|
||||
function field_test_entity_nested_form($form, &$form_state, $entity_1, $entity_2) {
|
||||
// First entity.
|
||||
foreach (array('ftid', 'ftvid', 'fttype') as $key) {
|
||||
$form[$key] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $entity_1->$key,
|
||||
);
|
||||
}
|
||||
field_attach_form('test_entity', $entity_1, $form, $form_state);
|
||||
|
||||
// Second entity.
|
||||
$form['entity_2'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Second entity'),
|
||||
'#tree' => TRUE,
|
||||
'#parents' => array('entity_2'),
|
||||
'#weight' => 50,
|
||||
);
|
||||
foreach (array('ftid', 'ftvid', 'fttype') as $key) {
|
||||
$form['entity_2'][$key] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $entity_2->$key,
|
||||
);
|
||||
}
|
||||
field_attach_form('test_entity', $entity_2, $form['entity_2'], $form_state);
|
||||
|
||||
$form['save'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
'#weight' => 100,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate handler for field_test_entity_nested_form().
|
||||
*/
|
||||
function field_test_entity_nested_form_validate($form, &$form_state) {
|
||||
$entity_1 = (object) $form_state['values'];
|
||||
field_attach_form_validate('test_entity', $entity_1, $form, $form_state);
|
||||
|
||||
$entity_2 = (object) $form_state['values']['entity_2'];
|
||||
field_attach_form_validate('test_entity', $entity_2, $form['entity_2'], $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for field_test_entity_nested_form().
|
||||
*/
|
||||
function field_test_entity_nested_form_submit($form, &$form_state) {
|
||||
$entity_1 = (object) $form_state['values'];
|
||||
field_attach_submit('test_entity', $entity_1, $form, $form_state);
|
||||
field_test_entity_save($entity_1);
|
||||
|
||||
$entity_2 = (object) $form_state['values']['entity_2'];
|
||||
field_attach_submit('test_entity', $entity_2, $form['entity_2'], $form_state);
|
||||
field_test_entity_save($entity_2);
|
||||
|
||||
drupal_set_message(t('test_entities @id_1 and @id_2 have been updated.', array('@id_1' => $entity_1->ftid, '@id_2' => $entity_2->ftid)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller class for the test_entity_bundle entity type.
|
||||
*
|
||||
* This extends the DrupalDefaultEntityController class, adding required
|
||||
* special handling for bundles (since they are not stored in the database).
|
||||
*/
|
||||
class TestEntityBundleController extends DrupalDefaultEntityController {
|
||||
|
||||
protected function attachLoad(&$entities, $revision_id = FALSE) {
|
||||
// Add bundle information.
|
||||
foreach ($entities as $key => $entity) {
|
||||
$entity->fttype = 'test_entity_bundle';
|
||||
$entities[$key] = $entity;
|
||||
}
|
||||
parent::attachLoad($entities, $revision_id);
|
||||
}
|
||||
}
|
413
modules/field/tests/field_test.field.inc
Normal file
413
modules/field/tests/field_test.field.inc
Normal file
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @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',
|
||||
),
|
||||
'shape' => array(
|
||||
'label' => t('Shape'),
|
||||
'description' => t('Another dummy field type.'),
|
||||
'settings' => array(
|
||||
'foreign_key_name' => 'shape',
|
||||
),
|
||||
'instance_settings' => array(),
|
||||
'default_widget' => 'test_field_widget',
|
||||
'default_formatter' => 'field_test_default',
|
||||
),
|
||||
'hidden_test_field' => array(
|
||||
'no_ui' => TRUE,
|
||||
'label' => t('Hidden from UI test field'),
|
||||
'description' => t('Dummy hidden field type used for tests.'),
|
||||
'settings' => array(),
|
||||
'instance_settings' => array(),
|
||||
'default_widget' => 'test_field_widget',
|
||||
'default_formatter' => 'field_test_default',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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($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.
|
||||
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_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(
|
||||
'error' => 'field_test_invalid',
|
||||
'message' => t('%name does not accept the value -1.', array('%name' => $instance['label'])),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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', 'hidden_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_form().
|
||||
*/
|
||||
function field_test_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
|
||||
switch ($instance['widget']['type']) {
|
||||
case 'test_field_widget':
|
||||
$element += array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
|
||||
);
|
||||
return array('value' => $element);
|
||||
|
||||
case 'test_field_widget_multiple':
|
||||
$values = array();
|
||||
foreach ($items as $delta => $value) {
|
||||
$values[] = $value['value'];
|
||||
}
|
||||
$element += array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => implode(', ', $values),
|
||||
'#element_validate' => array('field_test_widget_multiple_validate'),
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for 'test_field_widget_multiple' widget.
|
||||
*/
|
||||
function field_test_widget_multiple_validate($element, &$form_state) {
|
||||
$values = array_map('trim', explode(',', $element['#value']));
|
||||
$items = array();
|
||||
foreach ($values as $value) {
|
||||
$items[] = array('value' => $value);
|
||||
}
|
||||
form_set_value($element, $items, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_error().
|
||||
*/
|
||||
function field_test_field_widget_error($element, $error, $form, &$form_state) {
|
||||
// @todo No easy way to differenciate widget types, we should receive it as a
|
||||
// parameter.
|
||||
if (isset($element['value'])) {
|
||||
// Widget is test_field_widget.
|
||||
$error_element = $element['value'];
|
||||
}
|
||||
else {
|
||||
// Widget is test_field_widget_multiple.
|
||||
$error_element = $element;
|
||||
}
|
||||
|
||||
form_error($error_element, $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',
|
||||
),
|
||||
),
|
||||
'field_test_with_prepare_view' => array(
|
||||
'label' => t('Tests hook_field_formatter_prepare_view()'),
|
||||
'field types' => array('test_field'),
|
||||
'settings' => array(
|
||||
'test_formatter_setting_additional' => 'dummy test string',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_settings_form().
|
||||
*/
|
||||
function field_test_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
|
||||
$display = $instance['display'][$view_mode];
|
||||
$settings = $display['settings'];
|
||||
|
||||
$element = array();
|
||||
|
||||
// The name of the setting depends on the formatter type.
|
||||
$map = array(
|
||||
'field_test_default' => 'test_formatter_setting',
|
||||
'field_test_multiple' => 'test_formatter_setting_multiple',
|
||||
'field_test_with_prepare_view' => 'test_formatter_setting_additional',
|
||||
);
|
||||
|
||||
if (isset($map[$display['type']])) {
|
||||
$name = $map[$display['type']];
|
||||
|
||||
$element[$name] = array(
|
||||
'#title' => t('Setting'),
|
||||
'#type' => 'textfield',
|
||||
'#size' => 20,
|
||||
'#default_value' => $settings[$name],
|
||||
'#required' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_settings_summary().
|
||||
*/
|
||||
function field_test_field_formatter_settings_summary($field, $instance, $view_mode) {
|
||||
$display = $instance['display'][$view_mode];
|
||||
$settings = $display['settings'];
|
||||
|
||||
$summary = '';
|
||||
|
||||
// The name of the setting depends on the formatter type.
|
||||
$map = array(
|
||||
'field_test_default' => 'test_formatter_setting',
|
||||
'field_test_multiple' => 'test_formatter_setting_multiple',
|
||||
'field_test_with_prepare_view' => 'test_formatter_setting_additional',
|
||||
);
|
||||
|
||||
if (isset($map[$display['type']])) {
|
||||
$name = $map[$display['type']];
|
||||
$summary = t('@setting: @value', array('@setting' => $name, '@value' => $settings[$name]));
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_prepare_view().
|
||||
*/
|
||||
function field_test_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
|
||||
foreach ($items as $id => $item) {
|
||||
// To keep the test non-intrusive, only act on the
|
||||
// 'field_test_with_prepare_view' formatter.
|
||||
if ($displays[$id]['type'] == 'field_test_with_prepare_view') {
|
||||
foreach ($item as $delta => $value) {
|
||||
// Don't add anything on empty values.
|
||||
if ($value) {
|
||||
$items[$id][$delta]['additional_formatter_value'] = $value['value'] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_view().
|
||||
*/
|
||||
function field_test_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
|
||||
$element = array();
|
||||
$settings = $display['settings'];
|
||||
|
||||
switch ($display['type']) {
|
||||
case 'field_test_default':
|
||||
foreach ($items as $delta => $item) {
|
||||
$element[$delta] = array('#markup' => $settings['test_formatter_setting'] . '|' . $item['value']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'field_test_with_prepare_view':
|
||||
foreach ($items as $delta => $item) {
|
||||
$element[$delta] = array('#markup' => $settings['test_formatter_setting_additional'] . '|' . $item['value'] . '|' . $item['additional_formatter_value']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'field_test_multiple':
|
||||
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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample 'default value' callback.
|
||||
*/
|
||||
function field_test_default_value($entity_type, $entity, $field, $instance) {
|
||||
return array(array('value' => 99));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_access().
|
||||
*/
|
||||
function field_test_field_access($op, $field, $entity_type, $entity, $account) {
|
||||
if ($field['field_name'] == "field_no_{$op}_access") {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
13
modules/field/tests/field_test.info
Normal file
13
modules/field/tests/field_test.info
Normal file
|
@ -0,0 +1,13 @@
|
|||
name = "Field API Test"
|
||||
description = "Support module for the Field API tests."
|
||||
core = 7.x
|
||||
package = Testing
|
||||
files[] = field_test.entity.inc
|
||||
version = VERSION
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
162
modules/field/tests/field_test.install
Normal file
162
modules/field/tests/field_test.install
Normal file
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the field_test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function field_test_install() {
|
||||
// hook_entity_info_alter() needs to be executed as last.
|
||||
db_update('system')
|
||||
->fields(array('weight' => 1))
|
||||
->condition('name', 'field_test')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function field_test_schema() {
|
||||
$schema['test_entity'] = array(
|
||||
'description' => 'The base table for test_entities.',
|
||||
'fields' => array(
|
||||
'ftid' => array(
|
||||
'description' => 'The primary identifier for a test_entity.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'ftvid' => array(
|
||||
'description' => 'The current {test_entity_revision}.ftvid version identifier.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'fttype' => array(
|
||||
'description' => 'The type of this test_entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'ftlabel' => array(
|
||||
'description' => 'The label of this test_entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
'unique keys' => array(
|
||||
'ftvid' => array('ftvid'),
|
||||
),
|
||||
'primary key' => array('ftid'),
|
||||
);
|
||||
$schema['test_entity_bundle_key'] = array(
|
||||
'description' => 'The base table for test entities with a bundle key.',
|
||||
'fields' => array(
|
||||
'ftid' => array(
|
||||
'description' => 'The primary identifier for a test_entity_bundle_key.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'fttype' => array(
|
||||
'description' => 'The type of this test_entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => FALSE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
);
|
||||
$schema['test_entity_bundle'] = array(
|
||||
'description' => 'The base table for test entities with a bundle.',
|
||||
'fields' => array(
|
||||
'ftid' => array(
|
||||
'description' => 'The primary identifier for a test_entity_bundle.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
$schema['test_entity_revision'] = array(
|
||||
'description' => 'Stores information about each saved version of a {test_entity}.',
|
||||
'fields' => array(
|
||||
'ftid' => array(
|
||||
'description' => 'The {test_entity} this version belongs to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'ftvid' => array(
|
||||
'description' => 'The primary identifier for this version.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'nid' => array('ftid'),
|
||||
),
|
||||
'primary key' => array('ftvid'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_schema().
|
||||
*/
|
||||
function field_test_field_schema($field) {
|
||||
if ($field['type'] == 'test_field') {
|
||||
return array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'value' => array('value'),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$foreign_keys = array();
|
||||
// The 'foreign keys' key is not always used in tests.
|
||||
if (!empty($field['settings']['foreign_key_name'])) {
|
||||
$foreign_keys['foreign keys'] = array(
|
||||
// This is a dummy foreign key definition, references a table that
|
||||
// doesn't exist, but that's not a problem.
|
||||
$field['settings']['foreign_key_name'] => array(
|
||||
'table' => $field['settings']['foreign_key_name'],
|
||||
'columns' => array($field['settings']['foreign_key_name'] => 'id'),
|
||||
),
|
||||
);
|
||||
}
|
||||
return array(
|
||||
'columns' => array(
|
||||
'shape' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'color' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
) + $foreign_keys;
|
||||
}
|
||||
}
|
284
modules/field/tests/field_test.module
Normal file
284
modules/field/tests/field_test.module
Normal file
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
require_once DRUPAL_ROOT . '/modules/field/tests/field_test.entity.inc';
|
||||
require_once DRUPAL_ROOT . '/modules/field/tests/field_test.field.inc';
|
||||
require_once DRUPAL_ROOT . '/modules/field/tests/field_test.storage.inc';
|
||||
|
||||
/**
|
||||
* 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/manage/%field_test_entity_test/edit'] = array(
|
||||
'title' => 'Edit test entity',
|
||||
'page callback' => 'field_test_entity_edit',
|
||||
'page arguments' => array(2),
|
||||
'access arguments' => array('administer field_test content'),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
);
|
||||
|
||||
$items['test-entity/nested/%field_test_entity_test/%field_test_entity_test'] = array(
|
||||
'title' => 'Nested entity form',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('field_test_entity_nested_form', 2, 3),
|
||||
'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($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
return array($langcode => hash('sha256', serialize(array($entity_type, $entity, $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($entity_type, $entities, $field, $instances, $langcode, &$items) {
|
||||
$result = array();
|
||||
foreach ($entities as $id => $entity) {
|
||||
// Entities, instances and items are assumed to be consistently grouped by
|
||||
// language. To verify this we try to access all the passed data structures
|
||||
// by entity id. If they are grouped correctly, one entity, one instance and
|
||||
// one array of items should be available for each entity id.
|
||||
$field_name = $instances[$id]['field_name'];
|
||||
$result[$id] = array($langcode => hash('sha256', serialize(array($entity_type, $entity, $field_name, $langcode, $items[$id]))));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_available_languages_alter().
|
||||
*/
|
||||
function field_test_field_available_languages_alter(&$languages, $context) {
|
||||
if (variable_get('field_test_field_available_languages_alter', FALSE)) {
|
||||
// Add an unavailable language.
|
||||
$languages[] = 'xx';
|
||||
// Remove an available language.
|
||||
$index = array_search('en', $languages);
|
||||
unset($languages[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_language_alter().
|
||||
*/
|
||||
function field_test_field_language_alter(&$display_language, $context) {
|
||||
if (variable_get('field_test_language_fallback', TRUE)) {
|
||||
locale_field_language_fallback($display_language, $context['entity'], $context['language']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!isset($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_query_alter().
|
||||
*/
|
||||
function field_test_entity_query_alter(&$query) {
|
||||
if (!empty($query->alterMyExecuteCallbackPlease)) {
|
||||
$query->executeCallback = 'field_test_dummy_field_storage_query';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pseudo-implements hook_field_storage_query().
|
||||
*/
|
||||
function field_test_dummy_field_storage_query(EntityFieldQuery $query) {
|
||||
// Return dummy values that will be checked by the test.
|
||||
return array(
|
||||
'user' => array(
|
||||
1 => entity_create_stub_entity('user', array(1, NULL, NULL)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements callback_entity_info_label().
|
||||
*
|
||||
* @return
|
||||
* The label of the entity prefixed with "label callback".
|
||||
*/
|
||||
function field_test_entity_label_callback($entity) {
|
||||
return 'label callback ' . $entity->ftlabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_view_alter().
|
||||
*/
|
||||
function field_test_field_attach_view_alter(&$output, $context) {
|
||||
if (!empty($context['display']['settings']['alter'])) {
|
||||
$output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter');
|
||||
}
|
||||
|
||||
if (isset($output['test_field'])) {
|
||||
$output['test_field'][] = array('#markup' => 'field language is ' . $context['language']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter() for tag 'efq_table_prefixing_test'.
|
||||
*
|
||||
* @see EntityFieldQueryTestCase::testTablePrefixing()
|
||||
*/
|
||||
function field_test_query_efq_table_prefixing_test_alter(&$query) {
|
||||
// Add an additional join onto the entity base table. This will cause an
|
||||
// exception if the EFQ does not properly prefix the base table.
|
||||
$query->join('test_entity','te2','%alias.ftid = test_entity.ftid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter() for tag 'store_global_test_query'.
|
||||
*/
|
||||
function field_test_query_store_global_test_query_alter($query) {
|
||||
// Save the query in a global variable so that it can be examined by tests.
|
||||
// This can be used by any test which needs to check a query, but see
|
||||
// FieldSqlStorageTestCase::testFieldSqlStorageMultipleConditionsSameColumn()
|
||||
// for an example.
|
||||
$GLOBALS['test_query'] = $query;
|
||||
}
|
473
modules/field/tests/field_test.storage.inc
Normal file
473
modules/field/tests/field_test.storage.inc
Normal file
|
@ -0,0 +1,473 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
$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) {
|
||||
|
||||
// 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 (!isset($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($entity_type, $entities, $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 == $entity_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_available_languages($entity_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};
|
||||
}
|
||||
$entities[$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($entity_type, $entity, $op, $fields) {
|
||||
$data = _field_test_storage_data();
|
||||
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
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_available_languages($entity_type, $field);
|
||||
$field_languages = array_intersect($all_languages, array_keys((array) $entity->$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 $entity->$field_name.
|
||||
// Delete all languages if $entity->$field_name is empty.
|
||||
$languages = !empty($entity->$field_name) ? $field_languages : $all_languages;
|
||||
if ($languages) {
|
||||
foreach ($field_data['current'] as $key => $row) {
|
||||
if ($row->type == $entity_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 == $entity_type && $row->revision_id == $vid) {
|
||||
unset($field_data['revisions'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($field_languages as $langcode) {
|
||||
$items = (array) $entity->{$field_name}[$langcode];
|
||||
$delta_count = 0;
|
||||
foreach ($items as $delta => $item) {
|
||||
$row = (object) array(
|
||||
'field_id' => $field_id,
|
||||
'type' => $entity_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($entity_type, $entity, $fields) {
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
// 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($entity_type, $entity, $field, $instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_storage_purge().
|
||||
*/
|
||||
function field_test_field_storage_purge($entity_type, $entity, $field, $instance) {
|
||||
$data = _field_test_storage_data();
|
||||
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
$field_data = &$data[$field['id']];
|
||||
foreach (array('current', 'revisions') as $sub_table) {
|
||||
foreach ($field_data[$sub_table] as $key => $row) {
|
||||
if ($row->type == $entity_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($entity_type, $entity, $fields) {
|
||||
$data = _field_test_storage_data();
|
||||
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
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 == $entity_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 entity type and entity id.
|
||||
usort($field_data[$sub_table], '_field_test_field_storage_query_sort_helper');
|
||||
|
||||
// Initialize results array.
|
||||
$return = array();
|
||||
$entity_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 && $entity_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 (!isset($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['entity 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));
|
||||
$entity_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 entity type and entity 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($entity_type, $bundle, $instances) {
|
||||
$data = _field_test_storage_data();
|
||||
|
||||
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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue