2009-02-03 17:30:13 +00:00
|
|
|
<?php
|
|
|
|
// $Id$
|
|
|
|
|
|
|
|
define('FIELD_TEST_ELEMENT_ID', 1);
|
|
|
|
define('FIELD_TEST_BUNDLE', 'test_bundle');
|
2009-02-05 03:42:58 +00:00
|
|
|
|
|
|
|
/**
|
2009-07-05 18:00:11 +00:00
|
|
|
* Implement hook_permission().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
2009-07-05 18:00:11 +00:00
|
|
|
function field_test_permission() {
|
2009-02-05 03:42:58 +00:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_menu().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
function field_test_menu() {
|
|
|
|
$items = array();
|
2009-07-10 05:58:13 +00:00
|
|
|
$bundles = field_info_bundles('test_entity');
|
2009-02-05 03:42:58 +00:00
|
|
|
|
2009-07-10 05:58:13 +00:00
|
|
|
foreach ($bundles as $bundle_name => $bundle_info) {
|
|
|
|
$bundle_url_str = str_replace('_', '-', $bundle_name);
|
2009-02-05 03:42:58 +00:00
|
|
|
$items['test-entity/add/' . $bundle_url_str] = array(
|
2009-07-10 05:58:13 +00:00
|
|
|
'title' => t('Add %bundle test_entity', array('%bundle' => $bundle_info['label'])),
|
2009-02-05 03:42:58 +00:00
|
|
|
'page callback' => 'field_test_entity_add',
|
|
|
|
'page arguments' => array(2),
|
|
|
|
'access arguments' => array('administer field_test content'),
|
|
|
|
'type' => MENU_NORMAL_ITEM,
|
|
|
|
);
|
|
|
|
}
|
2009-10-16 03:47:14 +00:00
|
|
|
$items['test-entity/%field_test_entity_test/edit'] = array(
|
2009-02-05 03:42:58 +00:00
|
|
|
'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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 'Field attach' API.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define a test fieldable entity.
|
|
|
|
*/
|
2009-08-25 21:53:48 +00:00
|
|
|
function field_test_entity_info() {
|
2009-07-10 05:58:13 +00:00
|
|
|
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
2009-02-05 03:42:58 +00:00
|
|
|
return array(
|
|
|
|
'test_entity' => array(
|
|
|
|
'name' => t('Test Entity'),
|
2009-07-10 05:58:13 +00:00
|
|
|
'object keys' => array(
|
|
|
|
'id' => 'ftid',
|
|
|
|
'revision' => 'ftvid',
|
|
|
|
'bundle' => 'fttype',
|
|
|
|
),
|
2009-02-05 03:42:58 +00:00
|
|
|
'cacheable' => FALSE,
|
|
|
|
'bundles' => $bundles,
|
2009-08-25 21:53:48 +00:00
|
|
|
'fieldable' => TRUE,
|
2009-02-05 03:42:58 +00:00
|
|
|
),
|
|
|
|
// This entity type doesn't get form handling for now...
|
|
|
|
'test_cacheable_entity' => array(
|
|
|
|
'name' => t('Test Entity, cacheable'),
|
2009-07-10 05:58:13 +00:00
|
|
|
'object keys' => array(
|
|
|
|
'id' => 'ftid',
|
|
|
|
'revision' => 'ftvid',
|
|
|
|
'bundle' => 'fttype',
|
|
|
|
),
|
2009-08-25 21:53:48 +00:00
|
|
|
'fieldable' => TRUE,
|
2009-02-05 03:42:58 +00:00
|
|
|
'cacheable' => TRUE,
|
|
|
|
'bundles' => $bundles,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-08-22 00:58:55 +00:00
|
|
|
/**
|
2009-08-31 05:35:47 +00:00
|
|
|
* Implement hook_entity_info_alter().
|
2009-08-22 00:58:55 +00:00
|
|
|
*/
|
2009-08-31 05:35:47 +00:00
|
|
|
function field_test_entity_info_alter(&$entity_info) {
|
2009-10-16 02:04:44 +00:00
|
|
|
// Enable/disable field_test as a translation handler.
|
2009-08-31 05:35:47 +00:00
|
|
|
foreach (field_test_entity_info_translatable() as $obj_type => $translatable) {
|
2009-10-16 02:04:44 +00:00
|
|
|
$entity_info[$obj_type]['translation']['field_test'] = $translatable;
|
|
|
|
}
|
|
|
|
// Disable locale as a translation handler.
|
|
|
|
foreach (field_info_fieldable_types() as $obj_type => $info) {
|
|
|
|
$entity_info[$obj_type]['translation']['locale'] = FALSE;
|
2009-08-22 00:58:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-17 00:32:29 +00:00
|
|
|
/**
|
|
|
|
* Create a new bundle for test_entity objects.
|
|
|
|
*
|
2009-10-15 12:44:36 +00:00
|
|
|
* @param $bundle
|
2009-05-17 00:32:29 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
2009-10-15 12:44:36 +00:00
|
|
|
function field_test_create_bundle($bundle, $text = NULL) {
|
2009-07-10 05:58:13 +00:00
|
|
|
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
2009-10-15 12:44:36 +00:00
|
|
|
$bundles += array($bundle => array('label' => $text ? $text : $bundle));
|
2009-02-05 03:42:58 +00:00
|
|
|
variable_set('field_test_bundles', $bundles);
|
|
|
|
|
2009-10-15 12:44:36 +00:00
|
|
|
$info = field_test_entity_info();
|
|
|
|
foreach ($info as $type => $type_info) {
|
|
|
|
field_attach_create_bundle($type, $bundle);
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2009-05-17 00:32:29 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2009-02-05 03:42:58 +00:00
|
|
|
function field_test_rename_bundle($bundle_old, $bundle_new) {
|
2009-07-10 05:58:13 +00:00
|
|
|
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
2009-02-05 03:42:58 +00:00
|
|
|
$bundles[$bundle_new] = $bundles[$bundle_old];
|
|
|
|
unset($bundles[$bundle_old]);
|
|
|
|
variable_set('field_test_bundles', $bundles);
|
|
|
|
|
2009-10-15 12:44:36 +00:00
|
|
|
$info = field_test_entity_info();
|
|
|
|
foreach ($info as $type => $type_info) {
|
|
|
|
field_attach_rename_bundle($type, $bundle_old, $bundle_new);
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
2009-05-17 00:32:29 +00:00
|
|
|
/**
|
|
|
|
* Delete a bundle for test_entity objects.
|
|
|
|
*
|
2009-10-15 12:44:36 +00:00
|
|
|
* @param $bundle
|
2009-05-17 00:32:29 +00:00
|
|
|
* The machine-readable name of the bundle to delete.
|
|
|
|
*/
|
2009-10-15 12:44:36 +00:00
|
|
|
function field_test_delete_bundle($bundle) {
|
2009-07-10 05:58:13 +00:00
|
|
|
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
2009-10-15 12:44:36 +00:00
|
|
|
unset($bundles[$bundle]);
|
2009-02-05 03:42:58 +00:00
|
|
|
variable_set('field_test_bundles', $bundles);
|
|
|
|
|
2009-10-15 12:44:36 +00:00
|
|
|
$info = field_test_entity_info();
|
|
|
|
foreach ($info as $type => $type_info) {
|
|
|
|
field_attach_delete_bundle($type, $bundle);
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_build_modes().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
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();
|
2009-04-28 05:24:41 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2009-02-05 03:42:58 +00:00
|
|
|
$entity->fttype = $bundle;
|
|
|
|
|
|
|
|
return $entity;
|
|
|
|
}
|
|
|
|
|
2009-10-16 03:47:14 +00:00
|
|
|
function field_test_entity_test_load($ftid, $ftvid = NULL) {
|
2009-02-05 03:42:58 +00:00
|
|
|
// 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);
|
|
|
|
|
2009-05-17 00:49:18 +00:00
|
|
|
if (!isset($entity->is_new)) {
|
|
|
|
$entity->is_new = empty($entity->ftid);
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
2009-05-17 00:49:18 +00:00
|
|
|
|
|
|
|
if (!$entity->is_new && !empty($entity->revision)) {
|
2009-02-05 03:42:58 +00:00
|
|
|
$entity->old_ftvid = $entity->ftvid;
|
2009-05-17 00:49:18 +00:00
|
|
|
unset($entity->ftvid);
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2009-09-18 00:12:48 +00:00
|
|
|
function field_test_entity_form($form, &$form_state, $entity) {
|
2009-02-05 03:42:58 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
$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) {
|
2009-03-26 13:31:28 +00:00
|
|
|
$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);
|
2009-02-05 03:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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']);
|
|
|
|
field_attach_submit('test_entity', $entity, $form, $form_state);
|
|
|
|
|
|
|
|
$form_state['test_entity'] = (array)$entity;
|
|
|
|
$form_state['rebuild'] = TRUE;
|
|
|
|
|
|
|
|
return $entity;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 'Field type' API.
|
|
|
|
*
|
|
|
|
*/
|
2009-02-03 17:30:13 +00:00
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_info().
|
2009-02-03 17:30:13 +00:00
|
|
|
*
|
|
|
|
* This field provides a textfield which only accepts the value 1.
|
|
|
|
*/
|
|
|
|
function field_test_field_info() {
|
|
|
|
return array(
|
|
|
|
'test_field' => array(
|
|
|
|
'label' => t('Test Field'),
|
2009-05-17 00:32:29 +00:00
|
|
|
'settings' => array(
|
|
|
|
'test_field_setting' => 'dummy test string',
|
2009-09-26 15:57:39 +00:00
|
|
|
'changeable' => 'a changeable field setting',
|
|
|
|
'unchangeable' => 'an unchangeable field setting',
|
2009-05-17 00:32:29 +00:00
|
|
|
),
|
|
|
|
'instance_settings' => array(
|
|
|
|
'test_instance_setting' => 'dummy test string',
|
|
|
|
'test_hook_field_load' => FALSE,
|
|
|
|
),
|
2009-02-03 17:30:13 +00:00
|
|
|
'default_widget' => 'test_field_widget',
|
|
|
|
'default_formatter' => 'field_test_default',
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-09-26 15:57:39 +00:00
|
|
|
/**
|
|
|
|
* 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'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-03 17:30:13 +00:00
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_schema().
|
2009-02-03 17:30:13 +00:00
|
|
|
*/
|
2009-05-20 09:48:47 +00:00
|
|
|
function field_test_field_schema($field) {
|
|
|
|
return array(
|
|
|
|
'columns' => array(
|
|
|
|
'value' => array(
|
|
|
|
'type' => 'int',
|
|
|
|
'size' => 'tiny',
|
|
|
|
'not null' => FALSE,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'indexes' => array(
|
|
|
|
'value' => array('value'),
|
|
|
|
),
|
2009-02-03 17:30:13 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-07-02 20:19:48 +00:00
|
|
|
/**
|
2009-07-08 07:19:05 +00:00
|
|
|
* Implement hook_field_load().
|
2009-07-02 20:19:48 +00:00
|
|
|
*/
|
2009-08-22 00:58:55 +00:00
|
|
|
function field_test_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) {
|
2009-07-08 07:19:05 +00:00
|
|
|
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';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-02 20:19:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-03 17:30:13 +00:00
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_validate().
|
2009-03-26 13:31:28 +00:00
|
|
|
*
|
|
|
|
* Possible error codes:
|
|
|
|
* - 'field_test_invalid': The value is invalid.
|
2009-02-03 17:30:13 +00:00
|
|
|
*/
|
2009-08-22 00:58:55 +00:00
|
|
|
function field_test_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
|
2009-03-26 13:31:28 +00:00
|
|
|
foreach ($items as $delta => $item) {
|
|
|
|
if ($item['value'] == -1) {
|
2009-08-22 00:58:55 +00:00
|
|
|
$errors[$field['field_name']][$langcode][$delta][] = array(
|
2009-03-26 13:31:28 +00:00
|
|
|
'error' => 'field_test_invalid',
|
|
|
|
'message' => t('%name does not accept the value -1.', array('%name' => $instance['label'])),
|
|
|
|
);
|
2009-02-03 17:30:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_sanitize().
|
2009-02-03 17:30:13 +00:00
|
|
|
*/
|
2009-08-22 00:58:55 +00:00
|
|
|
function field_test_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) {
|
2009-02-03 17:30:13 +00:00
|
|
|
foreach ($items as $delta => $item) {
|
|
|
|
$value = check_plain($item['value']);
|
|
|
|
$items[$delta]['safe'] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_is_empty().
|
2009-02-03 17:30:13 +00:00
|
|
|
*/
|
|
|
|
function field_test_field_is_empty($item, $field) {
|
|
|
|
return empty($item['value']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_widget_info().
|
2009-02-03 17:30:13 +00:00
|
|
|
*
|
2009-08-19 22:46:05 +00:00
|
|
|
* Here we indicate that the field module will handle
|
2009-02-03 17:30:13 +00:00
|
|
|
* 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(
|
2009-02-05 03:42:58 +00:00
|
|
|
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
|
|
|
'default value' => FIELD_BEHAVIOR_DEFAULT,
|
2009-02-03 17:30:13 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_widget().
|
2009-02-03 17:30:13 +00:00
|
|
|
*
|
|
|
|
* Attach a single form element to the form. It will be built out and
|
2009-09-10 06:31:39 +00:00
|
|
|
* validated in the callback(s) listed in hook_element_info(). We build it
|
2009-02-03 17:30:13 +00:00
|
|
|
* out in the callbacks rather than here in hook_widget so it can be
|
|
|
|
* plugged into any module that can provide it with valid
|
|
|
|
* $field information.
|
|
|
|
*
|
2009-08-19 22:46:05 +00:00
|
|
|
* If there are multiple values for this field, the field module will
|
2009-02-03 17:30:13 +00:00
|
|
|
* call this function as many times as needed.
|
|
|
|
*
|
|
|
|
* @param $form
|
|
|
|
* the entire form array, $form['#node'] holds node information
|
|
|
|
* @param $form_state
|
|
|
|
* the form_state, $form_state['values'][$field['field_name']]
|
|
|
|
* holds the field's form values.
|
|
|
|
* @param $field
|
|
|
|
* The field structure.
|
2009-08-22 00:58:55 +00:00
|
|
|
* @param $instance
|
|
|
|
* the instance array
|
2009-02-03 17:30:13 +00:00
|
|
|
* @param $items
|
|
|
|
* array of default values for this field
|
|
|
|
* @param $delta
|
|
|
|
* the order of this item in the array of subelements (0, 1, 2, etc)
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* the form item for a single element for this field
|
|
|
|
*/
|
2009-08-27 00:33:52 +00:00
|
|
|
function field_test_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta = 0) {
|
2009-02-05 03:42:58 +00:00
|
|
|
$element = array(
|
|
|
|
'value' => array(
|
2009-02-03 17:30:13 +00:00
|
|
|
'#title' => $instance['label'],
|
|
|
|
'#type' => 'textfield',
|
2009-02-05 03:42:58 +00:00
|
|
|
'#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
|
|
|
|
'#required' => $instance['required'],
|
2009-02-03 17:30:13 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
return $element;
|
|
|
|
}
|
|
|
|
|
2009-03-26 13:31:28 +00:00
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_widget_error().
|
2009-03-26 13:31:28 +00:00
|
|
|
*/
|
|
|
|
function field_test_field_widget_error($element, $error) {
|
|
|
|
form_error($element['value'], $error['message']);
|
|
|
|
}
|
|
|
|
|
2009-02-05 03:42:58 +00:00
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_field_formatter_info().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
function field_test_field_formatter_info() {
|
|
|
|
return array(
|
|
|
|
'field_test_default' => array(
|
|
|
|
'label' => t('Default'),
|
2009-08-28 06:51:07 +00:00
|
|
|
'description' => t('Default formatter'),
|
2009-02-05 03:42:58 +00:00
|
|
|
'field types' => array('test_field'),
|
2009-02-03 17:30:13 +00:00
|
|
|
'settings' => array(
|
|
|
|
'test_formatter_setting' => 'dummy test string',
|
|
|
|
),
|
2009-02-05 03:42:58 +00:00
|
|
|
'behaviors' => array(
|
|
|
|
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
|
|
|
|
),
|
2009-02-03 17:30:13 +00:00
|
|
|
),
|
|
|
|
'field_test_multiple' => array(
|
2009-08-28 06:51:07 +00:00
|
|
|
'label' => t('Multiple'),
|
|
|
|
'description' => t('Multiple formatter'),
|
2009-02-03 17:30:13 +00:00
|
|
|
'field types' => array('test_field'),
|
|
|
|
'settings' => array(
|
|
|
|
'test_formatter_setting_multiple' => 'dummy test string',
|
|
|
|
),
|
|
|
|
'behaviors' => array(
|
|
|
|
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
|
|
|
),
|
2009-02-05 03:42:58 +00:00
|
|
|
),
|
2009-10-16 03:21:23 +00:00
|
|
|
'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',
|
|
|
|
),
|
|
|
|
),
|
2009-02-05 03:42:58 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-27 18:34:03 +00:00
|
|
|
* Implement hook_theme().
|
2009-02-05 03:42:58 +00:00
|
|
|
*/
|
|
|
|
function field_test_theme() {
|
|
|
|
return array(
|
|
|
|
'field_formatter_field_test_default' => array(
|
2009-10-23 22:24:19 +00:00
|
|
|
'render element' => 'element',
|
2009-02-05 03:42:58 +00:00
|
|
|
),
|
|
|
|
'field_formatter_field_test_multiple' => array(
|
2009-10-23 22:24:19 +00:00
|
|
|
'render element' => 'element',
|
2009-02-05 03:42:58 +00:00
|
|
|
),
|
2009-10-16 03:21:23 +00:00
|
|
|
'field_formatter_field_test_needs_additional_data' => array(
|
2009-10-23 22:24:19 +00:00
|
|
|
'render element' => 'element',
|
2009-10-16 03:21:23 +00:00
|
|
|
),
|
2009-02-05 03:42:58 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-10-16 03:21:23 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-05 03:42:58 +00:00
|
|
|
/**
|
|
|
|
* Theme function for 'field_test_default' formatter.
|
|
|
|
*/
|
2009-10-09 01:00:08 +00:00
|
|
|
function theme_field_formatter_field_test_default($variables) {
|
|
|
|
$element = $variables['element'];
|
|
|
|
|
2009-02-03 17:30:13 +00:00
|
|
|
$value = $element['#item']['value'];
|
|
|
|
$settings = $element['#settings'];
|
2009-02-05 03:42:58 +00:00
|
|
|
|
|
|
|
return $settings['test_formatter_setting'] . '|' . $value;
|
2009-02-03 17:30:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Theme function for 'field_test_multiple' formatter.
|
|
|
|
*/
|
2009-10-09 01:00:08 +00:00
|
|
|
function theme_field_formatter_field_test_multiple($variables) {
|
|
|
|
$element = $variables['element'];
|
|
|
|
|
2009-02-03 17:30:13 +00:00
|
|
|
$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;
|
2009-05-01 19:17:46 +00:00
|
|
|
}
|
|
|
|
|
2009-10-16 03:21:23 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2009-05-01 19:17:46 +00:00
|
|
|
/**
|
|
|
|
* Sample function to test default value assignment.
|
|
|
|
*/
|
|
|
|
function field_test_default_value($obj_type, $object, $field, $instance) {
|
|
|
|
return array(array('value' => 99));
|
2009-05-17 00:32:29 +00:00
|
|
|
}
|
2009-07-08 07:19:05 +00:00
|
|
|
|
2009-08-22 00:58:55 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2009-08-31 05:35:47 +00:00
|
|
|
function field_test_entity_info_translatable($obj_type = NULL, $translatable = NULL) {
|
2009-08-22 00:58:55 +00:00
|
|
|
$stored_value = &drupal_static(__FUNCTION__, array());
|
2009-10-16 02:04:44 +00:00
|
|
|
if (isset($obj_type)) {
|
2009-08-22 00:58:55 +00:00
|
|
|
$stored_value[$obj_type] = $translatable;
|
|
|
|
_field_info_collate_types(TRUE);
|
|
|
|
}
|
|
|
|
return $stored_value;
|
|
|
|
}
|
|
|
|
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* '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.'),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-10-14 14:55:12 +00:00
|
|
|
/**
|
|
|
|
* 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,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
- Patch #443422 by yched, bjaspan | chx, merlinofchaos, Scott Reynolds, plach, profix898, mattyoung: added support for pluggable 'per field' storage engine. Comes with documentation and tests.
The Field Attach API uses the Field Storage API to perform all "database access". Each Field Storage API hook function defines a primitive database operation such as read, write, or delete. The default field storage module, field_sql_storage.module, uses the local SQL database to implement these operations, but alternative field storage backends can choose to represent the data in SQL differently or use a completely different storage mechanism such as a cloud-based database.
2009-09-27 12:52:55 +00:00
|
|
|
/**
|
|
|
|
* 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) = field_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) = field_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) = field_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) = field_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 = field_info_fieldable_types($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] = field_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);
|
|
|
|
}
|
|
|
|
|
2009-07-08 07:19:05 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2009-09-26 15:57:39 +00:00
|
|
|
* just the most recently set value. field_test module hooks call
|
2009-07-08 07:19:05 +00:00
|
|
|
* 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) {
|
2009-08-25 16:38:51 +00:00
|
|
|
$memorize = &drupal_static(__FUNCTION__, NULL);
|
2009-07-08 07:19:05 +00:00
|
|
|
|
|
|
|
if (is_null($key)) {
|
2009-09-29 15:13:57 +00:00
|
|
|
$return = $memorize;
|
2009-07-08 07:19:05 +00:00
|
|
|
$memorize = array();
|
2009-09-29 15:13:57 +00:00
|
|
|
return $return;
|
2009-07-08 07:19:05 +00:00
|
|
|
}
|
|
|
|
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);
|
2009-08-11 14:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|