update to 7.22

Signed-off-by: bachy <git@g-u-i.net>
This commit is contained in:
bachy
2013-05-24 13:03:57 +02:00
parent d5097a4bc6
commit 5658794f17
265 changed files with 5551 additions and 8808 deletions

View File

@@ -873,7 +873,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);
}
/**
@@ -1735,11 +1735,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);

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.
@@ -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.
@@ -1093,9 +1120,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 +1201,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

@@ -319,7 +319,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 +341,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)

View File

@@ -5,13 +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 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -0,0 +1,668 @@
<?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;
cache_set('field_info:field_map', $map, 'cache_field');
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.
cache_set('field_info:fields', $this->fieldsById, 'cache_field');
}
// 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.
cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
}
$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']];
}
cache_set("field_info:bundle:$entity_type:$bundle", $cache, 'cache_field');
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;
cache_set("field_info:bundle_extra:$entity_type:$bundle", $info, 'cache_field');
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,
);
}
/**
@@ -161,282 +230,69 @@ function _field_info_collate_types($reset = FALSE) {
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);
}
// 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');
}
}
return $info;
}
/**
* 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 +439,51 @@ 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.
*/
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 +505,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 +524,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 +547,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 +610,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 +671,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

@@ -459,6 +459,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

@@ -873,7 +873,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
@@ -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

@@ -7,8 +7,8 @@ dependencies[] = field
files[] = field_sql_storage.test
required = TRUE
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -324,11 +324,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);

View File

@@ -7,8 +7,8 @@ dependencies[] = field
dependencies[] = options
files[] = tests/list.test
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -5,8 +5,8 @@ package = Testing
version = VERSION
hidden = TRUE
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = number.test
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = options.test
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -7,8 +7,8 @@ dependencies[] = field
files[] = text.test
required = TRUE
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"

View File

@@ -85,12 +85,28 @@ class FieldAttachTestCase extends FieldTestCase {
}
parent::setUp($modules);
$this->field_name = drupal_strtolower($this->randomName() . '_field_name');
$this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
$this->field = field_create_field($this->field);
$this->field_id = $this->field['id'];
$this->instance = array(
'field_name' => $this->field_name,
$this->createFieldWithInstance();
}
/**
* Create a field and an instance of it.
*
* @param string $suffix
* (optional) A string that should only contain characters that are valid in
* PHP variable names as well.
*/
function createFieldWithInstance($suffix = '') {
$field_name = 'field_name' . $suffix;
$field = 'field' . $suffix;
$field_id = 'field_id' . $suffix;
$instance = 'instance' . $suffix;
$this->$field_name = drupal_strtolower($this->randomName() . '_field_name' . $suffix);
$this->$field = array('field_name' => $this->$field_name, 'type' => 'test_field', 'cardinality' => 4);
$this->$field = field_create_field($this->$field);
$this->$field_id = $this->{$field}['id'];
$this->$instance = array(
'field_name' => $this->$field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'label' => $this->randomName() . '_label',
@@ -107,7 +123,7 @@ class FieldAttachTestCase extends FieldTestCase {
)
)
);
field_create_instance($this->instance);
field_create_instance($this->$instance);
}
}
@@ -641,13 +657,18 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
* Test field_attach_view() and field_attach_prepare_view().
*/
function testFieldAttachView() {
$this->createFieldWithInstance('_2');
$entity_type = 'test_entity';
$entity_init = field_test_create_stub_entity();
$langcode = LANGUAGE_NONE;
$options = array('field_name' => $this->field_name_2);
// Populate values to be displayed.
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity_init->{$this->field_name}[$langcode] = $values;
$values_2 = $this->_generateTestFieldValues($this->field_2['cardinality']);
$entity_init->{$this->field_name_2}[$langcode] = $values_2;
// Simple formatter, label displayed.
$entity = clone($entity_init);
@@ -662,15 +683,47 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
$formatter_setting_2 = $this->randomName();
$this->instance_2['display'] = array(
'full' => array(
'label' => 'above',
'type' => 'field_test_default',
'settings' => array(
'test_formatter_setting' => $formatter_setting_2,
)
),
);
field_update_instance($this->instance_2);
// View all fields.
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertRaw($this->instance['label'], "Label is displayed.");
$this->assertRaw($this->instance['label'], "First field's label is displayed.");
foreach ($values as $delta => $value) {
$this->content = $output;
$this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
}
$this->assertRaw($this->instance_2['label'], "Second field's label is displayed.");
foreach ($values_2 as $delta => $value) {
$this->content = $output;
$this->assertRaw("$formatter_setting_2|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
}
// View single field (the second field).
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full', $langcode, $options);
$entity->content = field_attach_view($entity_type, $entity, 'full', $langcode, $options);
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "First field's label is not displayed.");
foreach ($values as $delta => $value) {
$this->content = $output;
$this->assertNoRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
}
$this->assertRaw($this->instance_2['label'], "Second field's label is displayed.");
foreach ($values_2 as $delta => $value) {
$this->content = $output;
$this->assertRaw("$formatter_setting_2|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
}
// Label hidden.
$entity = clone($entity_init);
@@ -697,7 +750,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
foreach ($values as $delta => $value) {
$this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
$this->assertNoRaw("$formatter_setting|{$value['value']}", "Hidden field: value $delta is not displayed.");
}
// Multiple formatter.
@@ -907,11 +960,13 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
* hook_field_validate.
*/
function testFieldAttachValidate() {
$this->createFieldWithInstance('_2');
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
// Set up values to generate errors
// Set up all but one values of the first field to generate errors.
$values = array();
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
$values[$delta]['value'] = -1;
@@ -920,6 +975,14 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$values[1]['value'] = 1;
$entity->{$this->field_name}[$langcode] = $values;
// Set up all values of the second field to generate errors.
$values_2 = array();
for ($delta = 0; $delta < $this->field_2['cardinality']; $delta++) {
$values_2[$delta]['value'] = -1;
}
$entity->{$this->field_name_2}[$langcode] = $values_2;
// Validate all fields.
try {
field_attach_validate($entity_type, $entity);
}
@@ -929,26 +992,57 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
foreach ($values as $delta => $value) {
if ($value['value'] != 1) {
$this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
$this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on value $delta");
$this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on first field's value $delta");
$this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on first field's value $delta");
unset($errors[$this->field_name][$langcode][$delta]);
}
else {
$this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on value $delta");
$this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on first field's value $delta");
}
}
$this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set');
foreach ($values_2 as $delta => $value) {
$this->assertIdentical($errors[$this->field_name_2][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on second field's value $delta");
$this->assertEqual(count($errors[$this->field_name_2][$langcode][$delta]), 1, "Only one error set on second field's value $delta");
unset($errors[$this->field_name_2][$langcode][$delta]);
}
$this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set for first field');
$this->assertEqual(count($errors[$this->field_name_2][$langcode]), 0, 'No extraneous errors set for second field');
// Validate a single field.
$options = array('field_name' => $this->field_name_2);
try {
field_attach_validate($entity_type, $entity, $options);
}
catch (FieldValidationException $e) {
$errors = $e->errors;
}
foreach ($values_2 as $delta => $value) {
$this->assertIdentical($errors[$this->field_name_2][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on second field's value $delta");
$this->assertEqual(count($errors[$this->field_name_2][$langcode][$delta]), 1, "Only one error set on second field's value $delta");
unset($errors[$this->field_name_2][$langcode][$delta]);
}
$this->assertFalse(isset($errors[$this->field_name]), 'No validation errors are set for the first field, despite it having errors');
$this->assertEqual(count($errors[$this->field_name_2][$langcode]), 0, 'No extraneous errors set for second field');
// Check that cardinality is validated.
$entity->{$this->field_name}[$langcode] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
$entity->{$this->field_name_2}[$langcode] = $this->_generateTestFieldValues($this->field_2['cardinality'] + 1);
// When validating all fields.
try {
field_attach_validate($entity_type, $entity);
}
catch (FieldValidationException $e) {
$errors = $e->errors;
}
$this->assertEqual($errors[$this->field_name][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
$this->assertEqual($errors[$this->field_name_2][$langcode][0][0]['error'], 'field_cardinality', 'Cardinality validation failed.');
// When validating a single field (the second field).
try {
field_attach_validate($entity_type, $entity, $options);
}
catch (FieldValidationException $e) {
$errors = $e->errors;
}
$this->assertEqual($errors[$this->field_name_2][$langcode][0][0]['error'], 'field_cardinality', 'Cardinality validation failed.');
}
/**
@@ -958,34 +1052,59 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
* widgets show up.
*/
function testFieldAttachForm() {
$this->createFieldWithInstance('_2');
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
// When generating form for all fields.
$form = array();
$form_state = form_state_defaults();
field_attach_form($entity_type, $entity, $form, $form_state);
$langcode = LANGUAGE_NONE;
$this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
$this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "First field's form title is {$this->instance['label']}");
$this->assertEqual($form[$this->field_name_2][$langcode]['#title'], $this->instance_2['label'], "Second field's form title is {$this->instance_2['label']}");
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// field_test_widget uses 'textfield'
$this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
}
$this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "First field's form delta $delta widget is textfield");
}
for ($delta = 0; $delta < $this->field_2['cardinality']; $delta++) {
// field_test_widget uses 'textfield'
$this->assertEqual($form[$this->field_name_2][$langcode][$delta]['value']['#type'], 'textfield', "Second field's form delta $delta widget is textfield");
}
// When generating form for a single field (the second field).
$options = array('field_name' => $this->field_name_2);
$form = array();
$form_state = form_state_defaults();
field_attach_form($entity_type, $entity, $form, $form_state, NULL, $options);
$this->assertFalse(isset($form[$this->field_name]), 'The first field does not exist in the form');
$this->assertEqual($form[$this->field_name_2][$langcode]['#title'], $this->instance_2['label'], "Second field's form title is {$this->instance_2['label']}");
for ($delta = 0; $delta < $this->field_2['cardinality']; $delta++) {
// field_test_widget uses 'textfield'
$this->assertEqual($form[$this->field_name_2][$langcode][$delta]['value']['#type'], 'textfield', "Second field's form delta $delta widget is textfield");
}
}
/**
* Test field_attach_submit().
*/
function testFieldAttachSubmit() {
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$this->createFieldWithInstance('_2');
// Build the form.
$entity_type = 'test_entity';
$entity_init = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
// Build the form for all fields.
$form = array();
$form_state = form_state_defaults();
field_attach_form($entity_type, $entity, $form, $form_state);
field_attach_form($entity_type, $entity_init, $form, $form_state);
// Simulate incoming values.
// First field.
$values = array();
$weights = array();
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -999,22 +1118,59 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
// Leave an empty value. 'field_test' fields are empty if empty().
$values[1]['value'] = 0;
$langcode = LANGUAGE_NONE;
// Second field.
$values_2 = array();
$weights_2 = array();
for ($delta = 0; $delta < $this->field_2['cardinality']; $delta++) {
$values_2[$delta]['value'] = mt_rand(1, 127);
// Assign random weight.
do {
$weight = mt_rand(0, $this->field_2['cardinality']);
} while (in_array($weight, $weights_2));
$weights_2[$delta] = $weight;
$values_2[$delta]['_weight'] = $weight;
}
// Leave an empty value. 'field_test' fields are empty if empty().
$values_2[1]['value'] = 0;
// Pretend the form has been built.
drupal_prepare_form('field_test_entity_form', $form, $form_state);
drupal_process_form('field_test_entity_form', $form, $form_state);
$form_state['values'][$this->field_name][$langcode] = $values;
$form_state['values'][$this->field_name_2][$langcode] = $values_2;
// Call field_attach_submit() for all fields.
$entity = clone($entity_init);
field_attach_submit($entity_type, $entity, $form, $form_state);
asort($weights);
asort($weights_2);
$expected_values = array();
$expected_values_2 = array();
foreach ($weights as $key => $value) {
if ($key != 1) {
$expected_values[] = array('value' => $values[$key]['value']);
}
}
$this->assertIdentical($entity->{$this->field_name}[$langcode], $expected_values, 'Submit filters empty values');
foreach ($weights_2 as $key => $value) {
if ($key != 1) {
$expected_values_2[] = array('value' => $values_2[$key]['value']);
}
}
$this->assertIdentical($entity->{$this->field_name_2}[$langcode], $expected_values_2, 'Submit filters empty values');
// Call field_attach_submit() for a single field (the second field).
$options = array('field_name' => $this->field_name_2);
$entity = clone($entity_init);
field_attach_submit($entity_type, $entity, $form, $form_state, $options);
$expected_values_2 = array();
foreach ($weights_2 as $key => $value) {
if ($key != 1) {
$expected_values_2[] = array('value' => $values_2[$key]['value']);
}
}
$this->assertFalse(isset($entity->{$this->field_name}), 'The first field does not exist in the entity object');
$this->assertIdentical($entity->{$this->field_name_2}[$langcode], $expected_values_2, 'Submit filters empty values');
}
}
@@ -1144,6 +1300,16 @@ class FieldInfoTestCase extends FieldTestCase {
$this->assertIdentical($instances, $expected, "field_info_instances('user') returns " . var_export($expected, TRUE) . '.');
$instances = field_info_instances('user', 'user');
$this->assertIdentical($instances, array(), "field_info_instances('user', 'user') returns an empty array.");
// Test that querying for invalid entity types does not add entries in the
// list returned by field_info_instances().
field_info_cache_clear();
field_info_instances('invalid_entity', 'invalid_bundle');
// Simulate new request by clearing static caches.
drupal_static_reset();
field_info_instances('invalid_entity', 'invalid_bundle');
$instances = field_info_instances();
$this->assertFalse(isset($instances['invalid_entity']), 'field_info_instances() does not contain entries for the invalid entity type that was queried before');
}
/**
@@ -1253,6 +1419,80 @@ class FieldInfoTestCase extends FieldTestCase {
$this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), t('No instances are returned on disabled entity types.'));
}
/**
* Test field_info_field_map().
*/
function testFieldMap() {
// We will overlook fields created by the 'standard' install profile.
$exclude = field_info_field_map();
// Create a new bundle for 'test_entity' entity type.
field_test_create_bundle('test_bundle_2');
// Create a couple fields.
$fields = array(
array(
'field_name' => 'field_1',
'type' => 'test_field',
),
array(
'field_name' => 'field_2',
'type' => 'hidden_test_field',
),
);
foreach ($fields as $field) {
field_create_field($field);
}
// Create a couple instances.
$instances = array(
array(
'field_name' => 'field_1',
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
),
array(
'field_name' => 'field_1',
'entity_type' => 'test_entity',
'bundle' => 'test_bundle_2',
),
array(
'field_name' => 'field_2',
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
),
array(
'field_name' => 'field_2',
'entity_type' => 'test_cacheable_entity',
'bundle' => 'test_bundle',
),
);
foreach ($instances as $instance) {
field_create_instance($instance);
}
$expected = array(
'field_1' => array(
'type' => 'test_field',
'bundles' => array(
'test_entity' => array('test_bundle', 'test_bundle_2'),
),
),
'field_2' => array(
'type' => 'hidden_test_field',
'bundles' => array(
'test_entity' => array('test_bundle'),
'test_cacheable_entity' => array('test_bundle'),
),
),
);
// Check that the field map is correct.
$map = field_info_field_map();
$map = array_diff_key($map, $exclude);
$this->assertEqual($map, $expected);
}
/**
* Test that the field_info settings convenience functions work.
*/
@@ -1277,6 +1517,31 @@ class FieldInfoTestCase extends FieldTestCase {
$this->assertIdentical(field_info_formatter_settings($type), $data['settings'], "field_info_formatter_settings returns {$type}'s formatter settings");
}
}
/**
* Tests that the field info cache can be built correctly.
*/
function testFieldInfoCache() {
// Create a test field and ensure it's in the array returned by
// field_info_fields().
$field_name = drupal_strtolower($this->randomName());
$field = array(
'field_name' => $field_name,
'type' => 'test_field',
);
field_create_field($field);
$fields = field_info_fields();
$this->assertTrue(isset($fields[$field_name]), 'The test field is initially found in the array returned by field_info_fields().');
// Now rebuild the field info cache, and set a variable which will cause
// the cache to be cleared while it's being rebuilt; see
// field_test_entity_info(). Ensure the test field is still in the returned
// array.
field_info_cache_clear();
variable_set('field_test_clear_info_cache_in_hook_entity_info', TRUE);
$fields = field_info_fields();
$this->assertTrue(isset($fields[$field_name]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
}
}
class FieldFormTestCase extends FieldTestCase {
@@ -2178,6 +2443,41 @@ class FieldCrudTestCase extends FieldTestCase {
$this->assertTrue($field_definition < $field, t('The field was properly read.'));
}
/**
* Tests reading field definitions.
*/
function testReadFields() {
$field_definition = array(
'field_name' => 'field_1',
'type' => 'test_field',
);
field_create_field($field_definition);
// Check that 'single column' criteria works.
$fields = field_read_fields(array('field_name' => $field_definition['field_name']));
$this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
// Check that 'multi column' criteria works.
$fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => $field_definition['type']));
$this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
$fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => 'foo'));
$this->assertTrue(empty($fields), 'No field was found.');
// Create an instance of the field.
$instance_definition = array(
'field_name' => $field_definition['field_name'],
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
);
field_create_instance($instance_definition);
// Check that criteria spanning over the field_config_instance table work.
$fields = field_read_fields(array('entity_type' => $instance_definition['entity_type'], 'bundle' => $instance_definition['bundle']));
$this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
$fields = field_read_fields(array('entity_type' => $instance_definition['entity_type'], 'field_name' => $instance_definition['field_name']));
$this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
}
/**
* Test creation of indexes on data column.
*/

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

@@ -6,8 +6,8 @@ files[] = field_test.entity.inc
version = VERSION
hidden = TRUE
; Information added by drupal.org packaging script on 2013-03-07
version = "7.21"
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
project = "drupal"
datestamp = "1362616996"
datestamp = "1365027012"