security update core+modules

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-26 18:38:56 +02:00
parent 2f45ea820a
commit 7c96373038
1022 changed files with 30319 additions and 11259 deletions

View File

@@ -1,4 +1,8 @@
<?php
/**
* @file
* Hooks provided by the Field module.
*/
/**
* @addtogroup hooks
@@ -23,14 +27,22 @@
* @see hook_field_extra_fields_alter()
*
* @return
* A nested array of 'pseudo-field' components. Each list is nested within
* the following keys: entity type, bundle name, context (either 'form' or
* A nested array of 'pseudo-field' elements. Each list is nested within the
* following keys: entity type, bundle name, context (either 'form' or
* 'display'). The keys are the name of the elements as appearing in the
* renderable array (either the entity form or the displayed entity). The
* value is an associative array:
* - label: The human readable name of the component.
* - description: A short description of the component contents.
* - label: The human readable name of the element.
* - description: A short description of the element contents.
* - weight: The default weight of the element.
* - edit: (optional) String containing markup (normally a link) used as the
* element's 'edit' operation in the administration interface. Only for
* 'form' context.
* - delete: (optional) String containing markup (normally a link) used as the
* element's 'delete' operation in the administration interface. Only for
* 'form' context.
*
* @ingroup field_types
*/
function hook_field_extra_fields() {
$extra['node']['poll'] = array(
@@ -70,6 +82,8 @@ function hook_field_extra_fields() {
* The associative array of 'pseudo-field' components.
*
* @see hook_field_extra_fields()
*
* @ingroup field_types
*/
function hook_field_extra_fields_alter(&$info) {
// Force node title to always be at the top of the list by default.
@@ -107,6 +121,9 @@ function hook_field_extra_fields_alter(&$info) {
/**
* Define Field API field types.
*
* Along with this hook, you also need to implement other hooks. See
* @link field_types Field Types API @endlink for more information.
*
* @return
* An array whose keys are field type names and whose values are arrays
* describing the field type, with the following key/value pairs:
@@ -193,8 +210,11 @@ function hook_field_info_alter(&$info) {
/**
* Define the Field API schema for a field structure.
*
* This hook MUST be defined in .install for it to be detected during
* installation and upgrade.
* This is invoked when a field is created, in order to obtain the database
* schema from the module that defines the field's type.
*
* This hook must be defined in the module's .install file for it to be detected
* during installation and upgrade.
*
* @param $field
* A field structure.
@@ -644,6 +664,8 @@ function hook_field_delete_revision($entity_type, $entity, $field, $instance, $l
* The source entity from which field values are being copied.
* @param $source_langcode
* The source language from which field values are being copied.
*
* @ingroup field_language
*/
function hook_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
// If the translating user is not permitted to use the assigned text format,
@@ -873,7 +895,7 @@ function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langco
'#type' => $instance['widget']['type'],
'#default_value' => isset($items[$delta]) ? $items[$delta] : '',
);
return $element;
return array('value' => $element);
}
/**
@@ -1238,7 +1260,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
*/
/**
* @ingroup field_attach
* @addtogroup field_attach
* @{
*/
@@ -1300,9 +1322,33 @@ function hook_field_attach_load($entity_type, $entities, $age, $options) {
* This hook is invoked after the field module has performed the operation.
*
* See field_attach_validate() for details and arguments.
*
* @param $entity_type
* The type of $entity; e.g., 'node' or 'user'.
* @param $entity
* The entity with fields to validate.
* @param array $errors
* The array of errors (keyed by field name, language code, and delta) that
* have already been reported for the entity. The function should add its
* errors to this array. Each error is an associative array with the following
* keys and values:
* - error: An error code (should be a string prefixed with the module name).
* - message: The human readable message to be displayed.
*/
function hook_field_attach_validate($entity_type, $entity, &$errors) {
// @todo Needs function body.
// Make sure any images in article nodes have an alt text.
if ($entity_type == 'node' && $entity->type == 'article' && !empty($entity->field_image)) {
foreach ($entity->field_image as $langcode => $items) {
foreach ($items as $delta => $item) {
if (!empty($item['fid']) && empty($item['alt'])) {
$errors['field_image'][$langcode][$delta][] = array(
'error' => 'field_example_invalid',
'message' => t('All images in articles need to have an alternative text set.'),
);
}
}
}
}
}
/**
@@ -1504,6 +1550,8 @@ function hook_field_attach_prepare_translation_alter(&$entity, $context) {
* - entity_type: The type of the entity to be displayed.
* - entity: The entity with fields to render.
* - langcode: The language code $entity has to be displayed in.
*
* @ingroup field_language
*/
function hook_field_language_alter(&$display_language, $context) {
// Do not apply core language fallback rules if they are disabled or if Locale
@@ -1525,6 +1573,8 @@ function hook_field_language_alter(&$display_language, $context) {
* An associative array containing:
* - entity_type: The type of the entity the field is attached to.
* - field: A field data structure.
*
* @ingroup field_language
*/
function hook_field_available_languages_alter(&$languages, $context) {
// Add an unavailable language.
@@ -1575,7 +1625,7 @@ function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new)
* @param $entity_type
* The type of entity; for example, 'node' or 'user'.
* @param $bundle
* The bundle that was just deleted.
* The name of the bundle that was just deleted.
* @param $instances
* An array of all instances that existed for the bundle before it was
* deleted.
@@ -1590,7 +1640,7 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) {
}
/**
* @} End of "defgroup field_attach".
* @} End of "addtogroup field_attach".
*/
/**
@@ -1735,11 +1785,14 @@ function hook_field_storage_details_alter(&$details, $field) {
* loaded.
*/
function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
$field_info = field_info_field_by_ids();
$load_current = $age == FIELD_LOAD_CURRENT;
foreach ($fields as $field_id => $ids) {
$field = $field_info[$field_id];
// By the time this hook runs, the relevant field definitions have been
// populated and cached in FieldInfo, so calling field_info_field_by_id()
// on each field individually is more efficient than loading all fields in
// memory upfront with field_info_field_by_ids().
$field = field_info_field_by_id($field_id);
$field_name = $field['field_name'];
$table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
@@ -1844,7 +1897,7 @@ function hook_field_storage_write($entity_type, $entity, $op, $fields) {
$items = (array) $entity->{$field_name}[$langcode];
$delta_count = 0;
foreach ($items as $delta => $item) {
// We now know we have someting to insert.
// We now know we have something to insert.
$do_insert = TRUE;
$record = array(
'entity_type' => $entity_type,
@@ -2247,6 +2300,10 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
}
}
/**
* @} End of "addtogroup field_storage
*/
/**
* Returns the maximum weight for the entity components handled by the module.
*
@@ -2260,9 +2317,12 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
* @param $context
* The context for which the maximum weight is requested. Either 'form', or
* the name of a view mode.
*
* @return
* The maximum weight of the entity's components, or NULL if no components
* were found.
*
* @ingroup field_info
*/
function hook_field_info_max_weight($entity_type, $bundle, $context) {
$weights = array();
@@ -2274,6 +2334,11 @@ function hook_field_info_max_weight($entity_type, $bundle, $context) {
return $weights ? max($weights) : NULL;
}
/**
* @addtogroup field_types
* @{
*/
/**
* Alters the display settings of a field before it gets displayed.
*
@@ -2340,6 +2405,10 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
}
}
/**
* @} End of "addtogroup field_types
*/
/**
* Alters the display settings of pseudo-fields before an entity is displayed.
*
@@ -2355,6 +2424,8 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
* - entity_type: The entity type; e.g., 'node' or 'user'.
* - bundle: The bundle name.
* - view_mode: The view mode, e.g. 'full', 'teaser'...
*
* @ingroup field_types
*/
function hook_field_extra_fields_display_alter(&$displays, $context) {
if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') {
@@ -2384,6 +2455,8 @@ function hook_field_extra_fields_display_alter(&$displays, $context) {
* - instance: The instance of the field.
*
* @see hook_field_widget_properties_alter()
*
* @ingroup field_widget
*/
function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
// Change a widget's type according to the time of day.
@@ -2394,10 +2467,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
}
}
/**
* @} End of "addtogroup field_storage".
*/
/**
* @addtogroup field_crud
* @{
@@ -2505,7 +2574,7 @@ function hook_field_delete_field($field) {
*
* @param $instance
* The instance as it is post-update.
* @param $prior_$instance
* @param $prior_instance
* The instance as it was pre-update.
*/
function hook_field_update_instance($instance, $prior_instance) {
@@ -2593,6 +2662,8 @@ function hook_field_purge_instance($instance) {
*
* @param $field
* The field being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge_field($field) {
$table_name = _field_sql_storage_tablename($field);
@@ -2610,6 +2681,8 @@ function hook_field_storage_purge_field($field) {
*
* @param $instance
* The instance being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge_field_instance($instance) {
db_delete('my_module_field_instance_info')
@@ -2631,6 +2704,8 @@ function hook_field_storage_purge_field_instance($instance) {
* The (possibly deleted) field whose data is being purged.
* @param $instance
* The deleted field instance whose data is being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
@@ -2670,6 +2745,8 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
*
* @return
* TRUE if the operation is allowed, and FALSE if the operation is denied.
*
* @ingroup field_types
*/
function hook_field_access($op, $field, $entity_type, $entity, $account) {
if ($field['field_name'] == 'field_of_interest' && $op == 'edit') {

View File

@@ -283,7 +283,6 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
'language' => NULL,
);
$options += $default_options;
$field_info = field_info_field_by_ids();
$fields = array();
$grouped_instances = array();
@@ -307,7 +306,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
foreach ($instances as $instance) {
$field_id = $instance['field_id'];
$field_name = $instance['field_name'];
$field = $field_info[$field_id];
$field = field_info_field_by_id($field_id);
$function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
if (function_exists($function)) {
// Add the field to the list of fields to invoke the hook on.
@@ -319,7 +318,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
// Unless a language suggestion is provided we iterate on all the
// available languages.
$available_languages = field_available_languages($entity_type, $field);
$language = !empty($options['language'][$id]) ? $options['language'][$id] : $options['language'];
$language = is_array($options['language']) && !empty($options['language'][$id]) ? $options['language'][$id] : $options['language'];
$languages = _field_language_suggestion($available_languages, $language, $field_name);
foreach ($languages as $langcode) {
$grouped_items[$field_id][$langcode][$id] = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
@@ -555,16 +554,23 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
* @param $langcode
* The language the field values are going to be entered, if no language
* is provided the default site language will be used.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
*
* @see field_form_get_state()
* @see field_form_set_state()
*/
function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL) {
function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
// Set #parents to 'top-level' by default.
$form += array('#parents' => array());
// If no language is provided use the default site language.
$options = array('language' => field_valid_language($langcode));
$options['language'] = field_valid_language($langcode);
$form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
// Add custom weight handling.
@@ -614,7 +620,6 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
* non-deleted fields are operated on.
*/
function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
$field_info = field_info_field_by_ids();
$load_current = $age == FIELD_LOAD_CURRENT;
// Merge default options.
@@ -692,7 +697,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
}
// Collect the storage backend if the field has not been loaded yet.
if (!isset($skip_fields[$field_id])) {
$field = $field_info[$field_id];
$field = field_info_field_by_id($field_id);
$storages[$field['storage']['type']][$field_id][] = $load_current ? $id : $vid;
}
}
@@ -709,7 +714,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
_field_invoke_multiple('load', $entity_type, $queried_entities, $age, $null, $options);
// Invoke hook_field_attach_load(): let other modules act on loading the
// entitiy.
// entity.
module_invoke_all('field_attach_load', $entity_type, $queried_entities, $age, $options);
// Build cache data.
@@ -769,13 +774,21 @@ function field_attach_load_revision($entity_type, $entities, $options = array())
* If validation errors are found, a FieldValidationException is thrown. The
* 'errors' property contains the array of errors, keyed by field name,
* language and delta.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
*/
function field_attach_validate($entity_type, $entity) {
function field_attach_validate($entity_type, $entity, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
$errors = array();
// Check generic, field-type-agnostic errors first.
_field_invoke_default('validate', $entity_type, $entity, $errors);
$null = NULL;
_field_invoke_default('validate', $entity_type, $entity, $errors, $null, $options);
// Check field-type specific errors.
_field_invoke('validate', $entity_type, $entity, $errors);
_field_invoke('validate', $entity_type, $entity, $errors, $null, $options);
// Let other modules validate the entity.
// Avoid module_invoke_all() to let $errors be taken by reference.
@@ -817,14 +830,21 @@ function field_attach_validate($entity_type, $entity) {
* full form structure, or a sub-element of a larger form.
* @param $form_state
* An associative array containing the current state of the form.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
*/
function field_attach_form_validate($entity_type, $entity, $form, &$form_state) {
function field_attach_form_validate($entity_type, $entity, $form, &$form_state, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
// Extract field values from submitted values.
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
// Perform field_level validation.
try {
field_attach_validate($entity_type, $entity);
field_attach_validate($entity_type, $entity, $options);
}
catch (FieldValidationException $e) {
// Pass field-level validation errors back to widgets for accurate error
@@ -836,7 +856,7 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
}
}
_field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state);
_field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state, $options);
}
}
@@ -857,12 +877,19 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
* full form structure, or a sub-element of a larger form.
* @param $form_state
* An associative array containing the current state of the form.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
*/
function field_attach_submit($entity_type, $entity, $form, &$form_state) {
// Extract field values from submitted values.
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
function field_attach_submit($entity_type, $entity, $form, &$form_state, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
_field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
// Extract field values from submitted values.
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state, $options);
_field_invoke_default('submit', $entity_type, $entity, $form, $form_state, $options);
// Let other modules act on submitting the entity.
// Avoid module_invoke_all() to let $form_state be taken by reference.
@@ -949,6 +976,12 @@ function field_attach_insert($entity_type, $entity) {
/**
* Save field data for an existing entity.
*
* When calling this function outside an entity save operation be sure to
* clear caches for the entity:
* @code
* entity_get_controller($entity_type)->resetCache(array($entity_id))
* @endcode
*
* @param $entity_type
* The type of $entity; e.g. 'node' or 'user'.
* @param $entity
@@ -1093,9 +1126,16 @@ function field_attach_delete_revision($entity_type, $entity) {
* @param $langcode
* (Optional) The language the field values are to be shown in. If no language
* is provided the current language is used.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
*/
function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL) {
$options = array('language' => array());
function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
$options['language'] = array();
// To ensure hooks are only run once per entity, only process items without
// the _field_view_prepared flag.
@@ -1167,14 +1207,21 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
* @param $langcode
* The language the field values are to be shown in. If no language is
* provided the current language is used.
* @param array $options
* An associative array of additional options. See _field_invoke() for
* details.
* @return
* A renderable array for the field values.
*/
function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL) {
function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL, $options = array()) {
// Validate $options since this is a new parameter added after Drupal 7 was
// released.
$options = is_array($options) ? $options : array();
// Determine the actual language to display for each field, given the
// languages available in the field data.
$display_language = field_language($entity_type, $entity, NULL, $langcode);
$options = array('language' => $display_language);
$options['language'] = $display_language;
// Invoke field_default_view().
$null = NULL;

View File

@@ -60,11 +60,11 @@ function field_create_field($field) {
}
// Field type is required.
if (empty($field['type'])) {
throw new FieldException('Attempt to create a field with no type.');
throw new FieldException(format_string('Attempt to create field @field_name with no type.', array('@field_name' => $field['field_name'])));
}
// Field name cannot contain invalid characters.
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $field['field_name'])) {
throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
throw new FieldException(format_string('Attempt to create a field @field_name with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character', array('@field_name' => $field['field_name'])));
}
// Field name cannot be longer than 32 characters. We use drupal_strlen()
@@ -244,9 +244,11 @@ function field_update_field($field) {
// $prior_field may no longer be right.
module_load_install($field['module']);
$schema = (array) module_invoke($field['module'], 'field_schema', $field);
$schema += array('columns' => array(), 'indexes' => array());
$schema += array('columns' => array(), 'indexes' => array(), 'foreign keys' => array());
// 'columns' are hardcoded in the field type.
$field['columns'] = $schema['columns'];
// 'foreign keys' are hardcoded in the field type.
$field['foreign keys'] = $schema['foreign keys'];
// 'indexes' can be both hardcoded in the field type, and specified in the
// incoming $field definition.
$field += array(
@@ -319,7 +321,11 @@ function field_read_field($field_name, $include_additional = array()) {
* Reads in fields that match an array of conditions.
*
* @param array $params
* An array of conditions to match against.
* An array of conditions to match against. Keys are columns from the
* 'field_config' table, values are conditions to match. Additionally,
* conditions on the 'entity_type' and 'bundle' columns from the
* 'field_config_instance' table are supported (select fields having an
* instance on a given bundle).
* @param array $include_additional
* The default behavior of this function is to not return fields that
* are inactive or have been deleted. Setting
@@ -337,8 +343,21 @@ function field_read_fields($params = array(), $include_additional = array()) {
// Turn the conditions into a query.
foreach ($params as $key => $value) {
// Allow filtering on the 'entity_type' and 'bundle' columns of the
// field_config_instance table.
if ($key == 'entity_type' || $key == 'bundle') {
if (empty($fci_join)) {
$fci_join = $query->join('field_config_instance', 'fci', 'fc.id = fci.field_id');
}
$key = 'fci.' . $key;
}
else {
$key = 'fc.' . $key;
}
$query->condition($key, $value);
}
if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) {
$query
->condition('fc.active', 1)
@@ -505,17 +524,30 @@ function field_create_instance($instance) {
* Updates an instance of a field.
*
* @param $instance
* An associative array representing an instance structure. The required
* keys and values are:
* An associative array representing an instance structure. The following
* required array elements specify which field instance is being updated:
* - entity_type: The type of the entity the field is attached to.
* - bundle: The bundle this field belongs to.
* - field_name: The name of an existing field.
* Read-only_id properties are assigned automatically. Any other
* properties specified in $instance overwrite the existing values for
* the instance.
* The other array elements represent properties of the instance, and all
* properties must be specified or their default values will be used (except
* internal-use properties, which are assigned automatically). To avoid
* losing the previously stored properties of the instance when making a
* change, first load the instance with field_info_instance(), then override
* the values you want to override, and finally save using this function.
* Example:
* @code
* // Fetch an instance info array.
* $instance_info = field_info_instance($entity_type, $field_name, $bundle_name);
* // Change a single property in the instance definition.
* $instance_info['required'] = TRUE;
* // Write the changed definition back.
* field_update_instance($instance_info);
* @endcode
*
* @throws FieldException
*
* @see field_info_instance()
* @see field_create_instance()
*/
function field_update_instance($instance) {

View File

@@ -5,7 +5,14 @@ version = VERSION
core = 7.x
files[] = field.module
files[] = field.attach.inc
files[] = field.info.class.inc
files[] = tests/field.test
dependencies[] = field_sql_storage
required = TRUE
stylesheets[all][] = theme/field.css
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -0,0 +1,684 @@
<?php
/*
* @file
* Definition of the FieldInfo class.
*/
/**
* Provides field and instance definitions for the current runtime environment.
*
* A FieldInfo object is created and statically persisted through the request
* by the _field_info_field_cache() function. The object properties act as a
* "static cache" of fields and instances definitions.
*
* The preferred way to access definitions is through the getBundleInstances()
* method, which keeps cache entries per bundle, storing both fields and
* instances for a given bundle. Fields used in multiple bundles are duplicated
* in several cache entries, and are merged into a single list in the memory
* cache. Cache entries are loaded for bundles as a whole, optimizing memory
* and CPU usage for the most common pattern of iterating over all instances of
* a bundle rather than accessing a single instance.
*
* The getFields() and getInstances() methods, which return all existing field
* and instance definitions, are kept mainly for backwards compatibility, and
* should be avoided when possible, since they load and persist in memory a
* potentially large array of information. In many cases, the lightweight
* getFieldMap() method should be preferred.
*/
class FieldInfo {
/**
* Lightweight map of fields across entity types and bundles.
*
* @var array
*/
protected $fieldMap;
/**
* List of $field structures keyed by ID. Includes deleted fields.
*
* @var array
*/
protected $fieldsById = array();
/**
* Mapping of field names to the ID of the corresponding non-deleted field.
*
* @var array
*/
protected $fieldIdsByName = array();
/**
* Whether $fieldsById contains all field definitions or a subset.
*
* @var bool
*/
protected $loadedAllFields = FALSE;
/**
* Separately tracks requested field names or IDs that do not exist.
*
* @var array
*/
protected $unknownFields = array();
/**
* Instance definitions by bundle.
*
* @var array
*/
protected $bundleInstances = array();
/**
* Whether $bundleInstances contains all instances definitions or a subset.
*
* @var bool
*/
protected $loadedAllInstances = FALSE;
/**
* Separately tracks requested bundles that are empty (or do not exist).
*
* @var array
*/
protected $emptyBundles = array();
/**
* Extra fields by bundle.
*
* @var array
*/
protected $bundleExtraFields = array();
/**
* Clears the "static" and persistent caches.
*/
public function flush() {
$this->fieldMap = NULL;
$this->fieldsById = array();
$this->fieldIdsByName = array();
$this->loadedAllFields = FALSE;
$this->unknownFields = array();
$this->bundleInstances = array();
$this->loadedAllInstances = FALSE;
$this->emptyBundles = array();
$this->bundleExtraFields = array();
cache_clear_all('field_info:', 'cache_field', TRUE);
}
/**
* Collects a lightweight map of fields across bundles.
*
* @return
* An array keyed by field name. Each value is an array with two entries:
* - type: The field type.
* - bundles: The bundles in which the field appears, as an array with
* entity types as keys and the array of bundle names as values.
*/
public function getFieldMap() {
// Read from the "static" cache.
if ($this->fieldMap !== NULL) {
return $this->fieldMap;
}
// Read from persistent cache.
if ($cached = cache_get('field_info:field_map', 'cache_field')) {
$map = $cached->data;
// Save in "static" cache.
$this->fieldMap = $map;
return $map;
}
$map = array();
$query = db_query('SELECT fc.type, fci.field_name, fci.entity_type, fci.bundle FROM {field_config_instance} fci INNER JOIN {field_config} fc ON fc.id = fci.field_id WHERE fc.active = 1 AND fc.storage_active = 1 AND fc.deleted = 0 AND fci.deleted = 0');
foreach ($query as $row) {
$map[$row->field_name]['bundles'][$row->entity_type][] = $row->bundle;
$map[$row->field_name]['type'] = $row->type;
}
// Save in "static" and persistent caches.
$this->fieldMap = $map;
if (lock_acquire('field_info:field_map')) {
cache_set('field_info:field_map', $map, 'cache_field');
lock_release('field_info:field_map');
}
return $map;
}
/**
* Returns all active fields, including deleted ones.
*
* @return
* An array of field definitions, keyed by field ID.
*/
public function getFields() {
// Read from the "static" cache.
if ($this->loadedAllFields) {
return $this->fieldsById;
}
// Read from persistent cache.
if ($cached = cache_get('field_info:fields', 'cache_field')) {
$this->fieldsById = $cached->data;
}
else {
// Collect and prepare fields.
foreach (field_read_fields(array(), array('include_deleted' => TRUE)) as $field) {
$this->fieldsById[$field['id']] = $this->prepareField($field);
}
// Store in persistent cache.
if (lock_acquire('field_info:fields')) {
cache_set('field_info:fields', $this->fieldsById, 'cache_field');
lock_release('field_info:fields');
}
}
// Fill the name/ID map.
foreach ($this->fieldsById as $field) {
if (!$field['deleted']) {
$this->fieldIdsByName[$field['field_name']] = $field['id'];
}
}
$this->loadedAllFields = TRUE;
return $this->fieldsById;
}
/**
* Retrieves all active, non-deleted instances definitions.
*
* @param $entity_type
* (optional) The entity type.
*
* @return
* If $entity_type is not set, all instances keyed by entity type and bundle
* name. If $entity_type is set, all instances for that entity type, keyed
* by bundle name.
*/
public function getInstances($entity_type = NULL) {
// If the full list is not present in "static" cache yet.
if (!$this->loadedAllInstances) {
// Read from persistent cache.
if ($cached = cache_get('field_info:instances', 'cache_field')) {
$this->bundleInstances = $cached->data;
}
else {
// Collect and prepare instances.
// We also need to populate the static field cache, since it will not
// be set by subsequent getBundleInstances() calls.
$this->getFields();
// Initialize empty arrays for all existing entity types and bundles.
// This is not strictly needed, but is done to preserve the behavior of
// field_info_instances() before http://drupal.org/node/1915646.
foreach (field_info_bundles() as $existing_entity_type => $bundles) {
foreach ($bundles as $bundle => $bundle_info) {
$this->bundleInstances[$existing_entity_type][$bundle] = array();
}
}
foreach (field_read_instances() as $instance) {
$field = $this->getField($instance['field_name']);
$instance = $this->prepareInstance($instance, $field['type']);
$this->bundleInstances[$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
}
// Store in persistent cache.
if (lock_acquire('field_info:instances')) {
cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
lock_release('field_info:instances');
}
}
$this->loadedAllInstances = TRUE;
}
if (isset($entity_type)) {
return isset($this->bundleInstances[$entity_type]) ? $this->bundleInstances[$entity_type] : array();
}
else {
return $this->bundleInstances;
}
}
/**
* Returns a field definition from a field name.
*
* This method only retrieves active, non-deleted fields.
*
* @param $field_name
* The field name.
*
* @return
* The field definition, or NULL if no field was found.
*/
public function getField($field_name) {
// Read from the "static" cache.
if (isset($this->fieldIdsByName[$field_name])) {
$field_id = $this->fieldIdsByName[$field_name];
return $this->fieldsById[$field_id];
}
if (isset($this->unknownFields[$field_name])) {
return;
}
// Do not check the (large) persistent cache, but read the definition.
// Cache miss: read from definition.
if ($field = field_read_field($field_name)) {
$field = $this->prepareField($field);
// Save in the "static" cache.
$this->fieldsById[$field['id']] = $field;
$this->fieldIdsByName[$field['field_name']] = $field['id'];
return $field;
}
else {
$this->unknownFields[$field_name] = TRUE;
}
}
/**
* Returns a field definition from a field ID.
*
* This method only retrieves active fields, deleted or not.
*
* @param $field_id
* The field ID.
*
* @return
* The field definition, or NULL if no field was found.
*/
public function getFieldById($field_id) {
// Read from the "static" cache.
if (isset($this->fieldsById[$field_id])) {
return $this->fieldsById[$field_id];
}
if (isset($this->unknownFields[$field_id])) {
return;
}
// No persistent cache, fields are only persistently cached as part of a
// bundle.
// Cache miss: read from definition.
if ($fields = field_read_fields(array('id' => $field_id), array('include_deleted' => TRUE))) {
$field = current($fields);
$field = $this->prepareField($field);
// Store in the static cache.
$this->fieldsById[$field['id']] = $field;
if (!$field['deleted']) {
$this->fieldIdsByName[$field['field_name']] = $field['id'];
}
return $field;
}
else {
$this->unknownFields[$field_id] = TRUE;
}
}
/**
* Retrieves the instances for a bundle.
*
* The function also populates the corresponding field definitions in the
* "static" cache.
*
* @param $entity_type
* The entity type.
* @param $bundle
* The bundle name.
*
* @return
* The array of instance definitions, keyed by field name.
*/
public function getBundleInstances($entity_type, $bundle) {
// Read from the "static" cache.
if (isset($this->bundleInstances[$entity_type][$bundle])) {
return $this->bundleInstances[$entity_type][$bundle];
}
if (isset($this->emptyBundles[$entity_type][$bundle])) {
return array();
}
// Read from the persistent cache.
if ($cached = cache_get("field_info:bundle:$entity_type:$bundle", 'cache_field')) {
$info = $cached->data;
// Extract the field definitions and save them in the "static" cache.
foreach ($info['fields'] as $field) {
if (!isset($this->fieldsById[$field['id']])) {
$this->fieldsById[$field['id']] = $field;
if (!$field['deleted']) {
$this->fieldIdsByName[$field['field_name']] = $field['id'];
}
}
}
unset($info['fields']);
// Store the instance definitions in the "static" cache'. Empty (or
// non-existent) bundles are stored separately, so that they do not
// pollute the global list returned by getInstances().
if ($info['instances']) {
$this->bundleInstances[$entity_type][$bundle] = $info['instances'];
}
else {
$this->emptyBundles[$entity_type][$bundle] = TRUE;
}
return $info['instances'];
}
// Cache miss: collect from the definitions.
$instances = array();
// Collect the fields in the bundle.
$params = array('entity_type' => $entity_type, 'bundle' => $bundle);
$fields = field_read_fields($params);
// This iterates on non-deleted instances, so deleted fields are kept out of
// the persistent caches.
foreach (field_read_instances($params) as $instance) {
$field = $fields[$instance['field_name']];
$instance = $this->prepareInstance($instance, $field['type']);
$instances[$field['field_name']] = $instance;
// If the field is not in our global "static" list yet, add it.
if (!isset($this->fieldsById[$field['id']])) {
$field = $this->prepareField($field);
$this->fieldsById[$field['id']] = $field;
$this->fieldIdsByName[$field['field_name']] = $field['id'];
}
}
// Store in the 'static' cache'. Empty (or non-existent) bundles are stored
// separately, so that they do not pollute the global list returned by
// getInstances().
if ($instances) {
$this->bundleInstances[$entity_type][$bundle] = $instances;
}
else {
$this->emptyBundles[$entity_type][$bundle] = TRUE;
}
// The persistent cache additionally contains the definitions of the fields
// involved in the bundle.
$cache = array(
'instances' => $instances,
'fields' => array()
);
foreach ($instances as $instance) {
$cache['fields'][] = $this->fieldsById[$instance['field_id']];
}
if (lock_acquire("field_info:bundle:$entity_type:$bundle")) {
cache_set("field_info:bundle:$entity_type:$bundle", $cache, 'cache_field');
lock_release("field_info:bundle:$entity_type:$bundle");
}
return $instances;
}
/**
* Retrieves the "extra fields" for a bundle.
*
* @param $entity_type
* The entity type.
* @param $bundle
* The bundle name.
*
* @return
* The array of extra fields.
*/
public function getBundleExtraFields($entity_type, $bundle) {
// Read from the "static" cache.
if (isset($this->bundleExtraFields[$entity_type][$bundle])) {
return $this->bundleExtraFields[$entity_type][$bundle];
}
// Read from the persistent cache.
if ($cached = cache_get("field_info:bundle_extra:$entity_type:$bundle", 'cache_field')) {
$this->bundleExtraFields[$entity_type][$bundle] = $cached->data;
return $this->bundleExtraFields[$entity_type][$bundle];
}
// Cache miss: read from hook_field_extra_fields(). Note: given the current
// shape of the hook, we have no other way than collecting extra fields on
// all bundles.
$info = array();
$extra = module_invoke_all('field_extra_fields');
drupal_alter('field_extra_fields', $extra);
// Merge in saved settings.
if (isset($extra[$entity_type][$bundle])) {
$info = $this->prepareExtraFields($extra[$entity_type][$bundle], $entity_type, $bundle);
}
// Store in the 'static' and persistent caches.
$this->bundleExtraFields[$entity_type][$bundle] = $info;
if (lock_acquire("field_info:bundle_extra:$entity_type:$bundle")) {
cache_set("field_info:bundle_extra:$entity_type:$bundle", $info, 'cache_field');
lock_release("field_info:bundle_extra:$entity_type:$bundle");
}
return $this->bundleExtraFields[$entity_type][$bundle];
}
/**
* Prepares a field definition for the current run-time context.
*
* @param $field
* The raw field structure as read from the database.
*
* @return
* The field definition completed for the current runtime context.
*/
public function prepareField($field) {
// Make sure all expected field settings are present.
$field['settings'] += field_info_field_settings($field['type']);
$field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
// Add storage details.
$details = (array) module_invoke($field['storage']['module'], 'field_storage_details', $field);
drupal_alter('field_storage_details', $details, $field);
$field['storage']['details'] = $details;
// Populate the list of bundles using the field.
$field['bundles'] = array();
if (!$field['deleted']) {
$map = $this->getFieldMap();
if (isset($map[$field['field_name']])) {
$field['bundles'] = $map[$field['field_name']]['bundles'];
}
}
return $field;
}
/**
* Prepares an instance definition for the current run-time context.
*
* @param $instance
* The raw instance structure as read from the database.
* @param $field_type
* The field type.
*
* @return
* The field instance array completed for the current runtime context.
*/
public function prepareInstance($instance, $field_type) {
// Make sure all expected instance settings are present.
$instance['settings'] += field_info_instance_settings($field_type);
// Set a default value for the instance.
if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) {
$instance['default_value'] = NULL;
}
// Prepare widget settings.
$instance['widget'] = $this->prepareInstanceWidget($instance['widget'], $field_type);
// Prepare display settings.
foreach ($instance['display'] as $view_mode => $display) {
$instance['display'][$view_mode] = $this->prepareInstanceDisplay($display, $field_type);
}
// Fall back to 'hidden' for view modes configured to use custom display
// settings, and for which the instance has no explicit settings.
$entity_info = entity_get_info($instance['entity_type']);
$view_modes = array_merge(array('default'), array_keys($entity_info['view modes']));
$view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
foreach ($view_modes as $view_mode) {
if ($view_mode == 'default' || !empty($view_mode_settings[$view_mode]['custom_settings'])) {
if (!isset($instance['display'][$view_mode])) {
$instance['display'][$view_mode] = array(
'type' => 'hidden',
'label' => 'above',
'settings' => array(),
'weight' => 0,
);
}
}
}
return $instance;
}
/**
* Prepares widget properties for the current run-time context.
*
* @param $widget
* Widget specifications as found in $instance['widget'].
* @param $field_type
* The field type.
*
* @return
* The widget properties completed for the current runtime context.
*/
public function prepareInstanceWidget($widget, $field_type) {
$field_type_info = field_info_field_types($field_type);
// Fill in default values.
$widget += array(
'type' => $field_type_info['default_widget'],
'settings' => array(),
'weight' => 0,
);
$widget_type_info = field_info_widget_types($widget['type']);
// Fall back to default formatter if formatter type is not available.
if (!$widget_type_info) {
$widget['type'] = $field_type_info['default_widget'];
$widget_type_info = field_info_widget_types($widget['type']);
}
$widget['module'] = $widget_type_info['module'];
// Fill in default settings for the widget.
$widget['settings'] += field_info_widget_settings($widget['type']);
return $widget;
}
/**
* Adapts display specifications to the current run-time context.
*
* @param $display
* Display specifications as found in $instance['display']['a_view_mode'].
* @param $field_type
* The field type.
*
* @return
* The display properties completed for the current runtime context.
*/
public function prepareInstanceDisplay($display, $field_type) {
$field_type_info = field_info_field_types($field_type);
// Fill in default values.
$display += array(
'label' => 'above',
'type' => $field_type_info['default_formatter'],
'settings' => array(),
'weight' => 0,
);
if ($display['type'] != 'hidden') {
$formatter_type_info = field_info_formatter_types($display['type']);
// Fall back to default formatter if formatter type is not available.
if (!$formatter_type_info) {
$display['type'] = $field_type_info['default_formatter'];
$formatter_type_info = field_info_formatter_types($display['type']);
}
$display['module'] = $formatter_type_info['module'];
// Fill in default settings for the formatter.
$display['settings'] += field_info_formatter_settings($display['type']);
}
return $display;
}
/**
* Prepares 'extra fields' for the current run-time context.
*
* @param $extra_fields
* The array of extra fields, as collected in hook_field_extra_fields().
* @param $entity_type
* The entity type.
* @param $bundle
* The bundle name.
*
* @return
* The list of extra fields completed for the current runtime context.
*/
public function prepareExtraFields($extra_fields, $entity_type, $bundle) {
$entity_type_info = entity_get_info($entity_type);
$bundle_settings = field_bundle_settings($entity_type, $bundle);
$extra_fields += array('form' => array(), 'display' => array());
$result = array();
// Extra fields in forms.
foreach ($extra_fields['form'] as $name => $field_data) {
$settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array();
if (isset($settings['weight'])) {
$field_data['weight'] = $settings['weight'];
}
$result['form'][$name] = $field_data;
}
// Extra fields in displayed entities.
$data = $extra_fields['display'];
foreach ($extra_fields['display'] as $name => $field_data) {
$settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
$view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
foreach ($view_modes as $view_mode) {
if (isset($settings[$view_mode])) {
$field_data['display'][$view_mode] = $settings[$view_mode];
}
else {
$field_data['display'][$view_mode] = array(
'weight' => $field_data['weight'],
'visible' => TRUE,
);
}
}
unset($field_data['weight']);
$result['display'][$name] = $field_data;
}
return $result;
}
}

View File

@@ -5,6 +5,32 @@
* Field Info API, providing information about available fields and field types.
*/
/**
* Retrieves the FieldInfo object for the current request.
*
* @return FieldInfo
* An instance of the FieldInfo class.
*/
function _field_info_field_cache() {
// Use the advanced drupal_static() pattern, since this is called very often.
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['field_info_field_cache'] = &drupal_static(__FUNCTION__);
}
$field_info = &$drupal_static_fast['field_info_field_cache'];
if (!isset($field_info)) {
// @todo The registry should save the need for an explicit include, but not
// a couple upgrade tests (DisabledNodeTypeTestCase,
// FilterFormatUpgradePathTestCase...) break in a strange way without it.
include_once dirname(__FILE__) . '/field.info.class.inc';
$field_info = new FieldInfo();
}
return $field_info;
}
/**
* @defgroup field_info Field Info API
* @{
@@ -34,7 +60,50 @@ function field_info_cache_clear() {
entity_info_cache_clear();
_field_info_collate_types(TRUE);
_field_info_collate_fields(TRUE);
_field_info_field_cache()->flush();
}
/**
* Collates all information on existing fields and instances.
*
* Deprecated. This function is kept to ensure backwards compatibility, but has
* a serious performance impact, and should be absolutely avoided.
* See http://drupal.org/node/1915646.
*
* Use the regular field_info_*() API functions to access the information, or
* field_info_cache_clear() to clear the cached data.
*/
function _field_info_collate_fields($reset = FALSE) {
if ($reset) {
_field_info_field_cache()->flush();
return;
}
$cache = _field_info_field_cache();
// Collect fields, and build the array of IDs keyed by field_name.
$fields = $cache->getFields();
$field_ids = array();
foreach ($fields as $id => $field) {
if (!$field['deleted']) {
$field_ids[$field['field_name']] = $id;
}
}
// Collect extra fields for all entity types.
$extra_fields = array();
foreach (field_info_bundles() as $entity_type => $bundles) {
foreach ($bundles as $bundle => $info) {
$extra_fields[$entity_type][$bundle] = $cache->getBundleExtraFields($entity_type, $bundle);
}
}
return array(
'fields' => $fields,
'field_ids' => $field_ids,
'instances' => $cache->getInstances(),
'extra_fields' => $extra_fields,
);
}
/**
@@ -154,96 +223,11 @@ function _field_info_collate_types($reset = FALSE) {
}
drupal_alter('field_storage_info', $info['storage types']);
cache_set("field_info_types:$langcode", $info, 'cache_field');
}
}
return $info;
}
/**
* Collates all information on existing fields and instances.
*
* @param $reset
* If TRUE, clear the cache. The information will be rebuilt from the
* database next time it is needed. Defaults to FALSE.
*
* @return
* If $reset is TRUE, nothing.
* If $reset is FALSE, an array containing the following elements:
* - fields: Array of existing fields, keyed by field ID. This element
* lists deleted and non-deleted fields, but not inactive ones.
* Each field has an additional element, 'bundles', which is an array
* of all non-deleted instances of that field.
* - field_ids: Array of field IDs, keyed by field name. This element
* only lists non-deleted, active fields.
* - instances: Array of existing instances, keyed by entity type, bundle
* name and field name. This element only lists non-deleted instances
* whose field is active.
*/
function _field_info_collate_fields($reset = FALSE) {
static $info;
if ($reset) {
$info = NULL;
cache_clear_all('field_info_fields', 'cache_field');
return;
}
if (!isset($info)) {
if ($cached = cache_get('field_info_fields', 'cache_field')) {
$info = $cached->data;
}
else {
$definitions = array(
'field_ids' => field_read_fields(array(), array('include_deleted' => 1)),
'instances' => field_read_instances(),
);
// Populate 'fields' with all fields, keyed by ID.
$info['fields'] = array();
foreach ($definitions['field_ids'] as $key => $field) {
$info['fields'][$key] = $definitions['field_ids'][$key] = _field_info_prepare_field($field);
// Set the cache if we can acquire a lock.
if (lock_acquire("field_info_types:$langcode")) {
cache_set("field_info_types:$langcode", $info, 'cache_field');
lock_release("field_info_types:$langcode");
}
// Build an array of field IDs for non-deleted fields, keyed by name.
$info['field_ids'] = array();
foreach ($info['fields'] as $key => $field) {
if (!$field['deleted']) {
$info['field_ids'][$field['field_name']] = $key;
}
}
// Populate 'instances'. Only non-deleted instances are considered.
$info['instances'] = array();
foreach (field_info_bundles() as $entity_type => $bundles) {
foreach ($bundles as $bundle => $bundle_info) {
$info['instances'][$entity_type][$bundle] = array();
}
}
foreach ($definitions['instances'] as $instance) {
$field = $info['fields'][$instance['field_id']];
$instance = _field_info_prepare_instance($instance, $field);
$info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
// Enrich field definitions with the list of bundles where they have
// instances. NOTE: Deleted fields in $info['field_ids'] are not
// enriched because all of their instances are deleted, too, and
// are thus not in $definitions['instances'].
$info['fields'][$instance['field_id']]['bundles'][$instance['entity_type']][] = $instance['bundle'];
}
// Populate 'extra_fields'.
$extra = module_invoke_all('field_extra_fields');
drupal_alter('field_extra_fields', $extra);
// Merge in saved settings.
foreach ($extra as $entity_type => $bundles) {
foreach ($bundles as $bundle => $extra_fields) {
$extra_fields = _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle);
$info['extra_fields'][$entity_type][$bundle] = $extra_fields;
}
}
cache_set('field_info_fields', $info, 'cache_field');
}
}
@@ -253,190 +237,66 @@ function _field_info_collate_fields($reset = FALSE) {
/**
* Prepares a field definition for the current run-time context.
*
* Since the field was last saved or updated, new field settings can be
* expected.
* The functionality has moved to the FieldInfo class. This function is kept as
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
*
* @param $field
* The raw field structure as read from the database.
* @see FieldInfo::prepareField()
*/
function _field_info_prepare_field($field) {
// Make sure all expected field settings are present.
$field['settings'] += field_info_field_settings($field['type']);
$field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
// Add storage details.
$details = (array) module_invoke($field['storage']['module'], 'field_storage_details', $field);
drupal_alter('field_storage_details', $details, $field, $instance);
$field['storage']['details'] = $details;
// Initialize the 'bundles' list.
$field['bundles'] = array();
return $field;
$cache = _field_info_field_cache();
return $cache->prepareField($field);
}
/**
* Prepares an instance definition for the current run-time context.
*
* Since the instance was last saved or updated, a number of things might have
* changed: widgets or formatters disabled, new settings expected, new view
* modes added...
* The functionality has moved to the FieldInfo class. This function is kept as
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
*
* @param $instance
* The raw instance structure as read from the database.
* @param $field
* The field structure for the instance.
*
* @return
* Field instance array.
* @see FieldInfo::prepareInstance()
*/
function _field_info_prepare_instance($instance, $field) {
// Make sure all expected instance settings are present.
$instance['settings'] += field_info_instance_settings($field['type']);
// Set a default value for the instance.
if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) {
$instance['default_value'] = NULL;
}
$instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
foreach ($instance['display'] as $view_mode => $display) {
$instance['display'][$view_mode] = _field_info_prepare_instance_display($field, $display);
}
// Fallback to 'hidden' for view modes configured to use custom display
// settings, and for which the instance has no explicit settings.
$entity_info = entity_get_info($instance['entity_type']);
$view_modes = array_merge(array('default'), array_keys($entity_info['view modes']));
$view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
foreach ($view_modes as $view_mode) {
if ($view_mode == 'default' || !empty($view_mode_settings[$view_mode]['custom_settings'])) {
if (!isset($instance['display'][$view_mode])) {
$instance['display'][$view_mode] = array(
'type' => 'hidden',
'label' => 'above',
'settings' => array(),
'weight' => 0,
);
}
}
}
return $instance;
$cache = _field_info_field_cache();
return $cache->prepareInstance($instance, $field['type']);
}
/**
* Adapts display specifications to the current run-time context.
*
* @param $field
* The field structure for the instance.
* @param $display
* Display specifications as found in
* $instance['display']['some_view_mode'].
* The functionality has moved to the FieldInfo class. This function is kept as
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
*
* @see FieldInfo::prepareInstanceDisplay()
*/
function _field_info_prepare_instance_display($field, $display) {
$field_type = field_info_field_types($field['type']);
// Fill in default values.
$display += array(
'label' => 'above',
'type' => $field_type['default_formatter'],
'settings' => array(),
'weight' => 0,
);
if ($display['type'] != 'hidden') {
$formatter_type = field_info_formatter_types($display['type']);
// Fallback to default formatter if formatter type is not available.
if (!$formatter_type) {
$display['type'] = $field_type['default_formatter'];
$formatter_type = field_info_formatter_types($display['type']);
}
$display['module'] = $formatter_type['module'];
// Fill in default settings for the formatter.
$display['settings'] += field_info_formatter_settings($display['type']);
}
return $display;
$cache = _field_info_field_cache();
return $cache->prepareInstanceDisplay($display, $field['type']);
}
/**
* Prepares widget specifications for the current run-time context.
*
* @param $field
* The field structure for the instance.
* @param $widget
* Widget specifications as found in $instance['widget'].
* The functionality has moved to the FieldInfo class. This function is kept as
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
*
* @see FieldInfo::prepareInstanceWidget()
*/
function _field_info_prepare_instance_widget($field, $widget) {
$field_type = field_info_field_types($field['type']);
// Fill in default values.
$widget += array(
'type' => $field_type['default_widget'],
'settings' => array(),
'weight' => 0,
);
$widget_type = field_info_widget_types($widget['type']);
// Fallback to default formatter if formatter type is not available.
if (!$widget_type) {
$widget['type'] = $field_type['default_widget'];
$widget_type = field_info_widget_types($widget['type']);
}
$widget['module'] = $widget_type['module'];
// Fill in default settings for the widget.
$widget['settings'] += field_info_widget_settings($widget['type']);
return $widget;
$cache = _field_info_field_cache();
return $cache->prepareInstanceWidget($widget, $field['type']);
}
/**
* Prepares 'extra fields' for the current run-time context.
*
* @param $extra_fields
* The array of extra fields, as collected in hook_field_extra_fields().
* @param $entity_type
* The entity type.
* @param $bundle
* The bundle name.
* The functionality has moved to the FieldInfo class. This function is kept as
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
*
* @see FieldInfo::prepareExtraFields()
*/
function _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle) {
$entity_type_info = entity_get_info($entity_type);
$bundle_settings = field_bundle_settings($entity_type, $bundle);
$extra_fields += array('form' => array(), 'display' => array());
$result = array();
// Extra fields in forms.
foreach ($extra_fields['form'] as $name => $field_data) {
$settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array();
if (isset($settings['weight'])) {
$field_data['weight'] = $settings['weight'];
}
$result['form'][$name] = $field_data;
}
// Extra fields in displayed entities.
$data = $extra_fields['display'];
foreach ($extra_fields['display'] as $name => $field_data) {
$settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
$view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
foreach ($view_modes as $view_mode) {
if (isset($settings[$view_mode])) {
$field_data['display'][$view_mode] = $settings[$view_mode];
}
else {
$field_data['display'][$view_mode] = array(
'weight' => $field_data['weight'],
'visible' => TRUE,
);
}
}
unset($field_data['weight']);
$result['display'][$name] = $field_data;
}
return $result;
$cache = _field_info_field_cache();
return $cache->prepareExtraFields($extra_fields, $entity_type, $bundle);
}
/**
@@ -583,22 +443,62 @@ function field_info_bundles($entity_type = NULL) {
return $bundles;
}
/**
* Returns a lightweight map of fields across bundles.
*
* The function only returns active, non deleted fields.
*
* @return
* An array keyed by field name. Each value is an array with two entries:
* - type: The field type.
* - bundles: The bundles in which the field appears, as an array with entity
* types as keys and the array of bundle names as values.
* Example:
* @code
* array(
* 'body' => array(
* 'bundles' => array(
* 'node' => array('page', 'article'),
* ),
* 'type' => 'text_with_summary',
* ),
* );
* @endcode
*/
function field_info_field_map() {
$cache = _field_info_field_cache();
return $cache->getFieldMap();
}
/**
* Returns all field definitions.
*
* Use of this function should be avoided when possible, since it loads and
* statically caches a potentially large array of information. Use
* field_info_field_map() instead.
*
* When iterating over the fields present in a given bundle after a call to
* field_info_instances($entity_type, $bundle), it is recommended to use
* field_info_field() on each individual field instead.
*
* @return
* An array of field definitions, keyed by field name. Each field has an
* additional property, 'bundles', which is an array of all the bundles to
* which this field belongs keyed by entity type.
*
* @see field_info_field_map()
*/
function field_info_fields() {
$cache = _field_info_field_cache();
$info = $cache->getFields();
$fields = array();
$info = _field_info_collate_fields();
foreach ($info['fields'] as $key => $field) {
foreach ($info as $key => $field) {
if (!$field['deleted']) {
$fields[$field['field_name']] = $field;
}
}
return $fields;
}
@@ -620,10 +520,8 @@ function field_info_fields() {
* @see field_info_field_by_id()
*/
function field_info_field($field_name) {
$info = _field_info_collate_fields();
if (isset($info['field_ids'][$field_name])) {
return $info['fields'][$info['field_ids'][$field_name]];
}
$cache = _field_info_field_cache();
return $cache->getField($field_name);
}
/**
@@ -641,17 +539,19 @@ function field_info_field($field_name) {
* @see field_info_field()
*/
function field_info_field_by_id($field_id) {
$info = _field_info_collate_fields();
if (isset($info['fields'][$field_id])) {
return $info['fields'][$field_id];
}
$cache = _field_info_field_cache();
return $cache->getFieldById($field_id);
}
/**
* Returns the same data as field_info_field_by_id() for every field.
*
* This function is typically used when handling all fields of some entities
* to avoid thousands of calls to field_info_field_by_id().
* Use of this function should be avoided when possible, since it loads and
* statically caches a potentially large array of information.
*
* When iterating over the fields present in a given bundle after a call to
* field_info_instances($entity_type, $bundle), it is recommended to use
* field_info_field() on each individual field instead.
*
* @return
* An array, each key is a field ID and the values are field arrays as
@@ -662,41 +562,57 @@ function field_info_field_by_id($field_id) {
* @see field_info_field_by_id()
*/
function field_info_field_by_ids() {
$info = _field_info_collate_fields();
return $info['fields'];
$cache = _field_info_field_cache();
return $cache->getFields();
}
/**
* Retrieves information about field instances.
*
* Use of this function to retrieve instances across separate bundles (i.e.
* when the $bundle parameter is NULL) should be avoided when possible, since
* it loads and statically caches a potentially large array of information. Use
* field_info_field_map() instead.
*
* When retrieving the instances of a specific bundle (i.e. when both
* $entity_type and $bundle_name are provided), the function also populates a
* static cache with the corresponding field definitions, allowing fast
* retrieval of field_info_field() later in the request.
*
* @param $entity_type
* The entity type for which to return instances.
* (optional) The entity type for which to return instances.
* @param $bundle_name
* The bundle name for which to return instances.
* (optional) The bundle name for which to return instances. If $entity_type
* is NULL, the $bundle_name parameter is ignored.
*
* @return
* If $entity_type is not set, return all instances keyed by entity type and
* bundle name. If $entity_type is set, return all instances for that entity
* type, keyed by bundle name. If $entity_type and $bundle_name are set, return
* all instances for that bundle.
*
* @see field_info_field_map()
*/
function field_info_instances($entity_type = NULL, $bundle_name = NULL) {
$info = _field_info_collate_fields();
$cache = _field_info_field_cache();
if (isset($entity_type) && isset($bundle_name)) {
return isset($info['instances'][$entity_type][$bundle_name]) ? $info['instances'][$entity_type][$bundle_name] : array();
if (!isset($entity_type)) {
return $cache->getInstances();
}
elseif (isset($entity_type)) {
return isset($info['instances'][$entity_type]) ? $info['instances'][$entity_type] : array();
}
else {
return $info['instances'];
if (!isset($bundle_name)) {
return $cache->getInstances($entity_type);
}
return $cache->getBundleInstances($entity_type, $bundle_name);
}
/**
* Returns an array of instance data for a specific field and bundle.
*
* The function populates a static cache with all fields and instances used in
* the bundle, allowing fast retrieval of field_info_field() or
* field_info_instance() later in the request.
*
* @param $entity_type
* The entity type for the instance.
* @param $field_name
@@ -709,9 +625,10 @@ function field_info_instances($entity_type = NULL, $bundle_name = NULL) {
* NULL if the instance does not exist.
*/
function field_info_instance($entity_type, $field_name, $bundle_name) {
$info = _field_info_collate_fields();
if (isset($info['instances'][$entity_type][$bundle_name][$field_name])) {
return $info['instances'][$entity_type][$bundle_name][$field_name];
$cache = _field_info_field_cache();
$info = $cache->getBundleInstances($entity_type, $bundle_name);
if (isset($info[$field_name])) {
return $info[$field_name];
}
}
@@ -769,11 +686,10 @@ function field_info_instance($entity_type, $field_name, $bundle_name) {
* The array of pseudo-field elements in the bundle.
*/
function field_info_extra_fields($entity_type, $bundle, $context) {
$info = _field_info_collate_fields();
if (isset($info['extra_fields'][$entity_type][$bundle][$context])) {
return $info['extra_fields'][$entity_type][$bundle][$context];
}
return array();
$cache = _field_info_field_cache();
$info = $cache->getBundleExtraFields($entity_type, $bundle);
return isset($info[$context]) ? $info[$context] : array();
}
/**

View File

@@ -128,7 +128,7 @@ function field_schema() {
'not null' => TRUE,
'default' => ''
),
'entity_type' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
@@ -162,6 +162,7 @@ function field_schema() {
),
);
$schema['cache_field'] = drupal_get_schema_unprocessed('system', 'cache');
$schema['cache_field']['description'] = 'Cache table for the Field module to store already built field information.';
return $schema;
}
@@ -459,6 +460,13 @@ function field_update_7002() {
}
}
/**
* Add the FieldInfo class to the class registry.
*/
function field_update_7003() {
// Empty update to force a rebuild of the registry.
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

View File

@@ -342,17 +342,6 @@ function field_cron() {
field_purge_batch($limit);
}
/**
* Implements hook_modules_uninstalled().
*/
function field_modules_uninstalled($modules) {
module_load_include('inc', 'field', 'field.crud');
foreach ($modules as $module) {
// TODO D7: field_module_delete is not yet implemented
// field_module_delete($module);
}
}
/**
* Implements hook_system_info_alter().
*
@@ -819,9 +808,9 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display =
*
* This function can be used by third-party modules that need to output an
* isolated field.
* - Do not use inside node (or other entities) templates, use
* - Do not use inside node (or any other entity) templates; use
* render($content[FIELD_NAME]) instead.
* - Do not use to display all fields in an entity, use
* - Do not use to display all fields in an entity; use
* field_attach_prepare_view() and field_attach_view() instead.
* - The field_view_value() function can be used to output a single formatted
* field value, without label or wrapping field markup.
@@ -873,7 +862,8 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
if ($field = field_info_field($field_name)) {
if (is_array($display)) {
// When using custom display settings, fill in default values.
$display = _field_info_prepare_instance_display($field, $display);
$cache = _field_info_field_cache();
$display = $cache->prepareInstanceDisplay($display, $field["type"]);
}
// Hook invocations are done through the _field_invoke() functions in
@@ -904,6 +894,7 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
'entity' => $entity,
'view_mode' => '_custom',
'display' => $display,
'language' => $langcode,
);
drupal_alter('field_attach_view', $result, $context);
@@ -946,20 +937,30 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
*/
function field_has_data($field) {
$query = new EntityFieldQuery();
return (bool) $query
->fieldCondition($field)
$query = $query->fieldCondition($field)
->range(0, 1)
->count()
// Neutralize the 'entity_field_access' query tag added by
// field_sql_storage_field_storage_query(). The result cannot depend on the
// access grants of the current user.
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT')
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
return (bool) $query
->execute() || (bool) $query
->age(FIELD_LOAD_REVISION)
->execute();
}
/**
* Determine whether the user has access to a given field.
*
* This function does not determine whether access is granted to the entity
* itself, only the specific field. Callers are responsible for ensuring that
* entity access is also respected. For example, when checking field access for
* nodes, check node_access() before checking field_access(), and when checking
* field access for entities using the Entity API contributed module,
* check entity_access() before checking field_access().
*
* @param $op
* The operation to be performed. Possible values:
* - 'edit'
@@ -1197,7 +1198,7 @@ function _element_validate_integer($element, &$form_state) {
* Use element_validate_integer_positive() instead.
*
* @deprecated
* @see element_validate_number_positive()
* @see element_validate_integer_positive()
*/
function _element_validate_integer_positive($element, &$form_state) {
element_validate_integer_positive($element, $form_state);

View File

@@ -6,3 +6,9 @@ core = 7.x
dependencies[] = field
files[] = field_sql_storage.test
required = TRUE
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -64,6 +64,49 @@ function _field_sql_storage_revision_tablename($field) {
}
}
/**
* Generates a table alias for a field data table.
*
* The table alias is unique for each unique combination of field name
* (represented by $tablename), delta_group and language_group.
*
* @param $tablename
* The name of the data table for this field.
* @param $field_key
* The numeric key of this field in this query.
* @param $query
* The EntityFieldQuery that is executed.
*
* @return
* A string containing the generated table alias.
*/
function _field_sql_storage_tablealias($tablename, $field_key, EntityFieldQuery $query) {
// No conditions present: use a unique alias.
if (empty($query->fieldConditions[$field_key])) {
return $tablename . $field_key;
}
// Find the delta and language condition values and append them to the alias.
$condition = $query->fieldConditions[$field_key];
$alias = $tablename;
$has_group_conditions = FALSE;
foreach (array('delta', 'language') as $column) {
if (isset($condition[$column . '_group'])) {
$alias .= '_' . $column . '_' . $condition[$column . '_group'];
$has_group_conditions = TRUE;
}
}
// Return the alias when it has delta/language group conditions.
if ($has_group_conditions) {
return $alias;
}
// Return a unique alias in other cases.
return $tablename . $field_key;
}
/**
* Generate a column name for a field data table.
*
@@ -188,7 +231,7 @@ function _field_sql_storage_schema($field) {
foreach ($field['foreign keys'] as $specifier => $specification) {
$real_name = _field_sql_storage_indexname($field['field_name'], $specifier);
$current['foreign keys'][$real_name]['table'] = $specification['table'];
foreach ($specification['columns'] as $column => $referenced) {
foreach ($specification['columns'] as $column_name => $referenced) {
$sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name);
$current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
}
@@ -324,11 +367,14 @@ function field_sql_storage_field_storage_delete_field($field) {
* Implements hook_field_storage_load().
*/
function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) {
$field_info = field_info_field_by_ids();
$load_current = $age == FIELD_LOAD_CURRENT;
foreach ($fields as $field_id => $ids) {
$field = $field_info[$field_id];
// By the time this hook runs, the relevant field definitions have been
// populated and cached in FieldInfo, so calling field_info_field_by_id()
// on each field individually is more efficient than loading all fields in
// memory upfront with field_info_field_by_ids().
$field = field_info_field_by_id($field_id);
$field_name = $field['field_name'];
$table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
@@ -419,7 +465,7 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
$items = (array) $entity->{$field_name}[$langcode];
$delta_count = 0;
foreach ($items as $delta => $item) {
// We now know we have someting to insert.
// We now know we have something to insert.
$do_insert = TRUE;
$record = array(
'entity_type' => $entity_type,
@@ -501,17 +547,21 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
$id_key = 'revision_id';
}
$table_aliases = array();
$query_tables = NULL;
// Add tables for the fields used.
foreach ($query->fields as $key => $field) {
$tablename = $tablename_function($field);
// Every field needs a new table.
$table_alias = $tablename . $key;
$table_alias = _field_sql_storage_tablealias($tablename, $key, $query);
$table_aliases[$key] = $table_alias;
if ($key) {
$select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
if (!isset($query_tables[$table_alias])) {
$select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
}
}
else {
$select_query = db_select($tablename, $table_alias);
// Store a reference to the list of joined tables.
$query_tables =& $select_query->getTables();
// Allow queries internal to the Field API to opt out of the access
// check, for situations where the query's results should not depend on
// the access grants for the current user.

View File

@@ -126,7 +126,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is inserted correctly"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is inserted correctly", array('%delta' => $delta)));
}
else {
$this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted.");
@@ -145,7 +145,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is updated correctly"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is updated correctly", array('%delta' => $delta)));
}
else {
$this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated.");
@@ -175,7 +175,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Update with no field_name entry leaves value $delta untouched"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Update with no field_name entry leaves value %delta untouched", array('%delta' => $delta)));
}
}
@@ -183,7 +183,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$entity->{$this->field_name} = NULL;
field_attach_update($entity_type, $entity);
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
$this->assertEqual(count($rows), 0, t("Update with an empty field_name entry empties the field."));
$this->assertEqual(count($rows), 0, "Update with an empty field_name entry empties the field.");
}
/**
@@ -326,7 +326,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
// Ensure that the field tables are still there.
foreach (_field_sql_storage_schema($prior_field) as $table_name => $table_info) {
$this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
$this->assertTrue(db_table_exists($table_name), format_string('Table %table exists.', array('%table' => $table_name)));
}
}
@@ -345,8 +345,8 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
// Verify the indexes we will create do not exist yet.
foreach ($tables as $table) {
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), format_string("No index named value exists in %table", array('%table' => $table)));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), format_string("No index named value_format exists in %table", array('%table' => $table)));
}
// Add data so the table cannot be dropped.
@@ -358,21 +358,21 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$field = array('field_name' => $field_name, 'indexes' => array('value' => array('value')));
field_update_field($field);
foreach ($tables as $table) {
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in $table"));
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value created in %table", array('%table' => $table)));
}
// Add a different index, removing the existing custom one.
$field = array('field_name' => $field_name, 'indexes' => array('value_format' => array('value', 'format')));
field_update_field($field);
foreach ($tables as $table) {
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in $table"));
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), format_string("Index on value_format created in %table", array('%table' => $table)));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value removed in %table", array('%table' => $table)));
}
// Verify that the tables were not dropped.
$entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
field_attach_load('test_entity', array(0 => $entity));
$this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], 'field data', t("Index changes performed without dropping the tables"));
$this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], 'field data', "Index changes performed without dropping the tables");
}
/**
@@ -387,19 +387,19 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
// The storage details are indexed by a storage engine type.
$this->assertTrue(array_key_exists('sql', $field['storage']['details']), t('The storage type is SQL.'));
$this->assertTrue(array_key_exists('sql', $field['storage']['details']), 'The storage type is SQL.');
// The SQL details are indexed by table name.
$details = $field['storage']['details']['sql'];
$this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), t('Table name is available in the instance array.'));
$this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), t('Revision table name is available in the instance array.'));
$this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), 'Table name is available in the instance array.');
$this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), 'Revision table name is available in the instance array.');
// Test current and revision storage details together because the columns
// are the same.
foreach ((array) $this->field['columns'] as $column_name => $attributes) {
$storage_column_name = _field_sql_storage_columnname($this->field['field_name'], $column_name);
$this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
$this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
$this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
$this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
}
}
@@ -407,21 +407,180 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
* Test foreign key support.
*/
function testFieldSqlStorageForeignKeys() {
// Create a decimal field.
// Create a 'shape' field, with a configurable foreign key (see
// field_test_field_schema()).
$field_name = 'testfield';
$field = array('field_name' => $field_name, 'type' => 'text');
$field = field_create_field($field);
// Retrieve the field and instance with field_info and verify the foreign
// keys are in place.
$foreign_key_name = 'shape';
$field = array('field_name' => $field_name, 'type' => 'shape', 'settings' => array('foreign_key_name' => $foreign_key_name));
field_create_field($field);
// Retrieve the field definition and check that the foreign key is in place.
$field = field_info_field($field_name);
$this->assertEqual($field['foreign keys']['format']['table'], 'filter_format', t('Foreign key table name preserved through CRUD'));
$this->assertEqual($field['foreign keys']['format']['columns']['format'], 'format', t('Foreign key column name preserved through CRUD'));
$this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD');
$this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD');
// Update the field settings, it should update the foreign key definition
// too.
$foreign_key_name = 'color';
$field['settings']['foreign_key_name'] = $foreign_key_name;
field_update_field($field);
// Retrieve the field definition and check that the foreign key is in place.
$field = field_info_field($field_name);
$this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
$this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
// Now grab the SQL schema and verify that too.
$schema = drupal_get_schema(_field_sql_storage_tablename($field));
$this->assertEqual(count($schema['foreign keys']), 1, t("There is 1 foreign key in the schema"));
$schema = drupal_get_schema(_field_sql_storage_tablename($field), TRUE);
$this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
$foreign_key = reset($schema['foreign keys']);
$filter_column = _field_sql_storage_columnname($field['field_name'], 'format');
$this->assertEqual($foreign_key['table'], 'filter_format', t('Foreign key table name preserved in the schema'));
$this->assertEqual($foreign_key['columns'][$filter_column], 'format', t('Foreign key column name preserved in the schema'));
$foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_name);
$this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
$this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
}
/**
* Test handling multiple conditions on one column of a field.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsSameColumn() {
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 1);
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 2);
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 3);
field_test_entity_save($entity);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($this->field_name, 'value', 1, '<>', 0, LANGUAGE_NONE);
$query->fieldCondition($this->field_name, 'value', 2, '<>', 0, LANGUAGE_NONE);
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(1, count($tables), 'The query contains just one table.');
// Clean up.
unset($GLOBALS['test_query']);
}
/**
* Test handling multiple conditions on multiple columns of one field.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsDifferentColumns() {
// Create the multi-column shape field
$field_name = strtolower($this->randomName());
$field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
$field = field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle'
);
$instance = field_create_instance($instance);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'X');
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'B', 'color' => 'X');
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'Y');
field_test_entity_save($entity);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($field_name, 'shape', 'B', '=', 'something', LANGUAGE_NONE);
$query->fieldCondition($field_name, 'color', 'X', '=', 'something', LANGUAGE_NONE);
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(1, count($tables), 'The query contains just one table.');
// Clean up.
unset($GLOBALS['test_query']);
}
/**
* Test handling multiple conditions on multiple columns of one field for multiple languages.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsDifferentColumnsMultipleLanguages() {
field_test_entity_info_translatable('test_entity', TRUE);
// Create the multi-column shape field
$field_name = strtolower($this->randomName());
$field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4, 'translatable' => TRUE);
$field = field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'settings' => array(
// Prevent warning from field_test_field_load().
'test_hook_field_load' => FALSE,
),
);
$instance = field_create_instance($instance);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'X');
$entity->{$field_name}['en'][0] = array('shape' => 'B', 'color' => 'Y');
field_test_entity_save($entity);
$entity = field_test_entity_test_load($entity->ftid);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($field_name, 'color', 'X', '=', NULL, LANGUAGE_NONE);
$query->fieldCondition($field_name, 'shape', 'B', '=', NULL, 'en');
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(2, count($tables), 'The query contains two tables.');
// Clean up.
unset($GLOBALS['test_query']);
}
}

View File

@@ -6,3 +6,9 @@ core = 7.x
dependencies[] = field
dependencies[] = options
files[] = tests/list.test
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -51,9 +51,9 @@ class ListFieldTestCase extends FieldTestCase {
// All three options appear.
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
// Use one of the values in an actual entity, and check that this value
// cannot be removed from the list.
@@ -77,19 +77,19 @@ class ListFieldTestCase extends FieldTestCase {
field_update_field($this->field);
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
// Completely new options appear.
$this->field['settings']['allowed_values'] = array(10 => 'Update', 20 => 'Twenty');
field_update_field($this->field);
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][2]), t('Option 2 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][10]), t('Option 10 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][20]), t('Option 20 exists'));
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
$this->assertTrue(empty($form[$this->field_name][$langcode][2]), 'Option 2 does not exist');
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
$this->assertTrue(!empty($form[$this->field_name][$langcode][10]), 'Option 10 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][20]), 'Option 20 exists');
// Options are reset when a new field with the same name is created.
field_delete_field($this->field_name);
@@ -107,9 +107,9 @@ class ListFieldTestCase extends FieldTestCase {
$this->instance = field_create_instance($this->instance);
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
}
}
@@ -233,20 +233,20 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit integer keys.
$string = "0|Zero\n2|Two";
$array = array('0' => 'Zero', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Integer keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-integer keys.
$this->assertAllowedValuesInput("1.1|One", 'keys must be integers', t('Non integer keys are rejected.'));
$this->assertAllowedValuesInput("abc|abc", 'keys must be integers', t('Non integer keys are rejected.'));
$this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
$this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', t('Mixed lists are rejected.'));
$this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -256,22 +256,22 @@ class ListFieldUITestCase extends FieldTestCase {
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', t('Unkeyed lists are rejected once the field has data.'));
$this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n1|One\n2|Two";
$array = array('0' => 'Zero', '1' => 'One', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -284,19 +284,19 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit numeric keys.
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, t('Integer keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n.5|Point five\n1.0|One";
$array = array('0' => 'Zero', '0.5' => 'Point five', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-numeric keys.
$this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', t('Non numeric keys are rejected.'));
$this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', t('Mixed lists are rejected.'));
$this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -306,22 +306,22 @@ class ListFieldUITestCase extends FieldTestCase {
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput("Zero\nOne", 'invalid input', t('Unkeyed lists are rejected once the field has data.'));
$this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n.5|Point five\n2|Two";
$array = array('0' => 'Zero', '0.5' => 'Point five', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -334,21 +334,21 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit keys.
$string = "zero|Zero\none|One";
$array = array('zero' => 'Zero', 'one' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Explicit keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
// Check that values can be added and removed.
$string = "zero|Zero\ntwo|Two";
$array = array('zero' => 'Zero', 'two' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Mixed list of keyed and unkeyed values.
$string = "zero|Zero\nOne\n";
$array = array('zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Mixed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
// Overly long keys.
$this->assertAllowedValuesInput("zero|Zero\n" . $this->randomName(256) . "|One", 'each key must be a string at most 255 characters long', t('Overly long keys are rejected.'));
$this->assertAllowedValuesInput("zero|Zero\n" . $this->randomName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -361,22 +361,22 @@ class ListFieldUITestCase extends FieldTestCase {
// data.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are still accepted once the field has data.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "Zero\nOne\nTwo";
$array = array('Zero' => 'Zero', 'One' => 'One', 'Two' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "Zero";
$array = array('Zero' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -395,15 +395,15 @@ class ListFieldUITestCase extends FieldTestCase {
'off' => $off,
);
$this->drupalPost($this->admin_path, $edit, t('Save settings'));
$this->assertText("Saved field_list_boolean configuration.", t("The 'On' and 'Off' form fields work for boolean fields."));
$this->assertText("Saved field_list_boolean configuration.", "The 'On' and 'Off' form fields work for boolean fields.");
// Test the allowed_values on the field settings form.
$this->drupalGet($this->admin_path);
$this->assertFieldByName('on', $on, t("The 'On' value is stored correctly."));
$this->assertFieldByName('off', $off, t("The 'Off' value is stored correctly."));
$this->assertFieldByName('on', $on, "The 'On' value is stored correctly.");
$this->assertFieldByName('off', $off, "The 'Off' value is stored correctly.");
$field = field_info_field($this->field_name);
$this->assertEqual($field['settings']['allowed_values'], $allowed_values, t('The allowed value is correct'));
$this->assertFalse(isset($field['settings']['on']), t('The on value is not saved into settings'));
$this->assertFalse(isset($field['settings']['off']), t('The off value is not saved into settings'));
$this->assertEqual($field['settings']['allowed_values'], $allowed_values, 'The allowed value is correct');
$this->assertFalse(isset($field['settings']['on']), 'The on value is not saved into settings');
$this->assertFalse(isset($field['settings']['off']), 'The off value is not saved into settings');
}
/**

View File

@@ -4,3 +4,9 @@ core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
dependencies[] = field
files[] = number.test
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -58,7 +58,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
// Display creation form.
$this->drupalGet('test-entity/add/test-bundle');
$langcode = LANGUAGE_NONE;
$this->assertFieldByName("{$this->field['field_name']}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertFieldByName("{$this->field['field_name']}[$langcode][0][value]", '', 'Widget is displayed');
// Submit a signed decimal value within the allowed precision and scale.
$value = '-1234.5678';
@@ -68,8 +68,8 @@ class NumberFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(round($value, 2), t('Value is displayed.'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
$this->assertRaw(round($value, 2), 'Value is displayed.');
// Try to create entries with more than one decimal separator; assert fail.
$wrong_entries = array(
@@ -89,7 +89,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
$this->assertText(
t('There should only be one decimal separator (@separator)',
array('@separator' => $this->field['settings']['decimal_separator'])),
t('Correctly failed to save decimal value with more than one decimal point.')
'Correctly failed to save decimal value with more than one decimal point.'
);
}

View File

@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
dependencies[] = field
files[] = options.test
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -1,7 +1,7 @@
<?php
/**
* @file
* @file
* Tests for options.module.
*/
@@ -85,7 +85,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-card-1-$langcode-0");
$this->assertNoFieldChecked("edit-card-1-$langcode-1");
$this->assertNoFieldChecked("edit-card-1-$langcode-2");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -139,7 +139,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-card-2-$langcode-0");
$this->assertNoFieldChecked("edit-card-2-$langcode-1");
$this->assertNoFieldChecked("edit-card-2-$langcode-2");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array(
@@ -178,7 +178,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
"card_2[$langcode][2]" => TRUE,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', t('Validation error was displayed.'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array(
@@ -225,19 +225,19 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A required field without any value has a "none" option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), t('A required select list has a "Select a value" choice.'));
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), 'A required select list has a "Select a value" choice.');
// With no field data, nothing is selected.
$this->assertNoOptionSelected("edit-card-1-$langcode", '_none');
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
// Submit form: select invalid 'none' option.
$edit = array("card_1[$langcode]" => '_none');
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), t('Cannot save a required field when selecting "none" from the select list.'));
$this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), 'Cannot save a required field when selecting "none" from the select list.');
// Submit form: select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -247,7 +247,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form: check that the right options are selected.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A required field with a value has no 'none' option.
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), t('A required select list with an actual value has no "none" choice.'));
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), 'A required select list with an actual value has no "none" choice.');
$this->assertOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
@@ -259,7 +259,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A non-required field has a 'none' option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), t('A non-required select list has a "None" choice.'));
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), 'A non-required select list has a "None" choice.');
// Submit form: Unselect the option.
$edit = array("card_1[$langcode]" => '_none');
$this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
@@ -276,8 +276,8 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Group 1', t('Option groups are displayed.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -323,7 +323,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array("card_2[$langcode][]" => array(0 => 0, 2 => 2));
@@ -350,7 +350,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Submit form: select the three options while the field accepts only 2.
$edit = array("card_2[$langcode][]" => array(0 => 0, 1 => 1, 2 => 2));
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', t('Validation error was displayed.'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array("card_2[$langcode][]" => array());
@@ -359,7 +359,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Test the 'None' option.
// Check that the 'none' option has no efect if actual options are selected
// Check that the 'none' option has no effect if actual options are selected
// as well.
$edit = array("card_2[$langcode][]" => array('_none' => '_none', 0 => 0));
$this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
@@ -374,7 +374,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$instance['required'] = TRUE;
field_update_instance($instance);
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), t('A required select list does not have an empty key.'));
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), 'A required select list does not have an empty key.');
// We do not have to test that a required select list with one option is
// auto-selected because the browser does it for us.
@@ -393,8 +393,8 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Group 1', t('Option groups are displayed.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array("card_2[$langcode][]" => array(0 => 0));
@@ -438,7 +438,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form: with no field data, option is unchecked.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
$this->assertNoFieldChecked("edit-bool-$langcode");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Submit form: check the option.
$edit = array("bool[$langcode]" => TRUE);
@@ -483,13 +483,13 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertText(
'Use field label instead of the "On value" as label ',
t('Display setting checkbox available.')
'Display setting checkbox available.'
);
$this->assertFieldByXPath(
'*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="MyOnValue "]',
TRUE,
t('Default case shows "On value"')
'Default case shows "On value"'
);
// Enable setting
@@ -502,16 +502,16 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->drupalGet($fieldEditUrl);
$this->assertText(
'Use field label instead of the "On value" as label ',
t('Display setting checkbox is available')
'Display setting checkbox is available'
);
$this->assertFieldChecked(
'edit-instance-widget-settings-display-label',
t('Display settings checkbox checked')
'Display settings checkbox checked'
);
$this->assertFieldByXPath(
'*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="' . $this->bool['field_name'] . ' "]',
TRUE,
t('Display label changes label of the checkbox')
'Display label changes label of the checkbox'
);
}
}

View File

@@ -6,3 +6,9 @@ core = 7.x
dependencies[] = field
files[] = text.test
required = TRUE
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -12,9 +12,9 @@ Drupal.behaviors.textSummary = {
$summaries.once('text-summary-wrapper').each(function(index) {
var $summary = $(this);
var $summaryLabel = $summary.find('label');
var $summaryLabel = $summary.find('label').first();
var $full = $widget.find('.text-full').eq(index).closest('.form-item');
var $fullLabel = $full.find('label');
var $fullLabel = $full.find('label').first();
// Create a placeholder label when the field cardinality is
// unlimited or greater than 1.
@@ -23,24 +23,28 @@ Drupal.behaviors.textSummary = {
}
// Setup the edit/hide summary link.
var $link = $('<span class="field-edit-link">(<a class="link-edit-summary" href="#">' + Drupal.t('Hide summary') + '</a>)</span>').toggle(
function () {
var $link = $('<span class="field-edit-link">(<a class="link-edit-summary" href="#">' + Drupal.t('Hide summary') + '</a>)</span>');
var $a = $link.find('a');
var toggleClick = true;
$link.bind('click', function (e) {
if (toggleClick) {
$summary.hide();
$(this).find('a').html(Drupal.t('Edit summary')).end().appendTo($fullLabel);
return false;
},
function () {
$summary.show();
$(this).find('a').html(Drupal.t('Hide summary')).end().appendTo($summaryLabel);
return false;
$a.html(Drupal.t('Edit summary'));
$link.appendTo($fullLabel);
}
).appendTo($summaryLabel);
else {
$summary.show();
$a.html(Drupal.t('Hide summary'));
$link.appendTo($summaryLabel);
}
toggleClick = !toggleClick;
return false;
}).appendTo($summaryLabel);
// If no summary is set, hide the summary field.
if ($(this).find('.text-summary').val() == '') {
$link.click();
}
return;
});
});
}

View File

@@ -245,7 +245,7 @@ function text_field_formatter_settings_summary($field, $instance, $view_mode) {
$summary = '';
if (strpos($display['type'], '_trimmed') !== FALSE) {
$summary = t('Trim length') . ': ' . $settings['trim_length'];
$summary = t('Trim length') . ': ' . check_plain($settings['trim_length']);
}
return $summary;

View File

@@ -110,8 +110,8 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display creation form.
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '1', t('Format selector is not displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '1', 'Format selector is not displayed');
// Submit with some value.
$value = $this->randomName();
@@ -121,7 +121,7 @@ class TextFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
// Display the entity.
$entity = field_test_entity_test_load($id);
@@ -179,8 +179,8 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display the creation form. Since the user only has access to one format,
// no format selector will be displayed.
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is not displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', 'Format selector is not displayed');
// Submit with data that should be filtered.
$value = '<em>' . $this->randomName() . '</em>';
@@ -190,14 +190,14 @@ class TextFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
// Display the entity.
$entity = field_test_entity_test_load($id);
$entity->content = field_attach_view($entity_type, $entity, 'full');
$this->content = drupal_render($entity->content);
$this->assertNoRaw($value, t('HTML tags are not displayed.'));
$this->assertRaw(check_plain($value), t('Escaped HTML is displayed correctly.'));
$this->assertNoRaw($value, 'HTML tags are not displayed.');
$this->assertRaw(check_plain($value), 'Escaped HTML is displayed correctly.');
// Create a new text format that does not escape HTML, and grant the user
// access to it.
@@ -219,21 +219,21 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display edition form.
// We should now have a 'text format' selector.
$this->drupalGet('test-entity/manage/' . $id . '/edit');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", NULL, t('Widget is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][format]", NULL, t('Format selector is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", NULL, 'Widget is displayed');
$this->assertFieldByName("{$this->field_name}[$langcode][0][format]", NULL, 'Format selector is displayed');
// Edit and change the text format to the new one that was created.
$edit = array(
"{$this->field_name}[$langcode][0][format]" => $format_id,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), t('Entity was updated'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
// Display the entity.
$entity = field_test_entity_test_load($id);
$entity->content = field_attach_view($entity_type, $entity, 'full');
$this->content = drupal_render($entity->content);
$this->assertRaw($value, t('Value is displayed unfiltered'));
$this->assertRaw($value, 'Value is displayed unfiltered');
}
}
@@ -383,7 +383,7 @@ class TextSummaryTestCase extends DrupalWebTestCase {
*/
function callTextSummary($text, $expected, $format = NULL, $size = NULL) {
$summary = text_summary($text, $format, $size);
$this->assertIdentical($summary, $expected, t('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
$this->assertIdentical($summary, $expected, format_string('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
}
/**
@@ -401,7 +401,7 @@ class TextSummaryTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/article', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->assertIdentical($node->body['und'][0]['summary'], $summary, t('Article with with summary and no body has been submitted.'));
$this->assertIdentical($node->body['und'][0]['summary'], $summary, 'Article with with summary and no body has been submitted.');
}
}
@@ -436,7 +436,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
// Set "Article" content type to use multilingual support with translation.
$edit = array('language_content_type' => 2);
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), t('Article content type has been updated.'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), 'Article content type has been updated.');
}
/**
@@ -464,7 +464,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->drupalGet("node/$node->nid/translate");
$this->clickLink(t('add translation'));
$this->assertFieldByXPath("//textarea[@name='body[$langcode][0][value]']", $body, t('The textfield widget is populated.'));
$this->assertFieldByXPath("//textarea[@name='body[$langcode][0][value]']", $body, 'The textfield widget is populated.');
}
/**
@@ -476,7 +476,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$edit = array('field[cardinality]' => -1);
$this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings'));
$this->drupalGet('node/add/article');
$this->assertFieldByXPath("//input[@name='body_add_more']", t('Add another item'), t('Body field cardinality set to multiple.'));
$this->assertFieldByXPath("//input[@name='body_add_more']", t('Add another item'), 'Body field cardinality set to multiple.');
$body = array(
$this->randomName(),
@@ -501,7 +501,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
"body[$langcode][$delta][format]" => array_shift($formats),
);
$this->drupalPost('node/1/edit', $edit, t('Save'));
$this->assertText($body[$delta], t('The body field with delta @delta has been saved.', array('@delta' => $delta)));
$this->assertText($body[$delta], format_string('The body field with delta @delta has been saved.', array('@delta' => $delta)));
}
// Login as translator.
@@ -511,7 +511,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$node = $this->drupalGetNodeByTitle($title);
$this->drupalGet("node/$node->nid/translate");
$this->clickLink(t('add translation'));
$this->assertNoText($body[0], t('The body field with delta @delta is hidden.', array('@delta' => 0)));
$this->assertText($body[1], t('The body field with delta @delta is shown.', array('@delta' => 1)));
$this->assertNoText($body[0], format_string('The body field with delta @delta is hidden.', array('@delta' => 0)));
$this->assertText($body[1], format_string('The body field with delta @delta is shown.', array('@delta' => 1)));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,12 @@
* 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(

View File

@@ -28,7 +28,9 @@ function field_test_field_info() {
'shape' => array(
'label' => t('Shape'),
'description' => t('Another dummy field type.'),
'settings' => array(),
'settings' => array(
'foreign_key_name' => 'shape',
),
'instance_settings' => array(),
'default_widget' => 'test_field_widget',
'default_formatter' => 'field_test_default',

View File

@@ -5,3 +5,9 @@ package = Testing
files[] = field_test.entity.inc
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2015-04-02
version = "7.36"
project = "drupal"
datestamp = "1427943826"

View File

@@ -60,7 +60,7 @@ function field_test_schema() {
'description' => 'The base table for test entities with a bundle key.',
'fields' => array(
'ftid' => array(
'description' => 'The primary indentifier for a test_entity_bundle_key.',
'description' => 'The primary identifier for a test_entity_bundle_key.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
@@ -79,7 +79,7 @@ function field_test_schema() {
'description' => 'The base table for test entities with a bundle.',
'fields' => array(
'ftid' => array(
'description' => 'The primary indentifier for a test_entity_bundle.',
'description' => 'The primary identifier for a test_entity_bundle.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
@@ -132,6 +132,18 @@ function field_test_field_schema($field) {
);
}
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(
@@ -145,6 +157,6 @@ function field_test_field_schema($field) {
'not null' => FALSE,
),
),
);
) + $foreign_keys;
}
}

View File

@@ -204,10 +204,7 @@ function field_test_dummy_field_storage_query(EntityFieldQuery $query) {
}
/**
* Entity label callback.
*
* @param $entity
* The entity object.
* Implements callback_entity_info_label().
*
* @return
* The label of the entity prefixed with "label callback".
@@ -223,6 +220,10 @@ 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']);
}
}
/**
@@ -270,3 +271,14 @@ function field_test_query_efq_table_prefixing_test_alter(&$query) {
// 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;
}