summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/field
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/field')
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.api.php2698
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.attach.inc1416
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.crud.inc1010
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.default.inc268
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.form.inc604
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.info18
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.info.class.inc668
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.info.inc812
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.install471
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.module1217
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/field.multilingual.inc301
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install215
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module758
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test441
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.install138
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.module486
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test457
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module32
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.install45
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.module419
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.test155
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php74
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.module417
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.test553
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.install95
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.js49
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.module611
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.test517
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field.test3709
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.entity.inc500
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.field.inc413
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.install162
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.module269
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/tests/field_test.storage.inc473
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/theme/field-rtl.css14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/theme/field.css28
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/theme/field.tpl.php62
44 files changed, 20668 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.api.php b/kolab.org/www/drupal-7.26/modules/field/field.api.php
new file mode 100644
index 0000000..b0fc6a4
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.api.php
@@ -0,0 +1,2698 @@
+<?php
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Exposes "pseudo-field" components on fieldable entities.
+ *
+ * Field UI's "Manage fields" and "Manage display" pages let users re-order
+ * fields, but also non-field components. For nodes, these include the title,
+ * poll choices, and other elements exposed by modules through hook_form() or
+ * hook_form_alter().
+ *
+ * Fieldable entities or modules that want to have their components supported
+ * should expose them using this hook. The user-defined settings (weight,
+ * visible) are automatically applied on rendered forms and displayed
+ * entities in a #pre_render callback added by field_attach_form() and
+ * field_attach_view().
+ *
+ * @see _field_extra_fields_pre_render()
+ * @see hook_field_extra_fields_alter()
+ *
+ * @return
+ * 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 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.
+ */
+function hook_field_extra_fields() {
+ $extra['node']['poll'] = array(
+ 'form' => array(
+ 'choice_wrapper' => array(
+ 'label' => t('Poll choices'),
+ 'description' => t('Poll choices'),
+ 'weight' => -4,
+ ),
+ 'settings' => array(
+ 'label' => t('Poll settings'),
+ 'description' => t('Poll module settings'),
+ 'weight' => -3,
+ ),
+ ),
+ 'display' => array(
+ 'poll_view_voting' => array(
+ 'label' => t('Poll vote'),
+ 'description' => t('Poll vote'),
+ 'weight' => 0,
+ ),
+ 'poll_view_results' => array(
+ 'label' => t('Poll results'),
+ 'description' => t('Poll results'),
+ 'weight' => 0,
+ ),
+ )
+ );
+
+ return $extra;
+}
+
+/**
+ * Alter "pseudo-field" components on fieldable entities.
+ *
+ * @param $info
+ * The associative array of 'pseudo-field' components.
+ *
+ * @see hook_field_extra_fields()
+ */
+function hook_field_extra_fields_alter(&$info) {
+ // Force node title to always be at the top of the list by default.
+ foreach (node_type_get_types() as $bundle) {
+ if (isset($info['node'][$bundle->type]['form']['title'])) {
+ $info['node'][$bundle->type]['form']['title']['weight'] = -20;
+ }
+ }
+}
+
+/**
+ * @defgroup field_types Field Types API
+ * @{
+ * Define field types.
+ *
+ * In the Field API, each field has a type, which determines what kind of data
+ * (integer, string, date, etc.) the field can hold, which settings it provides,
+ * and so on. The data type(s) accepted by a field are defined in
+ * hook_field_schema(); other basic properties of a field are defined in
+ * hook_field_info(). The other hooks below are called by the Field Attach API
+ * to perform field-type-specific actions.
+ *
+ * The Field Types API also defines two kinds of pluggable handlers: widgets
+ * and formatters. @link field_widget Widgets @endlink specify how the field
+ * appears in edit forms, while @link field_formatter formatters @endlink
+ * specify how the field appears in displayed entities.
+ *
+ * A third kind of pluggable handlers, storage backends, is defined by the
+ * @link field_storage Field Storage API @endlink.
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * 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:
+ * - label: The human-readable name of the field type.
+ * - description: A short description for the field type.
+ * - settings: An array whose keys are the names of the settings available
+ * for the field type, and whose values are the default values for those
+ * settings.
+ * - instance_settings: An array whose keys are the names of the settings
+ * available for instances of the field type, and whose values are the
+ * default values for those settings. Instance-level settings can have
+ * different values on each field instance, and thus allow greater
+ * flexibility than field-level settings. It is recommended to put settings
+ * at the instance level whenever possible. Notable exceptions: settings
+ * acting on the schema definition, or settings that Views needs to use
+ * across field instances (for example, the list of allowed values).
+ * - default_widget: The machine name of the default widget to be used by
+ * instances of this field type, when no widget is specified in the
+ * instance definition. This widget must be available whenever the field
+ * type is available (i.e. provided by the field type module, or by a module
+ * the field type module depends on).
+ * - default_formatter: The machine name of the default formatter to be used
+ * by instances of this field type, when no formatter is specified in the
+ * instance definition. This formatter must be available whenever the field
+ * type is available (i.e. provided by the field type module, or by a module
+ * the field type module depends on).
+ * - no_ui: (optional) A boolean specifying that users should not be allowed
+ * to create fields and instances of this field type through the UI. Such
+ * fields can only be created programmatically with field_create_field()
+ * and field_create_instance(). Defaults to FALSE.
+ *
+ * @see hook_field_info_alter()
+ */
+function hook_field_info() {
+ return array(
+ 'text' => array(
+ 'label' => t('Text'),
+ 'description' => t('This field stores varchar text in the database.'),
+ 'settings' => array('max_length' => 255),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textfield',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_long' => array(
+ 'label' => t('Long text'),
+ 'description' => t('This field stores long text in the database.'),
+ 'settings' => array('max_length' => ''),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textarea',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_with_summary' => array(
+ 'label' => t('Long text and summary'),
+ 'description' => t('This field stores long text in the database along with optional summary text.'),
+ 'settings' => array('max_length' => ''),
+ 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
+ 'default_widget' => 'text_textarea_with_summary',
+ 'default_formatter' => 'text_summary_or_trimmed',
+ ),
+ );
+}
+
+/**
+ * Perform alterations on Field API field types.
+ *
+ * @param $info
+ * Array of information on field types exposed by hook_field_info()
+ * implementations.
+ */
+function hook_field_info_alter(&$info) {
+ // Add a setting to all field types.
+ foreach ($info as $field_type => $field_type_info) {
+ $info[$field_type]['settings'] += array(
+ 'mymodule_additional_setting' => 'default value',
+ );
+ }
+
+ // Change the default widget for fields of type 'foo'.
+ if (isset($info['foo'])) {
+ $info['foo']['default widget'] = 'mymodule_widget';
+ }
+}
+
+/**
+ * Define the Field API schema for a field structure.
+ *
+ * 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.
+ *
+ * @return
+ * An associative array with the following keys:
+ * - columns: An array of Schema API column specifications, keyed by column
+ * name. This specifies what comprises a value for a given field. For
+ * example, a value for a number field is simply 'value', while a value for
+ * a formatted text field is the combination of 'value' and 'format'. It is
+ * recommended to avoid having the column definitions depend on field
+ * settings when possible. No assumptions should be made on how storage
+ * engines internally use the original column name to structure their
+ * storage.
+ * - indexes: (optional) An array of Schema API indexes definitions. Only
+ * columns that appear in the 'columns' array are allowed. Those indexes
+ * will be used as default indexes. Callers of field_create_field() can
+ * specify additional indexes, or, at their own risk, modify the default
+ * indexes specified by the field-type module. Some storage engines might
+ * not support indexes.
+ * - foreign keys: (optional) An array of Schema API foreign keys
+ * definitions.
+ */
+function hook_field_schema($field) {
+ if ($field['type'] == 'text_long') {
+ $columns = array(
+ 'value' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ );
+ }
+ else {
+ $columns = array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => $field['settings']['max_length'],
+ 'not null' => FALSE,
+ ),
+ );
+ }
+ $columns += array(
+ 'format' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ );
+ return array(
+ 'columns' => $columns,
+ 'indexes' => array(
+ 'format' => array('format'),
+ ),
+ 'foreign keys' => array(
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ );
+}
+
+/**
+ * Define custom load behavior for this module's field types.
+ *
+ * Unlike most other field hooks, this hook operates on multiple entities. The
+ * $entities, $instances and $items parameters are arrays keyed by entity ID.
+ * For performance reasons, information for all available entity should be
+ * loaded in a single query where possible.
+ *
+ * Note that the changes made to the field values get cached by the field cache
+ * for subsequent loads. You should never use this hook to load fieldable
+ * entities, since this is likely to cause infinite recursions when
+ * hook_field_load() is run on those as well. Use
+ * hook_field_formatter_prepare_view() instead.
+ *
+ * Make changes or additions to field values by altering the $items parameter by
+ * reference. There is no return value.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entities
+ * Array of entities being loaded, keyed by entity ID.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instances
+ * Array of instance structures for $field for each entity, keyed by entity
+ * ID.
+ * @param $langcode
+ * The language code associated with $items.
+ * @param $items
+ * Array of field values already loaded for the entities, keyed by entity ID.
+ * Store your changes in this parameter (passed by reference).
+ * @param $age
+ * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+ * FIELD_LOAD_REVISION to load the version indicated by each entity.
+ */
+function hook_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
+ // Sample code from text.module: precompute sanitized strings so they are
+ // stored in the field cache.
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ // Only process items with a cacheable format, the rest will be handled
+ // by formatters if needed.
+ if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) {
+ $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : '';
+ if ($field['type'] == 'text_with_summary') {
+ $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $item, 'summary') : '';
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Prepare field values prior to display.
+ *
+ * This hook is invoked before the field values are handed to formatters
+ * for display, and runs before the formatters' own
+ * hook_field_formatter_prepare_view().
+ *
+ * Unlike most other field hooks, this hook operates on multiple entities. The
+ * $entities, $instances and $items parameters are arrays keyed by entity ID.
+ * For performance reasons, information for all available entities should be
+ * loaded in a single query where possible.
+ *
+ * Make changes or additions to field values by altering the $items parameter by
+ * reference. There is no return value.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entities
+ * Array of entities being displayed, keyed by entity ID.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instances
+ * Array of instance structures for $field for each entity, keyed by entity
+ * ID.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * $entity->{$field['field_name']}, or an empty array if unset.
+ */
+function hook_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
+ // Sample code from image.module: if there are no images specified at all,
+ // use the default image.
+ foreach ($entities as $id => $entity) {
+ if (empty($items[$id]) && $field['settings']['default_image']) {
+ if ($file = file_load($field['settings']['default_image'])) {
+ $items[$id][0] = (array) $file + array(
+ 'is_default' => TRUE,
+ 'alt' => '',
+ 'title' => '',
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Validate this module's field data.
+ *
+ * If there are validation problems, add to the $errors array (passed by
+ * reference). There is no return value.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ * @param $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_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ foreach ($items as $delta => $item) {
+ if (!empty($item['value'])) {
+ if (!empty($field['settings']['max_length']) && drupal_strlen($item['value']) > $field['settings']['max_length']) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'text_max_length',
+ 'message' => t('%name: the value may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Define custom presave behavior for this module's field types.
+ *
+ * Make changes or additions to field values by altering the $items parameter by
+ * reference. There is no return value.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ */
+function hook_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ if ($field['type'] == 'number_decimal') {
+ // Let PHP round the value to ensure consistent behavior across storage
+ // backends.
+ foreach ($items as $delta => $item) {
+ if (isset($item['value'])) {
+ $items[$delta]['value'] = round($item['value'], $field['settings']['scale']);
+ }
+ }
+ }
+}
+
+/**
+ * Define custom insert behavior for this module's field data.
+ *
+ * This hook is invoked from field_attach_insert() on the module that defines a
+ * field, during the process of inserting an entity object (node, taxonomy term,
+ * etc.). It is invoked just before the data for this field on the particular
+ * entity object is inserted into field storage. Only field modules that are
+ * storing or tracking information outside the standard field storage mechanism
+ * need to implement this hook.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ *
+ * @see hook_field_update()
+ * @see hook_field_delete()
+ */
+function hook_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node' && $entity->status) {
+ $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created', ));
+ foreach ($items as $item) {
+ $query->values(array(
+ 'nid' => $entity->nid,
+ 'tid' => $item['tid'],
+ 'sticky' => $entity->sticky,
+ 'created' => $entity->created,
+ ));
+ }
+ $query->execute();
+ }
+}
+
+/**
+ * Define custom update behavior for this module's field data.
+ *
+ * This hook is invoked from field_attach_update() on the module that defines a
+ * field, during the process of updating an entity object (node, taxonomy term,
+ * etc.). It is invoked just before the data for this field on the particular
+ * entity object is updated into field storage. Only field modules that are
+ * storing or tracking information outside the standard field storage mechanism
+ * need to implement this hook.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ *
+ * @see hook_field_insert()
+ * @see hook_field_delete()
+ */
+function hook_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node') {
+ $first_call = &drupal_static(__FUNCTION__, array());
+
+ // We don't maintain data for old revisions, so clear all previous values
+ // from the table. Since this hook runs once per field, per object, make
+ // sure we only wipe values once.
+ if (!isset($first_call[$entity->nid])) {
+ $first_call[$entity->nid] = FALSE;
+ db_delete('taxonomy_index')->condition('nid', $entity->nid)->execute();
+ }
+ // Only save data to the table if the node is published.
+ if ($entity->status) {
+ $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created'));
+ foreach ($items as $item) {
+ $query->values(array(
+ 'nid' => $entity->nid,
+ 'tid' => $item['tid'],
+ 'sticky' => $entity->sticky,
+ 'created' => $entity->created,
+ ));
+ }
+ $query->execute();
+ }
+ }
+}
+
+/**
+ * Update the storage information for a field.
+ *
+ * This is invoked on the field's storage module from field_update_field(),
+ * before the new field information is saved to the database. The field storage
+ * module should update its storage tables to agree with the new field
+ * information. If there is a problem, the field storage module should throw an
+ * exception.
+ *
+ * @param $field
+ * The updated field structure to be saved.
+ * @param $prior_field
+ * The previously-saved field structure.
+ * @param $has_data
+ * TRUE if the field has data in storage currently.
+ */
+function hook_field_storage_update_field($field, $prior_field, $has_data) {
+ if (!$has_data) {
+ // There is no data. Re-create the tables completely.
+ $prior_schema = _field_sql_storage_schema($prior_field);
+ foreach ($prior_schema as $name => $table) {
+ db_drop_table($name, $table);
+ }
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ }
+ else {
+ // There is data. See field_sql_storage_field_storage_update_field() for
+ // an example of what to do to modify the schema in place, preserving the
+ // old data as much as possible.
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Define custom delete behavior for this module's field data.
+ *
+ * This hook is invoked from field_attach_delete() on the module that defines a
+ * field, during the process of deleting an entity object (node, taxonomy term,
+ * etc.). It is invoked just before the data for this field on the particular
+ * entity object is deleted from field storage. Only field modules that are
+ * storing or tracking information outside the standard field storage mechanism
+ * need to implement this hook.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ *
+ * @see hook_field_insert()
+ * @see hook_field_update()
+ */
+function hook_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ // For hook_file_references(), remember that this is being deleted.
+ $item['file_field_name'] = $field['field_name'];
+ // Pass in the ID of the object that is being removed so all references can
+ // be counted in hook_file_references().
+ $item['file_field_type'] = $entity_type;
+ $item['file_field_id'] = $id;
+ file_field_delete_file($item, $field, $entity_type, $id);
+ }
+}
+
+/**
+ * Define custom revision delete behavior for this module's field types.
+ *
+ * This hook is invoked just before the data is deleted from field storage
+ * in field_attach_delete_revision(), and will only be called for fieldable
+ * types that are versioned.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ */
+function hook_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ // For hook_file_references, remember that this file is being deleted.
+ $item['file_field_name'] = $field['field_name'];
+ if (file_field_delete_file($item, $field, $entity_type, $id)) {
+ $items[$delta] = NULL;
+ }
+ }
+}
+
+/**
+ * Define custom prepare_translation behavior for this module's field types.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ * @param $source_entity
+ * The source entity from which field values are being copied.
+ * @param $source_langcode
+ * The source language from which field values are being copied.
+ */
+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,
+ // we must not expose the source values.
+ $field_name = $field['field_name'];
+ $formats = filter_formats();
+ $format_id = $source_entity->{$field_name}[$source_langcode][0]['format'];
+ if (!filter_access($formats[$format_id])) {
+ $items = array();
+ }
+}
+
+/**
+ * Define what constitutes an empty item for a field type.
+ *
+ * @param $item
+ * An item that may or may not be empty.
+ * @param $field
+ * The field to which $item belongs.
+ *
+ * @return
+ * TRUE if $field's type considers $item not to contain any data;
+ * FALSE otherwise.
+ */
+function hook_field_is_empty($item, $field) {
+ if (empty($item['value']) && (string) $item['value'] !== '0') {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * @} End of "defgroup field_types".
+ */
+
+/**
+ * @defgroup field_widget Field Widget API
+ * @{
+ * Define Field API widget types.
+ *
+ * Field API widgets specify how fields are displayed in edit forms. Fields of a
+ * given @link field_types field type @endlink may be edited using more than one
+ * widget. In this case, the Field UI module allows the site builder to choose
+ * which widget to use. Widget types are defined by implementing
+ * hook_field_widget_info().
+ *
+ * Widgets are @link forms_api_reference.html Form API @endlink elements with
+ * additional processing capabilities. Widget hooks are typically called by the
+ * Field Attach API during the creation of the field form structure with
+ * field_attach_form().
+ *
+ * @see field
+ * @see field_types
+ * @see field_formatter
+ */
+
+/**
+ * Expose Field API widget types.
+ *
+ * @return
+ * An array describing the widget types implemented by the module.
+ * The keys are widget type names. To avoid name clashes, widget type
+ * names should be prefixed with the name of the module that exposes them.
+ * The values are arrays describing the widget type, with the following
+ * key/value pairs:
+ * - label: The human-readable name of the widget type.
+ * - description: A short description for the widget type.
+ * - field types: An array of field types the widget supports.
+ * - settings: An array whose keys are the names of the settings available
+ * for the widget type, and whose values are the default values for those
+ * settings.
+ * - behaviors: (optional) An array describing behaviors of the widget, with
+ * the following elements:
+ * - multiple values: One of the following constants:
+ * - FIELD_BEHAVIOR_DEFAULT: (default) If the widget allows the input of
+ * one single field value (most common case). The widget will be
+ * repeated for each value input.
+ * - FIELD_BEHAVIOR_CUSTOM: If one single copy of the widget can receive
+ * several field values. Examples: checkboxes, multiple select,
+ * comma-separated textfield.
+ * - default value: One of the following constants:
+ * - FIELD_BEHAVIOR_DEFAULT: (default) If the widget accepts default
+ * values.
+ * - FIELD_BEHAVIOR_NONE: if the widget does not support default values.
+ * - weight: (optional) An integer to determine the weight of this widget
+ * relative to other widgets in the Field UI when selecting a widget for a
+ * given field instance.
+ *
+ * @see hook_field_widget_info_alter()
+ * @see hook_field_widget_form()
+ * @see hook_field_widget_form_alter()
+ * @see hook_field_widget_WIDGET_TYPE_form_alter()
+ * @see hook_field_widget_error()
+ * @see hook_field_widget_settings_form()
+ */
+function hook_field_widget_info() {
+ return array(
+ 'text_textfield' => array(
+ 'label' => t('Text field'),
+ 'field types' => array('text'),
+ 'settings' => array('size' => 60),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+ 'default value' => FIELD_BEHAVIOR_DEFAULT,
+ ),
+ ),
+ 'text_textarea' => array(
+ 'label' => t('Text area (multiple rows)'),
+ 'field types' => array('text_long'),
+ 'settings' => array('rows' => 5),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+ 'default value' => FIELD_BEHAVIOR_DEFAULT,
+ ),
+ ),
+ 'text_textarea_with_summary' => array(
+ 'label' => t('Text area with a summary'),
+ 'field types' => array('text_with_summary'),
+ 'settings' => array('rows' => 20, 'summary_rows' => 5),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+ 'default value' => FIELD_BEHAVIOR_DEFAULT,
+ ),
+ // As an advanced widget, force it to sink to the bottom of the choices.
+ 'weight' => 2,
+ ),
+ );
+}
+
+/**
+ * Perform alterations on Field API widget types.
+ *
+ * @param $info
+ * Array of informations on widget types exposed by hook_field_widget_info()
+ * implementations.
+ */
+function hook_field_widget_info_alter(&$info) {
+ // Add a setting to a widget type.
+ $info['text_textfield']['settings'] += array(
+ 'mymodule_additional_setting' => 'default value',
+ );
+
+ // Let a new field type re-use an existing widget.
+ $info['options_select']['field types'][] = 'my_field_type';
+}
+
+/**
+ * Return the form for a single field widget.
+ *
+ * Field widget form elements should be based on the passed-in $element, which
+ * contains the base form element properties derived from the field
+ * configuration.
+ *
+ * Field API will set the weight, field name and delta values for each form
+ * element. If there are multiple values for this field, the Field API will
+ * invoke this hook as many times as needed.
+ *
+ * Note that, depending on the context in which the widget is being included
+ * (regular entity form, field configuration form, advanced search form...),
+ * the values for $field and $instance might be different from the "official"
+ * definitions returned by field_info_field() and field_info_instance().
+ * Examples: mono-value widget even if the field is multi-valued, non-required
+ * widget even if the field is 'required'...
+ *
+ * Therefore, the FAPI element callbacks (such as #process, #element_validate,
+ * #value_callback...) used by the widget cannot use the field_info_field()
+ * or field_info_instance() functions to retrieve the $field or $instance
+ * definitions they should operate on. The field_widget_field() and
+ * field_widget_instance() functions should be used instead to fetch the
+ * current working definitions from $form_state, where Field API stores them.
+ *
+ * Alternatively, hook_field_widget_form() can extract the needed specific
+ * properties from $field and $instance and set them as ad-hoc
+ * $element['#custom'] properties, for later use by its element callbacks.
+ *
+ * Other modules may alter the form element provided by this function using
+ * hook_field_widget_form_alter().
+ *
+ * @param $form
+ * The form structure where widgets are being attached to. This might be a
+ * 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 $field
+ * The field structure.
+ * @param $instance
+ * The field instance.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * Array of default values for this field.
+ * @param $delta
+ * The order of this item in the array of subelements (0, 1, 2, etc).
+ * @param $element
+ * A form element array containing basic properties for the widget:
+ * - #entity_type: The name of the entity the field is attached to.
+ * - #bundle: The name of the field bundle the field is contained in.
+ * - #field_name: The name of the field.
+ * - #language: The language the field is being edited in.
+ * - #field_parents: The 'parents' space for the field in the form. Most
+ * widgets can simply overlook this property. This identifies the
+ * location where the field values are placed within
+ * $form_state['values'], and is used to access processing information
+ * for the field through the field_form_get_state() and
+ * field_form_set_state() functions.
+ * - #columns: A list of field storage columns of the field.
+ * - #title: The sanitized element label for the field instance, ready for
+ * output.
+ * - #description: The sanitized element description for the field instance,
+ * ready for output.
+ * - #required: A Boolean indicating whether the element value is required;
+ * for required multiple value fields, only the first widget's values are
+ * required.
+ * - #delta: The order of this item in the array of subelements; see $delta
+ * above.
+ *
+ * @return
+ * The form elements for a single widget for this field.
+ *
+ * @see field_widget_field()
+ * @see field_widget_instance()
+ * @see hook_field_widget_form_alter()
+ * @see hook_field_widget_WIDGET_TYPE_form_alter()
+ */
+function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ $element += array(
+ '#type' => $instance['widget']['type'],
+ '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
+ );
+ return array('value' => $element);
+}
+
+/**
+ * Alter forms for field widgets provided by other modules.
+ *
+ * @param $element
+ * The field widget form element as constructed by hook_field_widget_form().
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ * @param $context
+ * An associative array containing the following key-value pairs, matching the
+ * arguments received by hook_field_widget_form():
+ * - form: The form structure to which widgets are being attached. This may be
+ * a full form structure, or a sub-element of a larger form.
+ * - field: The field structure.
+ * - instance: The field instance structure.
+ * - langcode: The language associated with $items.
+ * - items: Array of default values for this field.
+ * - delta: The order of this item in the array of subelements (0, 1, 2, etc).
+ *
+ * @see hook_field_widget_form()
+ * @see hook_field_widget_WIDGET_TYPE_form_alter()
+ */
+function hook_field_widget_form_alter(&$element, &$form_state, $context) {
+ // Add a css class to widget form elements for all fields of type mytype.
+ if ($context['field']['type'] == 'mytype') {
+ // Be sure not to overwrite existing attributes.
+ $element['#attributes']['class'][] = 'myclass';
+ }
+}
+
+/**
+ * Alter widget forms for a specific widget provided by another module.
+ *
+ * Modules can implement hook_field_widget_WIDGET_TYPE_form_alter() to modify a
+ * specific widget form, rather than using hook_field_widget_form_alter() and
+ * checking the widget type.
+ *
+ * @param $element
+ * The field widget form element as constructed by hook_field_widget_form().
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ * @param $context
+ * An associative array containing the following key-value pairs, matching the
+ * arguments received by hook_field_widget_form():
+ * - "form": The form structure where widgets are being attached to. This
+ * might be a full form structure, or a sub-element of a larger form.
+ * - "field": The field structure.
+ * - "instance": The field instance structure.
+ * - "langcode": The language associated with $items.
+ * - "items": Array of default values for this field.
+ * - "delta": The order of this item in the array of subelements (0, 1, 2,
+ * etc).
+ *
+ * @see hook_field_widget_form()
+ * @see hook_field_widget_form_alter()
+ */
+function hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $context) {
+ // Code here will only act on widgets of type WIDGET_TYPE. For example,
+ // hook_field_widget_mymodule_autocomplete_form_alter() will only act on
+ // widgets of type 'mymodule_autocomplete'.
+ $element['#autocomplete_path'] = 'mymodule/autocomplete_path';
+}
+
+/**
+ * Alters the widget properties of a field instance before it gets displayed.
+ *
+ * Note that instead of hook_field_widget_properties_alter(), which is called
+ * for all fields on all entity types,
+ * hook_field_widget_properties_ENTITY_TYPE_alter() may be used to alter widget
+ * properties for fields on a specific entity type only.
+ *
+ * This hook is called once per field per added or edit entity. If the result
+ * of the hook involves reading from the database, it is highly recommended to
+ * statically cache the information.
+ *
+ * @param $widget
+ * The instance's widget properties.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The entity type; e.g., 'node' or 'user'.
+ * - entity: The entity object.
+ * - field: The field that the widget belongs to.
+ * - instance: The instance of the field.
+ *
+ * @see hook_field_widget_properties_ENTITY_TYPE_alter()
+ */
+function hook_field_widget_properties_alter(&$widget, $context) {
+ // Change a widget's type according to the time of day.
+ $field = $context['field'];
+ if ($context['entity_type'] == 'node' && $field['field_name'] == 'field_foo') {
+ $time = date('H');
+ $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
+ }
+}
+
+/**
+ * Flag a field-level validation error.
+ *
+ * @param $element
+ * An array containing the form element for the widget. The error needs to be
+ * flagged on the right sub-element, according to the widget's internal
+ * structure.
+ * @param $error
+ * An associative array with the following key-value pairs, as returned by
+ * hook_field_validate():
+ * - error: the error code. Complex widgets might need to report different
+ * errors to different form elements inside the widget.
+ * - message: the human readable message to be displayed.
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * full form structure, or a sub-element of a larger form.
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ */
+function hook_field_widget_error($element, $error, $form, &$form_state) {
+ form_error($element, $error['message']);
+}
+
+
+/**
+ * @} End of "defgroup field_widget".
+ */
+
+
+/**
+ * @defgroup field_formatter Field Formatter API
+ * @{
+ * Define Field API formatter types.
+ *
+ * Field API formatters specify how fields are displayed when the entity to
+ * which the field is attached is displayed. Fields of a given
+ * @link field_types field type @endlink may be displayed using more than one
+ * formatter. In this case, the Field UI module allows the site builder to
+ * choose which formatter to use. Field formatters are defined by implementing
+ * hook_field_formatter_info().
+ *
+ * @see field
+ * @see field_types
+ * @see field_widget
+ */
+
+/**
+ * Expose Field API formatter types.
+ *
+ * Formatters handle the display of field values. Formatter hooks are typically
+ * called by the Field Attach API field_attach_prepare_view() and
+ * field_attach_view() functions.
+ *
+ * @return
+ * An array describing the formatter types implemented by the module.
+ * The keys are formatter type names. To avoid name clashes, formatter type
+ * names should be prefixed with the name of the module that exposes them.
+ * The values are arrays describing the formatter type, with the following
+ * key/value pairs:
+ * - label: The human-readable name of the formatter type.
+ * - description: A short description for the formatter type.
+ * - field types: An array of field types the formatter supports.
+ * - settings: An array whose keys are the names of the settings available
+ * for the formatter type, and whose values are the default values for
+ * those settings.
+ *
+ * @see hook_field_formatter_info_alter()
+ * @see hook_field_formatter_view()
+ * @see hook_field_formatter_prepare_view()
+ */
+function hook_field_formatter_info() {
+ return array(
+ 'text_default' => array(
+ 'label' => t('Default'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+ 'text_plain' => array(
+ 'label' => t('Plain text'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+
+ // The text_trimmed formatter displays the trimmed version of the
+ // full element of the field. It is intended to be used with text
+ // and text_long fields. It also works with text_with_summary
+ // fields though the text_summary_or_trimmed formatter makes more
+ // sense for that field type.
+ 'text_trimmed' => array(
+ 'label' => t('Trimmed'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+
+ // The 'summary or trimmed' field formatter for text_with_summary
+ // fields displays returns the summary element of the field or, if
+ // the summary is empty, the trimmed version of the full element
+ // of the field.
+ 'text_summary_or_trimmed' => array(
+ 'label' => t('Summary or trimmed'),
+ 'field types' => array('text_with_summary'),
+ ),
+ );
+}
+
+/**
+ * Perform alterations on Field API formatter types.
+ *
+ * @param $info
+ * An array of information on formatter types exposed by
+ * hook_field_formatter_info() implementations.
+ */
+function hook_field_formatter_info_alter(&$info) {
+ // Add a setting to a formatter type.
+ $info['text_default']['settings'] += array(
+ 'mymodule_additional_setting' => 'default value',
+ );
+
+ // Let a new field type re-use an existing formatter.
+ $info['text_default']['field types'][] = 'my_field_type';
+}
+
+/**
+ * Allow formatters to load information for field values being displayed.
+ *
+ * This should be used when a formatter needs to load additional information
+ * from the database in order to render a field, for example a reference field
+ * which displays properties of the referenced entities such as name or type.
+ *
+ * This hook is called after the field type's own hook_field_prepare_view().
+ *
+ * Unlike most other field hooks, this hook operates on multiple entities. The
+ * $entities, $instances and $items parameters are arrays keyed by entity ID.
+ * For performance reasons, information for all available entities should be
+ * loaded in a single query where possible.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entities
+ * Array of entities being displayed, keyed by entity ID.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instances
+ * Array of instance structures for $field for each entity, keyed by entity
+ * ID.
+ * @param $langcode
+ * The language the field values are to be shown in. If no language is
+ * provided the current language is used.
+ * @param $items
+ * Array of field values for the entities, keyed by entity ID.
+ * @param $displays
+ * Array of display settings to use for each entity, keyed by entity ID.
+ *
+ * @return
+ * Changes or additions to field values are done by altering the $items
+ * parameter by reference.
+ */
+function hook_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
+ $tids = array();
+
+ // Collect every possible term attached to any of the fieldable entities.
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ // Force the array key to prevent duplicates.
+ $tids[$item['tid']] = $item['tid'];
+ }
+ }
+
+ if ($tids) {
+ $terms = taxonomy_term_load_multiple($tids);
+
+ // Iterate through the fieldable entities again to attach the loaded term
+ // data.
+ foreach ($entities as $id => $entity) {
+ $rekey = FALSE;
+
+ foreach ($items[$id] as $delta => $item) {
+ // Check whether the taxonomy term field instance value could be loaded.
+ if (isset($terms[$item['tid']])) {
+ // Replace the instance value with the term data.
+ $items[$id][$delta]['taxonomy_term'] = $terms[$item['tid']];
+ }
+ // Otherwise, unset the instance value, since the term does not exist.
+ else {
+ unset($items[$id][$delta]);
+ $rekey = TRUE;
+ }
+ }
+
+ if ($rekey) {
+ // Rekey the items array.
+ $items[$id] = array_values($items[$id]);
+ }
+ }
+ }
+}
+
+/**
+ * Build a renderable array for a field value.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity being displayed.
+ * @param $field
+ * The field structure.
+ * @param $instance
+ * The field instance.
+ * @param $langcode
+ * The language associated with $items.
+ * @param $items
+ * Array of values for this field.
+ * @param $display
+ * The display settings to use, as found in the 'display' entry of instance
+ * definitions. The array notably contains the following keys and values;
+ * - type: The name of the formatter to use.
+ * - settings: The array of formatter settings.
+ *
+ * @return
+ * A renderable array for the $items, as an array of child elements keyed
+ * by numeric indexes starting from 0.
+ */
+function hook_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+ $settings = $display['settings'];
+
+ switch ($display['type']) {
+ case 'sample_field_formatter_simple':
+ // Common case: each value is displayed individually in a sub-element
+ // keyed by delta. The field.tpl.php template specifies the markup
+ // wrapping each value.
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => $settings['some_setting'] . $item['value']);
+ }
+ break;
+
+ case 'sample_field_formatter_themeable':
+ // More elaborate formatters can defer to a theme function for easier
+ // customization.
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array(
+ '#theme' => 'mymodule_theme_sample_field_formatter_themeable',
+ '#data' => $item['value'],
+ '#some_setting' => $settings['some_setting'],
+ );
+ }
+ break;
+
+ case 'sample_field_formatter_combined':
+ // Some formatters might need to display all values within a single piece
+ // of markup.
+ $rows = array();
+ foreach ($items as $delta => $item) {
+ $rows[] = array($delta, $item['value']);
+ }
+ $element[0] = array(
+ '#theme' => 'table',
+ '#header' => array(t('Delta'), t('Value')),
+ '#rows' => $rows,
+ );
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * @} End of "defgroup field_formatter".
+ */
+
+/**
+ * @ingroup field_attach
+ * @{
+ */
+
+/**
+ * Act on field_attach_form().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ * Implementing modules should alter the $form or $form_state parameters.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The entity for which an edit form is being built.
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * full form structure, or a sub-element of a larger form. The
+ * $form['#parents'] property can be used to identify the corresponding part
+ * of $form_state['values']. Hook implementations that need to act on the
+ * top-level properties of the global form (like #submit, #validate...) can
+ * add a #process callback to the array received in the $form parameter, and
+ * act on the $complete_form parameter in the process callback.
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ * @param $langcode
+ * The language the field values are going to be entered in. If no language
+ * is provided the default site language will be used.
+ */
+function hook_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+ // Add a checkbox allowing a given field to be emptied.
+ // See hook_field_attach_submit() for the corresponding processing code.
+ $form['empty_field_foo'] = array(
+ '#type' => 'checkbox',
+ '#title' => t("Empty the 'field_foo' field"),
+ );
+}
+
+/**
+ * Act on field_attach_load().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
+ * Instead of the usual $entity parameter, it accepts an array of entities,
+ * indexed by entity ID. For performance reasons, information for all available
+ * entities should be loaded in a single query where possible.
+ *
+ * The changes made to the entities' field values get cached by the field cache
+ * for subsequent loads.
+ *
+ * See field_attach_load() for details and arguments.
+ */
+function hook_field_attach_load($entity_type, $entities, $age, $options) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_validate().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_validate() for details and arguments.
+ */
+function hook_field_attach_validate($entity_type, $entity, &$errors) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_submit().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The entity for which an edit form is being submitted. The incoming form
+ * values have been extracted as field values of the $entity object.
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * full form structure, or a sub-part of a larger form. The $form['#parents']
+ * property can be used to identify the corresponding part of
+ * $form_state['values'].
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ */
+function hook_field_attach_submit($entity_type, $entity, $form, &$form_state) {
+ // Sample case of an 'Empty the field' checkbox added on the form, allowing
+ // a given field to be emptied.
+ $values = drupal_array_get_nested_value($form_state['values'], $form['#parents']);
+ if (!empty($values['empty_field_foo'])) {
+ unset($entity->field_foo);
+ }
+}
+
+/**
+ * Act on field_attach_presave().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_presave() for details and arguments.
+ */
+function hook_field_attach_presave($entity_type, $entity) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_insert().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_insert() for details and arguments.
+ */
+function hook_field_attach_insert($entity_type, $entity) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_update().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_update() for details and arguments.
+ */
+function hook_field_attach_update($entity_type, $entity) {
+ // @todo Needs function body.
+}
+
+/**
+ * Alter field_attach_preprocess() variables.
+ *
+ * This hook is invoked while preprocessing the field.tpl.php template file
+ * in field_attach_preprocess().
+ *
+ * @param $variables
+ * The variables array is passed by reference and will be populated with field
+ * values.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The type of $entity; for example, 'node' or 'user'.
+ * - entity: The entity with fields to render.
+ * - element: The structured array containing the values ready for rendering.
+ */
+function hook_field_attach_preprocess_alter(&$variables, $context) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_delete().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_delete() for details and arguments.
+ */
+function hook_field_attach_delete($entity_type, $entity) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_attach_delete_revision().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_delete_revision() for details and arguments.
+ */
+function hook_field_attach_delete_revision($entity_type, $entity) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field_purge_data().
+ *
+ * This hook is invoked in field_purge_data() and allows modules to act on
+ * purging data from a single field pseudo-entity. For example, if a module
+ * relates data in the field with its own data, it may purge its own data
+ * during this process as well.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The pseudo-entity whose field data is being purged.
+ * @param $field
+ * The (possibly deleted) field whose data is being purged.
+ * @param $instance
+ * The deleted field instance whose data is being purged.
+ *
+ * @see @link field_purge Field API bulk data deletion @endlink
+ * @see field_purge_data()
+ */
+function hook_field_attach_purge($entity_type, $entity, $field, $instance) {
+ // find the corresponding data in mymodule and purge it
+ if ($entity_type == 'node' && $field->field_name == 'my_field_name') {
+ mymodule_remove_mydata($entity->nid);
+ }
+}
+
+/**
+ * Perform alterations on field_attach_view() or field_view_field().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * @param $output
+ * The structured content array tree for all of the entity's fields.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The type of $entity; for example, 'node' or 'user'.
+ * - entity: The entity with fields to render.
+ * - view_mode: View mode; for example, 'full' or 'teaser'.
+ * - display: Either a view mode string or an array of display settings. If
+ * this hook is being invoked from field_attach_view(), the 'display'
+ * element is set to the view mode string. If this hook is being invoked
+ * from field_view_field(), this element is set to the $display argument
+ * and the view_mode element is set to '_custom'. See field_view_field()
+ * for more information on what its $display argument contains.
+ * - language: The language code used for rendering.
+ */
+function hook_field_attach_view_alter(&$output, $context) {
+ // Append RDF term mappings on displayed taxonomy links.
+ foreach (element_children($output) as $field_name) {
+ $element = &$output[$field_name];
+ if ($element['#field_type'] == 'taxonomy_term_reference' && $element['#formatter'] == 'taxonomy_term_reference_link') {
+ foreach ($element['#items'] as $delta => $item) {
+ $term = $item['taxonomy_term'];
+ if (!empty($term->rdf_mapping['rdftype'])) {
+ $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
+ }
+ if (!empty($term->rdf_mapping['name']['predicates'])) {
+ $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Perform alterations on field_attach_prepare_translation().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * @param $entity
+ * The entity being prepared for translation.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The type of $entity; e.g. 'node' or 'user'.
+ * - langcode: The language the entity has to be translated in.
+ * - source_entity: The entity holding the field values to be translated.
+ * - source_langcode: The source language from which translate.
+ */
+function hook_field_attach_prepare_translation_alter(&$entity, $context) {
+ if ($context['entity_type'] == 'custom_entity_type') {
+ $entity->custom_field = $context['source_entity']->custom_field;
+ }
+}
+
+/**
+ * Perform alterations on field_language() values.
+ *
+ * This hook is invoked to alter the array of display languages for the given
+ * entity.
+ *
+ * @param $display_language
+ * A reference to an array of language codes keyed by field name.
+ * @param $context
+ * An associative array containing:
+ * - 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.
+ */
+function hook_field_language_alter(&$display_language, $context) {
+ // Do not apply core language fallback rules if they are disabled or if Locale
+ // is not registered as a translation handler.
+ if (variable_get('locale_field_language_fallback', TRUE) && field_has_translation_handler($context['entity_type'], 'locale')) {
+ locale_field_language_fallback($display_language, $context['entity'], $context['language']);
+ }
+}
+
+/**
+ * Alter field_available_languages() values.
+ *
+ * This hook is invoked from field_available_languages() to allow modules to
+ * alter the array of available languages for the given field.
+ *
+ * @param $languages
+ * A reference to an array of language codes to be made available.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The type of the entity the field is attached to.
+ * - field: A field data structure.
+ */
+function hook_field_available_languages_alter(&$languages, $context) {
+ // Add an unavailable language.
+ $languages[] = 'xx';
+
+ // Remove an available language.
+ $index = array_search('yy', $languages);
+ unset($languages[$index]);
+}
+
+/**
+ * Act on field_attach_create_bundle().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_create_bundle() for details and arguments.
+ */
+function hook_field_attach_create_bundle($entity_type, $bundle) {
+ // When a new bundle is created, the menu needs to be rebuilt to add the
+ // Field UI menu item tabs.
+ variable_set('menu_rebuild_needed', TRUE);
+}
+
+/**
+ * Act on field_attach_rename_bundle().
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * See field_attach_rename_bundle() for details and arguments.
+ */
+function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+ // Update the extra weights variable with new information.
+ if ($bundle_old !== $bundle_new) {
+ $extra_weights = variable_get('field_extra_weights', array());
+ if (isset($info[$entity_type][$bundle_old])) {
+ $extra_weights[$entity_type][$bundle_new] = $extra_weights[$entity_type][$bundle_old];
+ unset($extra_weights[$entity_type][$bundle_old]);
+ variable_set('field_extra_weights', $extra_weights);
+ }
+ }
+}
+
+/**
+ * Act on field_attach_delete_bundle.
+ *
+ * This hook is invoked after the field module has performed the operation.
+ *
+ * @param $entity_type
+ * The type of entity; for example, 'node' or 'user'.
+ * @param $bundle
+ * The bundle that was just deleted.
+ * @param $instances
+ * An array of all instances that existed for the bundle before it was
+ * deleted.
+ */
+function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) {
+ // Remove the extra weights variable information for this bundle.
+ $extra_weights = variable_get('field_extra_weights', array());
+ if (isset($extra_weights[$entity_type][$bundle])) {
+ unset($extra_weights[$entity_type][$bundle]);
+ variable_set('field_extra_weights', $extra_weights);
+ }
+}
+
+/**
+ * @} End of "defgroup field_attach".
+ */
+
+/**
+ * @addtogroup field_storage
+ * @{
+ */
+
+/**
+ * Expose Field API storage backends.
+ *
+ * @return
+ * An array describing the storage backends implemented by the module.
+ * The keys are storage backend names. To avoid name clashes, storage backend
+ * names should be prefixed with the name of the module that exposes them.
+ * The values are arrays describing the storage backend, with the following
+ * key/value pairs:
+ * - label: The human-readable name of the storage backend.
+ * - description: A short description for the storage backend.
+ * - settings: An array whose keys are the names of the settings available
+ * for the storage backend, and whose values are the default values for
+ * those settings.
+ */
+function hook_field_storage_info() {
+ return array(
+ 'field_sql_storage' => array(
+ 'label' => t('Default SQL storage'),
+ 'description' => t('Stores fields in the local SQL database, using per-field tables.'),
+ 'settings' => array(),
+ ),
+ );
+}
+
+/**
+ * Perform alterations on Field API storage types.
+ *
+ * @param $info
+ * Array of informations on storage types exposed by
+ * hook_field_field_storage_info() implementations.
+ */
+function hook_field_storage_info_alter(&$info) {
+ // Add a setting to a storage type.
+ $info['field_sql_storage']['settings'] += array(
+ 'mymodule_additional_setting' => 'default value',
+ );
+}
+
+/**
+ * Reveal the internal details about the storage for a field.
+ *
+ * For example, an SQL storage module might return the Schema API structure for
+ * the table. A key/value storage module might return the server name,
+ * authentication credentials, and bin name.
+ *
+ * Field storage modules are not obligated to implement this hook. Modules
+ * that rely on these details must only use them for read operations.
+ *
+ * @param $field
+ * A field structure.
+ *
+ * @return
+ * An array of details.
+ * - The first dimension is a store type (sql, solr, etc).
+ * - The second dimension indicates the age of the values in the store
+ * FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
+ * - Other dimensions are specific to the field storage module.
+ *
+ * @see hook_field_storage_details_alter()
+ */
+function hook_field_storage_details($field) {
+ $details = array();
+
+ // Add field columns.
+ foreach ((array) $field['columns'] as $column_name => $attributes) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $columns[$column_name] = $real_name;
+ }
+ return array(
+ 'sql' => array(
+ FIELD_LOAD_CURRENT => array(
+ _field_sql_storage_tablename($field) => $columns,
+ ),
+ FIELD_LOAD_REVISION => array(
+ _field_sql_storage_revision_tablename($field) => $columns,
+ ),
+ ),
+ );
+}
+
+/**
+ * Perform alterations on Field API storage details.
+ *
+ * @param $details
+ * An array of storage details for fields as exposed by
+ * hook_field_storage_details() implementations.
+ * @param $field
+ * A field structure.
+ *
+ * @see hook_field_storage_details()
+ */
+function hook_field_storage_details_alter(&$details, $field) {
+ if ($field['field_name'] == 'field_of_interest') {
+ $columns = array();
+ foreach ((array) $field['columns'] as $column_name => $attributes) {
+ $columns[$column_name] = $column_name;
+ }
+ $details['drupal_variables'] = array(
+ FIELD_LOAD_CURRENT => array(
+ 'moon' => $columns,
+ ),
+ FIELD_LOAD_REVISION => array(
+ 'mars' => $columns,
+ ),
+ );
+ }
+}
+
+/**
+ * Load field data for a set of entities.
+ *
+ * This hook is invoked from field_attach_load() to ask the field storage
+ * module to load field data.
+ *
+ * Modules implementing this hook should load field values and add them to
+ * objects in $entities. Fields with no values should be added as empty
+ * arrays.
+ *
+ * @param $entity_type
+ * The type of entity, such as 'node' or 'user'.
+ * @param $entities
+ * The array of entity objects to add fields to, keyed by entity ID.
+ * @param $age
+ * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+ * FIELD_LOAD_REVISION to load the version indicated by each entity.
+ * @param $fields
+ * An array listing the fields to be loaded. The keys of the array are field
+ * IDs, and the values of the array are the entity IDs (or revision IDs,
+ * depending on the $age parameter) to add each field to.
+ * @param $options
+ * An associative array of additional options, with the following keys:
+ * - deleted: If TRUE, deleted fields should be loaded as well as
+ * non-deleted fields. If unset or FALSE, only non-deleted fields should be
+ * loaded.
+ */
+function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
+ $load_current = $age == FIELD_LOAD_CURRENT;
+
+ foreach ($fields as $field_id => $ids) {
+ // 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);
+
+ $query = db_select($table, 't')
+ ->fields('t')
+ ->condition('entity_type', $entity_type)
+ ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+ ->condition('language', field_available_languages($entity_type, $field), 'IN')
+ ->orderBy('delta');
+
+ if (empty($options['deleted'])) {
+ $query->condition('deleted', 0);
+ }
+
+ $results = $query->execute();
+
+ $delta_count = array();
+ foreach ($results as $row) {
+ if (!isset($delta_count[$row->entity_id][$row->language])) {
+ $delta_count[$row->entity_id][$row->language] = 0;
+ }
+
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) {
+ $item = array();
+ // For each column declared by the field, populate the item
+ // from the prefixed database column.
+ foreach ($field['columns'] as $column => $attributes) {
+ $column_name = _field_sql_storage_columnname($field_name, $column);
+ $item[$column] = $row->$column_name;
+ }
+
+ // Add the item to the field values for the entity.
+ $entities[$row->entity_id]->{$field_name}[$row->language][] = $item;
+ $delta_count[$row->entity_id][$row->language]++;
+ }
+ }
+ }
+}
+
+/**
+ * Write field data for an entity.
+ *
+ * This hook is invoked from field_attach_insert() and field_attach_update(),
+ * to ask the field storage module to save field data.
+ *
+ * @param $entity_type
+ * The entity type of entity, such as 'node' or 'user'.
+ * @param $entity
+ * The entity on which to operate.
+ * @param $op
+ * FIELD_STORAGE_UPDATE when updating an existing entity,
+ * FIELD_STORAGE_INSERT when inserting a new entity.
+ * @param $fields
+ * An array listing the fields to be written. The keys and values of the
+ * array are field IDs.
+ */
+function hook_field_storage_write($entity_type, $entity, $op, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ if (!isset($vid)) {
+ $vid = $id;
+ }
+
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $field_name = $field['field_name'];
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+
+ $all_languages = field_available_languages($entity_type, $field);
+ $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
+
+ // Delete and insert, rather than update, in case a value was added.
+ if ($op == FIELD_STORAGE_UPDATE) {
+ // Delete languages present in the incoming $entity->$field_name.
+ // Delete all languages if $entity->$field_name is empty.
+ $languages = !empty($entity->$field_name) ? $field_languages : $all_languages;
+ if ($languages) {
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ }
+ }
+
+ // Prepare the multi-insert query.
+ $do_insert = FALSE;
+ $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'language');
+ foreach ($field['columns'] as $column => $attributes) {
+ $columns[] = _field_sql_storage_columnname($field_name, $column);
+ }
+ $query = db_insert($table_name)->fields($columns);
+ $revision_query = db_insert($revision_name)->fields($columns);
+
+ foreach ($field_languages as $langcode) {
+ $items = (array) $entity->{$field_name}[$langcode];
+ $delta_count = 0;
+ foreach ($items as $delta => $item) {
+ // We now know we have someting to insert.
+ $do_insert = TRUE;
+ $record = array(
+ 'entity_type' => $entity_type,
+ 'entity_id' => $id,
+ 'revision_id' => $vid,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'language' => $langcode,
+ );
+ foreach ($field['columns'] as $column => $attributes) {
+ $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
+ }
+ $query->values($record);
+ if (isset($vid)) {
+ $revision_query->values($record);
+ }
+
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+ break;
+ }
+ }
+ }
+
+ // Execute the query if we have values to insert.
+ if ($do_insert) {
+ $query->execute();
+ $revision_query->execute();
+ }
+ }
+}
+
+/**
+ * Delete all field data for an entity.
+ *
+ * This hook is invoked from field_attach_delete() to ask the field storage
+ * module to delete field data.
+ *
+ * @param $entity_type
+ * The entity type of entity, such as 'node' or 'user'.
+ * @param $entity
+ * The entity on which to operate.
+ * @param $fields
+ * An array listing the fields to delete. The keys and values of the
+ * array are field IDs.
+ */
+function hook_field_storage_delete($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ if (isset($fields[$instance['field_id']])) {
+ $field = field_info_field_by_id($instance['field_id']);
+ field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance);
+ }
+ }
+}
+
+/**
+ * Delete a single revision of field data for an entity.
+ *
+ * This hook is invoked from field_attach_delete_revision() to ask the field
+ * storage module to delete field revision data.
+ *
+ * Deleting the current (most recently written) revision is not
+ * allowed as has undefined results.
+ *
+ * @param $entity_type
+ * The entity type of entity, such as 'node' or 'user'.
+ * @param $entity
+ * The entity on which to operate. The revision to delete is
+ * indicated by the entity's revision ID property, as identified by
+ * hook_fieldable_info() for $entity_type.
+ * @param $fields
+ * An array listing the fields to delete. The keys and values of the
+ * array are field IDs.
+ */
+function hook_field_storage_delete_revision($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ if (isset($vid)) {
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Execute an EntityFieldQuery.
+ *
+ * This hook is called to find the entities having certain entity and field
+ * conditions and sort them in the given field order. If the field storage
+ * engine also handles property sorts and orders, it should unset those
+ * properties in the called object to signal that those have been handled.
+ *
+ * @param EntityFieldQuery $query
+ * An EntityFieldQuery.
+ *
+ * @return
+ * See EntityFieldQuery::execute() for the return values.
+ */
+function hook_field_storage_query($query) {
+ $groups = array();
+ if ($query->age == FIELD_LOAD_CURRENT) {
+ $tablename_function = '_field_sql_storage_tablename';
+ $id_key = 'entity_id';
+ }
+ else {
+ $tablename_function = '_field_sql_storage_revision_tablename';
+ $id_key = 'revision_id';
+ }
+ $table_aliases = array();
+ // 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_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");
+ }
+ else {
+ $select_query = db_select($tablename, $table_alias);
+ $select_query->addTag('entity_field_access');
+ $select_query->addMetaData('base_table', $tablename);
+ $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
+ $field_base_table = $table_alias;
+ }
+ if ($field['cardinality'] != 1) {
+ $select_query->distinct();
+ }
+ }
+
+ // Add field conditions.
+ foreach ($query->fieldConditions as $key => $condition) {
+ $table_alias = $table_aliases[$key];
+ $field = $condition['field'];
+ // Add the specified condition.
+ $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $condition['column']);
+ $query->addCondition($select_query, $sql_field, $condition);
+ // Add delta / language group conditions.
+ foreach (array('delta', 'language') as $column) {
+ if (isset($condition[$column . '_group'])) {
+ $group_name = $condition[$column . '_group'];
+ if (!isset($groups[$column][$group_name])) {
+ $groups[$column][$group_name] = $table_alias;
+ }
+ else {
+ $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
+ }
+ }
+ }
+ }
+
+ if (isset($query->deleted)) {
+ $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
+ }
+
+ // Is there a need to sort the query by property?
+ $has_property_order = FALSE;
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'property') {
+ $has_property_order = TRUE;
+ }
+ }
+
+ if ($query->propertyConditions || $has_property_order) {
+ if (empty($query->entityConditions['entity_type']['value'])) {
+ throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
+ }
+ $entity_type = $query->entityConditions['entity_type']['value'];
+ $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
+ $query->entityConditions['entity_type']['operator'] = '=';
+ foreach ($query->propertyConditions as $property_condition) {
+ $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
+ }
+ }
+ foreach ($query->entityConditions as $key => $condition) {
+ $query->addCondition($select_query, "$field_base_table.$key", $condition);
+ }
+
+ // Order the query.
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'entity') {
+ $key = $order['specifier'];
+ $select_query->orderBy("$field_base_table.$key", $order['direction']);
+ }
+ elseif ($order['type'] == 'field') {
+ $specifier = $order['specifier'];
+ $field = $specifier['field'];
+ $table_alias = $table_aliases[$specifier['index']];
+ $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
+ $select_query->orderBy($sql_field, $order['direction']);
+ }
+ elseif ($order['type'] == 'property') {
+ $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
+ }
+ }
+
+ return $query->finishQuery($select_query, $id_key);
+}
+
+/**
+ * Act on creation of a new field.
+ *
+ * This hook is invoked from field_create_field() to ask the field storage
+ * module to save field information and prepare for storing field instances.
+ * If there is a problem, the field storage module should throw an exception.
+ *
+ * @param $field
+ * The field structure being created.
+ */
+function hook_field_storage_create_field($field) {
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Act on deletion of a field.
+ *
+ * This hook is invoked from field_delete_field() to ask the field storage
+ * module to mark all information stored in the field for deletion.
+ *
+ * @param $field
+ * The field being deleted.
+ */
+function hook_field_storage_delete_field($field) {
+ // Mark all data associated with the field for deletion.
+ $field['deleted'] = 0;
+ $table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+ db_update($table)
+ ->fields(array('deleted' => 1))
+ ->execute();
+
+ // Move the table to a unique name while the table contents are being deleted.
+ $field['deleted'] = 1;
+ $new_table = _field_sql_storage_tablename($field);
+ $revision_new_table = _field_sql_storage_revision_tablename($field);
+ db_rename_table($table, $new_table);
+ db_rename_table($revision_table, $revision_new_table);
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Act on deletion of a field instance.
+ *
+ * This hook is invoked from field_delete_instance() to ask the field storage
+ * module to mark all information stored for the field instance for deletion.
+ *
+ * @param $instance
+ * The instance being deleted.
+ */
+function hook_field_storage_delete_instance($instance) {
+ $field = field_info_field($instance['field_name']);
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_update($table_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+ db_update($revision_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+}
+
+/**
+ * Act before the storage backends load field data.
+ *
+ * This hook allows modules to load data before the Field Storage API,
+ * optionally preventing the field storage module from doing so.
+ *
+ * This lets 3rd party modules override, mirror, shard, or otherwise store a
+ * subset of fields in a different way than the current storage engine.
+ * Possible use cases include per-bundle storage, per-combo-field storage, etc.
+ *
+ * Modules implementing this hook should load field values and add them to
+ * objects in $entities. Fields with no values should be added as empty
+ * arrays. In addition, fields loaded should be added as keys to $skip_fields.
+ *
+ * @param $entity_type
+ * The type of entity, such as 'node' or 'user'.
+ * @param $entities
+ * The array of entity objects to add fields to, keyed by entity ID.
+ * @param $age
+ * FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+ * FIELD_LOAD_REVISION to load the version indicated by each entity.
+ * @param $skip_fields
+ * An array keyed by field IDs whose data has already been loaded and
+ * therefore should not be loaded again. Add a key to this array to indicate
+ * that your module has already loaded a field.
+ * @param $options
+ * An associative array of additional options, with the following keys:
+ * - field_id: The field ID that should be loaded. If unset, all fields
+ * should be loaded.
+ * - deleted: If TRUE, deleted fields should be loaded as well as
+ * non-deleted fields. If unset or FALSE, only non-deleted fields should be
+ * loaded.
+ */
+function hook_field_storage_pre_load($entity_type, $entities, $age, &$skip_fields, $options) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act before the storage backends insert field data.
+ *
+ * This hook allows modules to store data before the Field Storage API,
+ * optionally preventing the field storage module from doing so.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to save.
+ * @param $skip_fields
+ * An array keyed by field IDs whose data has already been written and
+ * therefore should not be written again. The values associated with these
+ * keys are not specified.
+ * @return
+ * Saved field IDs are set set as keys in $skip_fields.
+ */
+function hook_field_storage_pre_insert($entity_type, $entity, &$skip_fields) {
+ if ($entity_type == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
+ $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
+ foreach ($entity->taxonomy_forums as $language) {
+ foreach ($language as $delta) {
+ $query->values(array(
+ 'nid' => $entity->nid,
+ 'title' => $entity->title,
+ 'tid' => $delta['value'],
+ 'sticky' => $entity->sticky,
+ 'created' => $entity->created,
+ 'comment_count' => 0,
+ 'last_comment_timestamp' => $entity->created,
+ ));
+ }
+ }
+ $query->execute();
+ }
+}
+
+/**
+ * Act before the storage backends update field data.
+ *
+ * This hook allows modules to store data before the Field Storage API,
+ * optionally preventing the field storage module from doing so.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to save.
+ * @param $skip_fields
+ * An array keyed by field IDs whose data has already been written and
+ * therefore should not be written again. The values associated with these
+ * keys are not specified.
+ * @return
+ * Saved field IDs are set set as keys in $skip_fields.
+ */
+function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
+ $first_call = &drupal_static(__FUNCTION__, array());
+
+ if ($entity_type == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
+ // We don't maintain data for old revisions, so clear all previous values
+ // from the table. Since this hook runs once per field, per entity, make
+ // sure we only wipe values once.
+ if (!isset($first_call[$entity->nid])) {
+ $first_call[$entity->nid] = FALSE;
+ db_delete('forum_index')->condition('nid', $entity->nid)->execute();
+ }
+ // Only save data to the table if the node is published.
+ if ($entity->status) {
+ $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
+ foreach ($entity->taxonomy_forums as $language) {
+ foreach ($language as $delta) {
+ $query->values(array(
+ 'nid' => $entity->nid,
+ 'title' => $entity->title,
+ 'tid' => $delta['value'],
+ 'sticky' => $entity->sticky,
+ 'created' => $entity->created,
+ 'comment_count' => 0,
+ 'last_comment_timestamp' => $entity->created,
+ ));
+ }
+ }
+ $query->execute();
+ // The logic for determining last_comment_count is fairly complex, so
+ // call _forum_update_forum_index() too.
+ _forum_update_forum_index($entity->nid);
+ }
+ }
+}
+
+/**
+ * Returns the maximum weight for the entity components handled by the module.
+ *
+ * Field API takes care of fields and 'extra_fields'. This hook is intended for
+ * third-party modules adding other entity components (e.g. field_group).
+ *
+ * @param $entity_type
+ * The type of entity; e.g. 'node' or 'user'.
+ * @param $bundle
+ * The bundle name.
+ * @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.
+ */
+function hook_field_info_max_weight($entity_type, $bundle, $context) {
+ $weights = array();
+
+ foreach (my_module_entity_additions($entity_type, $bundle, $context) as $addition) {
+ $weights[] = $addition['weight'];
+ }
+
+ return $weights ? max($weights) : NULL;
+}
+
+/**
+ * Alters the display settings of a field before it gets displayed.
+ *
+ * Note that instead of hook_field_display_alter(), which is called for all
+ * fields on all entity types, hook_field_display_ENTITY_TYPE_alter() may be
+ * used to alter display settings for fields on a specific entity type only.
+ *
+ * This hook is called once per field per displayed entity. If the result of the
+ * hook involves reading from the database, it is highly recommended to
+ * statically cache the information.
+ *
+ * @param $display
+ * The display settings that will be used to display the field values, as
+ * found in the 'display' key of $instance definitions.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The entity type; e.g., 'node' or 'user'.
+ * - field: The field being rendered.
+ * - instance: The instance being rendered.
+ * - entity: The entity being rendered.
+ * - view_mode: The view mode, e.g. 'full', 'teaser'...
+ *
+ * @see hook_field_display_ENTITY_TYPE_alter()
+ */
+function hook_field_display_alter(&$display, $context) {
+ // Leave field labels out of the search index.
+ // Note: The check against $context['entity_type'] == 'node' could be avoided
+ // by using hook_field_display_node_alter() instead of
+ // hook_field_display_alter(), resulting in less function calls when
+ // rendering non-node entities.
+ if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
+ $display['label'] = 'hidden';
+ }
+}
+
+/**
+ * Alters the display settings of a field on a given entity type before it gets displayed.
+ *
+ * Modules can implement hook_field_display_ENTITY_TYPE_alter() to alter display
+ * settings for fields on a specific entity type, rather than implementing
+ * hook_field_display_alter().
+ *
+ * This hook is called once per field per displayed entity. If the result of the
+ * hook involves reading from the database, it is highly recommended to
+ * statically cache the information.
+ *
+ * @param $display
+ * The display settings that will be used to display the field values, as
+ * found in the 'display' key of $instance definitions.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The entity type; e.g., 'node' or 'user'.
+ * - field: The field being rendered.
+ * - instance: The instance being rendered.
+ * - entity: The entity being rendered.
+ * - view_mode: The view mode, e.g. 'full', 'teaser'...
+ *
+ * @see hook_field_display_alter()
+ */
+function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
+ // Leave field labels out of the search index.
+ if ($context['view_mode'] == 'search_index') {
+ $display['label'] = 'hidden';
+ }
+}
+
+/**
+ * Alters the display settings of pseudo-fields before an entity is displayed.
+ *
+ * This hook is called once per displayed entity. If the result of the hook
+ * involves reading from the database, it is highly recommended to statically
+ * cache the information.
+ *
+ * @param $displays
+ * An array of display settings for the pseudo-fields in the entity, keyed
+ * by pseudo-field names.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The entity type; e.g., 'node' or 'user'.
+ * - bundle: The bundle name.
+ * - view_mode: The view mode, e.g. 'full', 'teaser'...
+ */
+function hook_field_extra_fields_display_alter(&$displays, $context) {
+ if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') {
+ $displays['description']['visible'] = FALSE;
+ }
+}
+
+/**
+ * Alters the widget properties of a field instance on a given entity type
+ * before it gets displayed.
+ *
+ * Modules can implement hook_field_widget_properties_ENTITY_TYPE_alter() to
+ * alter the widget properties for fields on a specific entity type, rather than
+ * implementing hook_field_widget_properties_alter().
+ *
+ * This hook is called once per field per displayed widget entity. If the result
+ * of the hook involves reading from the database, it is highly recommended to
+ * statically cache the information.
+ *
+ * @param $widget
+ * The instance's widget properties.
+ * @param $context
+ * An associative array containing:
+ * - entity_type: The entity type; e.g., 'node' or 'user'.
+ * - entity: The entity object.
+ * - field: The field that the widget belongs to.
+ * - instance: The instance of the field.
+ *
+ * @see hook_field_widget_properties_alter()
+ */
+function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
+ // Change a widget's type according to the time of day.
+ $field = $context['field'];
+ if ($field['field_name'] == 'field_foo') {
+ $time = date('H');
+ $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
+ }
+}
+
+/**
+ * @} End of "addtogroup field_storage".
+ */
+
+/**
+ * @addtogroup field_crud
+ * @{
+ */
+
+/**
+ * Act on a field being created.
+ *
+ * This hook is invoked from field_create_field() after the field is created, to
+ * allow modules to act on field creation.
+ *
+ * @param $field
+ * The field just created.
+ */
+function hook_field_create_field($field) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on a field instance being created.
+ *
+ * This hook is invoked from field_create_instance() after the instance record
+ * is saved, so it cannot be used to modify the instance itself.
+ *
+ * @param $instance
+ * The instance just created.
+ */
+function hook_field_create_instance($instance) {
+ // @todo Needs function body.
+}
+
+/**
+ * Forbid a field update from occurring.
+ *
+ * Any module may forbid any update for any reason. For example, the
+ * field's storage module might forbid an update if it would change
+ * the storage schema while data for the field exists. A field type
+ * module might forbid an update if it would change existing data's
+ * semantics, or if there are external dependencies on field settings
+ * that cannot be updated.
+ *
+ * To forbid the update from occurring, throw a FieldUpdateForbiddenException.
+ *
+ * @param $field
+ * The field as it will be post-update.
+ * @param $prior_field
+ * The field as it is pre-update.
+ * @param $has_data
+ * Whether any data already exists for this field.
+ */
+function hook_field_update_forbid($field, $prior_field, $has_data) {
+ // A 'list' field stores integer keys mapped to display values. If
+ // the new field will have fewer values, and any data exists for the
+ // abandoned keys, the field will have no way to display them. So,
+ // forbid such an update.
+ if ($has_data && count($field['settings']['allowed_values']) < count($prior_field['settings']['allowed_values'])) {
+ // Identify the keys that will be lost.
+ $lost_keys = array_diff(array_keys($field['settings']['allowed_values']), array_keys($prior_field['settings']['allowed_values']));
+ // If any data exist for those keys, forbid the update.
+ $query = new EntityFieldQuery();
+ $found = $query
+ ->fieldCondition($prior_field['field_name'], 'value', $lost_keys)
+ ->range(0, 1)
+ ->execute();
+ if ($found) {
+ throw new FieldUpdateForbiddenException("Cannot update a list field not to include keys with existing data");
+ }
+ }
+}
+
+/**
+ * Act on a field being updated.
+ *
+ * This hook is invoked just after field is updated in field_update_field().
+ *
+ * @param $field
+ * The field as it is post-update.
+ * @param $prior_field
+ * The field as it was pre-update.
+ * @param $has_data
+ * Whether any data already exists for this field.
+ */
+function hook_field_update_field($field, $prior_field, $has_data) {
+ // Reset the static value that keeps track of allowed values for list fields.
+ drupal_static_reset('list_allowed_values');
+}
+
+/**
+ * Act on a field being deleted.
+ *
+ * This hook is invoked just after a field is deleted by field_delete_field().
+ *
+ * @param $field
+ * The field just deleted.
+ */
+function hook_field_delete_field($field) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on a field instance being updated.
+ *
+ * This hook is invoked from field_update_instance() after the instance record
+ * is saved, so it cannot be used by a module to modify the instance itself.
+ *
+ * @param $instance
+ * The instance as it is post-update.
+ * @param $prior_instance
+ * The instance as it was pre-update.
+ */
+function hook_field_update_instance($instance, $prior_instance) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on a field instance being deleted.
+ *
+ * This hook is invoked from field_delete_instance() after the instance is
+ * deleted.
+ *
+ * @param $instance
+ * The instance just deleted.
+ */
+function hook_field_delete_instance($instance) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on field records being read from the database.
+ *
+ * This hook is invoked from field_read_fields() on each field being read.
+ *
+ * @param $field
+ * The field record just read from the database.
+ */
+function hook_field_read_field($field) {
+ // @todo Needs function body.
+}
+
+/**
+ * Act on a field record being read from the database.
+ *
+ * This hook is invoked from field_read_instances() on each instance being read.
+ *
+ * @param $instance
+ * The instance record just read from the database.
+ */
+function hook_field_read_instance($instance) {
+ // @todo Needs function body.
+}
+
+/**
+ * Acts when a field record is being purged.
+ *
+ * In field_purge_field(), after the field configuration has been
+ * removed from the database, the field storage module has had a chance to
+ * run its hook_field_storage_purge_field(), and the field info cache
+ * has been cleared, this hook is invoked on all modules to allow them to
+ * respond to the field being purged.
+ *
+ * @param $field
+ * The field being purged.
+ */
+function hook_field_purge_field($field) {
+ db_delete('my_module_field_info')
+ ->condition('id', $field['id'])
+ ->execute();
+}
+
+/**
+ * Acts when a field instance is being purged.
+ *
+ * In field_purge_instance(), after the field instance has been
+ * removed from the database, the field storage module has had a chance to
+ * run its hook_field_storage_purge_instance(), and the field info cache
+ * has been cleared, this hook is invoked on all modules to allow them to
+ * respond to the field instance being purged.
+ *
+ * @param $instance
+ * The instance being purged.
+ */
+function hook_field_purge_instance($instance) {
+ db_delete('my_module_field_instance_info')
+ ->condition('id', $instance['id'])
+ ->execute();
+}
+
+/**
+ * Remove field storage information when a field record is purged.
+ *
+ * Called from field_purge_field() to allow the field storage module
+ * to remove field information when a field is being purged.
+ *
+ * @param $field
+ * The field being purged.
+ */
+function hook_field_storage_purge_field($field) {
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_drop_table($table_name);
+ db_drop_table($revision_name);
+}
+
+/**
+ * Remove field storage information when a field instance is purged.
+ *
+ * Called from field_purge_instance() to allow the field storage module
+ * to remove field instance information when a field instance is being
+ * purged.
+ *
+ * @param $instance
+ * The instance being purged.
+ */
+function hook_field_storage_purge_field_instance($instance) {
+ db_delete('my_module_field_instance_info')
+ ->condition('id', $instance['id'])
+ ->execute();
+}
+
+/**
+ * Remove field storage information when field data is purged.
+ *
+ * Called from field_purge_data() to allow the field storage
+ * module to delete field data information.
+ *
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The pseudo-entity whose field data to delete.
+ * @param $field
+ * The (possibly deleted) field whose data is being purged.
+ * @param $instance
+ * The deleted field instance whose data is being purged.
+ */
+function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+}
+
+/**
+ * @} End of "addtogroup field_crud".
+ */
+
+/**
+ * Determine whether the user has access to a given field.
+ *
+ * This hook is invoked from field_access() to let modules block access to
+ * operations on fields. If no module returns FALSE, the operation is allowed.
+ *
+ * @param $op
+ * The operation to be performed. Possible values: 'edit', 'view'.
+ * @param $field
+ * The field on which the operation is to be performed.
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * (optional) The entity for the operation.
+ * @param $account
+ * (optional) The account to check; if not given use currently logged in user.
+ *
+ * @return
+ * TRUE if the operation is allowed, and FALSE if the operation is denied.
+ */
+function hook_field_access($op, $field, $entity_type, $entity, $account) {
+ if ($field['field_name'] == 'field_of_interest' && $op == 'edit') {
+ return user_access('edit field of interest', $account);
+ }
+ return TRUE;
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.attach.inc b/kolab.org/www/drupal-7.26/modules/field/field.attach.inc
new file mode 100644
index 0000000..4a90961
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.attach.inc
@@ -0,0 +1,1416 @@
+<?php
+
+/**
+ * @file
+ * Field attach API, allowing entities (nodes, users, ...) to be 'fieldable'.
+ */
+
+/**
+ * Exception thrown by field_attach_validate() on field validation errors.
+ */
+class FieldValidationException extends FieldException {
+ var $errors;
+
+ /**
+ * Constructor for FieldValidationException.
+ *
+ * @param $errors
+ * An array of field validation errors, keyed by field name and
+ * delta that contains two keys:
+ * - 'error': A machine-readable error code string, prefixed by
+ * the field module name. A field widget may use this code to decide
+ * how to report the error.
+ * - 'message': A human-readable error message such as to be
+ * passed to form_error() for the appropriate form element.
+ */
+ function __construct($errors) {
+ $this->errors = $errors;
+ parent::__construct(t('Field validation errors'));
+ }
+}
+
+/**
+ * @defgroup field_storage Field Storage API
+ * @{
+ * Implement a storage engine for Field API data.
+ *
+ * The Field Attach API uses the Field Storage API to perform all "database
+ * access". Each Field Storage API hook function defines a primitive database
+ * operation such as read, write, or delete. The default field storage module,
+ * field_sql_storage.module, uses the local SQL database to implement these
+ * operations, but alternative field storage backends can choose to represent
+ * the data in SQL differently or use a completely different storage mechanism
+ * such as a cloud-based database.
+ *
+ * Each field defines which storage backend it uses. The Drupal system variable
+ * 'field_storage_default' identifies the storage backend used by default.
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Argument for an update operation.
+ *
+ * This is used in hook_field_storage_write when updating an
+ * existing entity.
+ */
+define('FIELD_STORAGE_UPDATE', 'update');
+
+/**
+ * Argument for an insert operation.
+ *
+ * This is used in hook_field_storage_write when inserting a new entity.
+ */
+define('FIELD_STORAGE_INSERT', 'insert');
+
+/**
+ * @} End of "defgroup field_storage".
+ */
+
+/**
+ * @defgroup field_attach Field Attach API
+ * @{
+ * Operate on Field API data attached to Drupal entities.
+ *
+ * Field Attach API functions load, store, display, generate Field API
+ * structures, and perform a variety of other functions for field data attached
+ * to individual entities.
+ *
+ * Field Attach API functions generally take $entity_type and $entity arguments
+ * along with additional function-specific arguments. $entity_type is the type
+ * of the fieldable entity, such as 'node' or 'user', and $entity is the entity
+ * itself.
+ *
+ * hook_entity_info() is the central place for entity types to define if and
+ * how Field API should operate on their entity objects. Notably, the
+ * 'fieldable' property needs to be set to TRUE.
+ *
+ * The Field Attach API uses the concept of bundles: the set of fields for a
+ * given entity is defined on a per-bundle basis. The collection of bundles for
+ * an entity type is defined its hook_entity_info() implementation. For
+ * instance, node_entity_info() exposes each node type as its own bundle. This
+ * means that the set of fields of a node is determined by the node type. The
+ * Field API reads the bundle name for a given entity from a particular
+ * property of the entity object, and hook_entity_info() defines which property
+ * to use. For instance, node_entity_info() specifies:
+ * @code $info['entity keys']['bundle'] = 'type'@endcode
+ * This indicates that for a particular node object, the bundle name can be
+ * found in $node->type. This property can be omitted if the entity type only
+ * exposes a single bundle (all entities of this type have the same collection
+ * of fields). This is the case for the 'user' entity type.
+ *
+ * Most Field Attach API functions define a corresponding hook function that
+ * allows any module to act on Field Attach operations for any entity after the
+ * operation is complete, and access or modify all the field, form, or display
+ * data for that entity and operation. For example, field_attach_view() invokes
+ * hook_field_attach_view_alter(). These all-module hooks are distinct from
+ * those of the Field Types API, such as hook_field_load(), that are only
+ * invoked for the module that defines a specific field type.
+ *
+ * field_attach_load(), field_attach_insert(), and field_attach_update() also
+ * define pre-operation hooks, e.g. hook_field_attach_pre_load(). These hooks
+ * run before the corresponding Field Storage API and Field Type API
+ * operations. They allow modules to define additional storage locations (e.g.
+ * denormalizing, mirroring) for field data on a per-field basis. They also
+ * allow modules to take over field storage completely by instructing other
+ * implementations of the same hook and the Field Storage API itself not to
+ * operate on specified fields.
+ *
+ * The pre-operation hooks do not make the Field Storage API irrelevant. The
+ * Field Storage API is essentially the "fallback mechanism" for any fields
+ * that aren't being intercepted explicitly by pre-operation hooks.
+ *
+ * @link field_language Field Language API @endlink provides information about
+ * the structure of field objects.
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Invoke a field hook.
+ *
+ * @param $op
+ * Possible operations include:
+ * - form
+ * - validate
+ * - presave
+ * - insert
+ * - update
+ * - delete
+ * - delete revision
+ * - view
+ * - prepare translation
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The fully formed $entity_type entity.
+ * @param $a
+ * - The $form in the 'form' operation.
+ * - The value of $view_mode in the 'view' operation.
+ * - Otherwise NULL.
+ * @param $b
+ * - The $form_state in the 'submit' operation.
+ * - Otherwise NULL.
+ * @param $options
+ * An associative array of additional options, with the following keys:
+ * - 'field_name': The name of the field whose operation should be
+ * invoked. By default, the operation is invoked on all the fields
+ * in the entity's bundle. NOTE: This option is not compatible with
+ * the 'deleted' option; the 'field_id' option should be used
+ * instead.
+ * - 'field_id': The id of the field whose operation should be
+ * invoked. By default, the operation is invoked on all the fields
+ * in the entity's' bundles.
+ * - 'default': A boolean value, specifying which implementation of
+ * the operation should be invoked.
+ * - if FALSE (default), the field types implementation of the operation
+ * will be invoked (hook_field_[op])
+ * - If TRUE, the default field implementation of the field operation
+ * will be invoked (field_default_[op])
+ * Internal use only. Do not explicitely set to TRUE, but use
+ * _field_invoke_default() instead.
+ * - 'deleted': If TRUE, the function will operate on deleted fields
+ * as well as non-deleted fields. If unset or FALSE, only
+ * non-deleted fields are operated on.
+ * - 'language': A language code or an array of language codes keyed by field
+ * name. It will be used to narrow down to a single value the available
+ * languages to act on.
+ */
+function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $options = array()) {
+ // Merge default options.
+ $default_options = array(
+ 'default' => FALSE,
+ 'deleted' => FALSE,
+ 'language' => NULL,
+ );
+ $options += $default_options;
+
+ // Determine the list of instances to iterate on.
+ list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+ $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+
+ // Iterate through the instances and collect results.
+ $return = array();
+ foreach ($instances as $instance) {
+ // field_info_field() is not available for deleted fields, so use
+ // field_info_field_by_id().
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_name = $field['field_name'];
+ $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
+ if (function_exists($function)) {
+ // Determine the list of languages to iterate on.
+ $available_languages = field_available_languages($entity_type, $field);
+ $languages = _field_language_suggestion($available_languages, $options['language'], $field_name);
+
+ foreach ($languages as $langcode) {
+ $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
+ $result = $function($entity_type, $entity, $field, $instance, $langcode, $items, $a, $b);
+ if (isset($result)) {
+ // For hooks with array results, we merge results together.
+ // For hooks with scalar results, we collect results in an array.
+ if (is_array($result)) {
+ $return = array_merge($return, $result);
+ }
+ else {
+ $return[] = $result;
+ }
+ }
+
+ // Populate $items back in the field values, but avoid replacing missing
+ // fields with an empty array (those are not equivalent on update).
+ if ($items !== array() || isset($entity->{$field_name}[$langcode])) {
+ $entity->{$field_name}[$langcode] = $items;
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Invoke a field hook across fields on multiple entities.
+ *
+ * @param $op
+ * Possible operations include:
+ * - load
+ * - prepare_view
+ * For all other operations, use _field_invoke() / field_invoke_default()
+ * instead.
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entities
+ * An array of entities, keyed by entity id.
+ * @param $a
+ * - The $age parameter in the 'load' operation.
+ * - Otherwise NULL.
+ * @param $b
+ * Currently always NULL.
+ * @param $options
+ * An associative array of additional options, with the following keys:
+ * - 'field_name': The name of the field whose operation should be
+ * invoked. By default, the operation is invoked on all the fields
+ * in the entity's bundle. NOTE: This option is not compatible with
+ * the 'deleted' option; the 'field_id' option should be used instead.
+ * - 'field_id': The id of the field whose operation should be
+ * invoked. By default, the operation is invoked on all the fields
+ * in the entity's' bundles.
+ * - 'default': A boolean value, specifying which implementation of
+ * the operation should be invoked.
+ * - if FALSE (default), the field types implementation of the operation
+ * will be invoked (hook_field_[op])
+ * - If TRUE, the default field implementation of the field operation
+ * will be invoked (field_default_[op])
+ * Internal use only. Do not explicitely set to TRUE, but use
+ * _field_invoke_multiple_default() instead.
+ * - 'deleted': If TRUE, the function will operate on deleted fields
+ * as well as non-deleted fields. If unset or FALSE, only
+ * non-deleted fields are operated on.
+ * - 'language': A language code or an array of arrays of language codes keyed
+ * by entity id and field name. It will be used to narrow down to a single
+ * value the available languages to act on.
+ *
+ * @return
+ * An array of returned values keyed by entity id.
+ */
+function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b = NULL, $options = array()) {
+ // Merge default options.
+ $default_options = array(
+ 'default' => FALSE,
+ 'deleted' => FALSE,
+ 'language' => NULL,
+ );
+ $options += $default_options;
+
+ $fields = array();
+ $grouped_instances = array();
+ $grouped_entities = array();
+ $grouped_items = array();
+ $return = array();
+
+ // Go through the entities and collect the fields on which the hook should be
+ // invoked.
+ //
+ // We group fields by id, not by name, because this function can operate on
+ // deleted fields which may have non-unique names. However, entities can only
+ // contain data for a single field for each name, even if that field
+ // is deleted, so we reference field data via the
+ // $entity->$field_name property.
+ foreach ($entities as $entity) {
+ // Determine the list of instances to iterate on.
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+
+ foreach ($instances as $instance) {
+ $field_id = $instance['field_id'];
+ $field_name = $instance['field_name'];
+ $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.
+ if (!isset($fields[$field_id])) {
+ $fields[$field_id] = $field;
+ }
+ // Extract the field values into a separate variable, easily accessed
+ // by hook implementations.
+ // 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'];
+ $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();
+ // Group the instances and entities corresponding to the current
+ // field.
+ $grouped_instances[$field_id][$langcode][$id] = $instance;
+ $grouped_entities[$field_id][$langcode][$id] = $entities[$id];
+ }
+ }
+ }
+ // Initialize the return value for each entity.
+ $return[$id] = array();
+ }
+
+ // For each field, invoke the field hook and collect results.
+ foreach ($fields as $field_id => $field) {
+ $field_name = $field['field_name'];
+ $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
+ // Iterate over all the field translations.
+ foreach ($grouped_items[$field_id] as $langcode => &$items) {
+ $entities = $grouped_entities[$field_id][$langcode];
+ $instances = $grouped_instances[$field_id][$langcode];
+ $results = $function($entity_type, $entities, $field, $instances, $langcode, $items, $a, $b);
+ if (isset($results)) {
+ // Collect results by entity.
+ // For hooks with array results, we merge results together.
+ // For hooks with scalar results, we collect results in an array.
+ foreach ($results as $id => $result) {
+ if (is_array($result)) {
+ $return[$id] = array_merge($return[$id], $result);
+ }
+ else {
+ $return[$id][] = $result;
+ }
+ }
+ }
+ }
+
+ // Populate field values back in the entities, but avoid replacing missing
+ // fields with an empty array (those are not equivalent on update).
+ foreach ($grouped_entities[$field_id] as $langcode => $entities) {
+ foreach ($entities as $id => $entity) {
+ if ($grouped_items[$field_id][$langcode][$id] !== array() || isset($entity->{$field_name}[$langcode])) {
+ $entity->{$field_name}[$langcode] = $grouped_items[$field_id][$langcode][$id];
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Invoke field.module's version of a field hook.
+ *
+ * This function invokes the field_default_[op]() function.
+ * Use _field_invoke() to invoke the field type implementation,
+ * hook_field_[op]().
+ *
+ * @see _field_invoke()
+ */
+function _field_invoke_default($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $options = array()) {
+ $options['default'] = TRUE;
+ return _field_invoke($op, $entity_type, $entity, $a, $b, $options);
+}
+
+/**
+ * Invoke field.module's version of a field hook on multiple entities.
+ *
+ * This function invokes the field_default_[op]() function.
+ * Use _field_invoke_multiple() to invoke the field type implementation,
+ * hook_field_[op]().
+ *
+ * @see _field_invoke_multiple()
+ */
+function _field_invoke_multiple_default($op, $entity_type, $entities, &$a = NULL, &$b = NULL, $options = array()) {
+ $options['default'] = TRUE;
+ return _field_invoke_multiple($op, $entity_type, $entities, $a, $b, $options);
+}
+
+/**
+ * Helper for _field_invoke(): retrieves a list of instances to operate on.
+ *
+ * @param $entity_type
+ * The entity type.
+ * @param $bundle
+ * The bundle name.
+ * @param $options
+ * An associative array of options, as provided to _field_invoke(). Only the
+ * following keys are considered :
+ * - deleted
+ * - field_name
+ * - field_id
+ * See _field_invoke() for details.
+ *
+ * @return
+ * The array of selected instance definitions.
+ */
+function _field_invoke_get_instances($entity_type, $bundle, $options) {
+ if ($options['deleted']) {
+ // Deleted fields are not included in field_info_instances(), and need to
+ // be fetched from the database with field_read_instances().
+ $params = array('entity_type' => $entity_type, 'bundle' => $bundle);
+ if (isset($options['field_id'])) {
+ // Single-field mode by field id: field_read_instances() does the filtering.
+ // Single-field mode by field name is not compatible with the 'deleted'
+ // option.
+ $params['field_id'] = $options['field_id'];
+ }
+ $instances = field_read_instances($params, array('include_deleted' => TRUE));
+ }
+ elseif (isset($options['field_name'])) {
+ // Single-field mode by field name: field_info_instance() does the
+ // filtering.
+ $instances = array(field_info_instance($entity_type, $options['field_name'], $bundle));
+ }
+ else {
+ $instances = field_info_instances($entity_type, $bundle);
+ if (isset($options['field_id'])) {
+ // Single-field mode by field id: we need to loop on each instance to
+ // find the right one.
+ foreach ($instances as $instance) {
+ if ($instance['field_id'] == $options['field_id']) {
+ $instances = array($instance);
+ break;
+ }
+ }
+ }
+ }
+
+ return $instances;
+}
+
+/**
+ * Add form elements for all fields for an entity to a form structure.
+ *
+ * The form elements for the entity's fields are added by reference as direct
+ * children in the $form parameter. This parameter can be a full form structure
+ * (most common case for entity edit forms), or a sub-element of a larger form.
+ *
+ * By default, submitted field values appear at the top-level of
+ * $form_state['values']. A different location within $form_state['values'] can
+ * be specified by setting the '#parents' property on the incoming $form
+ * parameter. Because of name clashes, two instances of the same field cannot
+ * appear within the same $form element, or within the same '#parents' space.
+ *
+ * For each call to field_attach_form(), field values are processed by calling
+ * field_attach_form_validate() and field_attach_submit() on the same $form
+ * element.
+ *
+ * Sample resulting structure in $form:
+ * @code
+ * '#parents' => The location of field values in $form_state['values'],
+ * '#entity_type' => The name of the entity type,
+ * '#bundle' => The name of the bundle,
+ * // One sub-array per field appearing in the entity, keyed by field name.
+ * // The structure of the array differs slightly depending on whether the
+ * // widget is 'single-value' (provides the input for one field value,
+ * // most common case), and will therefore be repeated as many times as
+ * // needed, or 'multiple-values' (one single widget allows the input of
+ * // several values, e.g checkboxes, select box...).
+ * // The sub-array is nested into a $langcode key where $langcode has the
+ * // same value of the $langcode parameter above.
+ * // The '#language' key holds the same value of $langcode and it is used
+ * // to access the field sub-array when $langcode is unknown.
+ * 'field_foo' => array(
+ * '#tree' => TRUE,
+ * '#field_name' => The name of the field,
+ * '#language' => $langcode,
+ * $langcode => array(
+ * '#field_name' => The name of the field,
+ * '#language' => $langcode,
+ * '#field_parents' => The 'parents' space for the field in the form,
+ * equal to the #parents property of the $form parameter received by
+ * field_attach_form(),
+ * '#required' => Whether or not the field is required,
+ * '#title' => The label of the field instance,
+ * '#description' => The description text for the field instance,
+ *
+ * // Only for 'single' widgets:
+ * '#theme' => 'field_multiple_value_form',
+ * '#cardinality' => The field cardinality,
+ * // One sub-array per copy of the widget, keyed by delta.
+ * 0 => array(
+ * '#entity_type' => The name of the entity type,
+ * '#bundle' => The name of the bundle,
+ * '#field_name' => The name of the field,
+ * '#field_parents' => The 'parents' space for the field in the form,
+ * equal to the #parents property of the $form parameter received by
+ * field_attach_form(),
+ * '#title' => The title to be displayed by the widget,
+ * '#default_value' => The field value for delta 0,
+ * '#required' => Whether the widget should be marked required,
+ * '#delta' => 0,
+ * '#columns' => The array of field columns,
+ * // The remaining elements in the sub-array depend on the widget.
+ * '#type' => The type of the widget,
+ * ...
+ * ),
+ * 1 => array(
+ * ...
+ * ),
+ *
+ * // Only for multiple widgets:
+ * '#entity_type' => The name of the entity type,
+ * '#bundle' => $instance['bundle'],
+ * '#columns' => array_keys($field['columns']),
+ * // The remaining elements in the sub-array depend on the widget.
+ * '#type' => The type of the widget,
+ * ...
+ * ),
+ * ...
+ * ),
+ * )
+ * @endcode
+ *
+ * Additionally, some processing data is placed in $form_state, and can be
+ * accessed by field_form_get_state() and field_form_set_state().
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity for which to load form elements, used to initialize
+ * default form values.
+ * @param $form
+ * The form structure to fill in. This can be a full form structure, or a
+ * sub-element of a larger form. The #parents property can be set to control
+ * the location of submitted field values within $form_state['values']. If
+ * not specified, $form['#parents'] is set to an empty array, placing field
+ * values at the top-level of $form_state['values'].
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ * @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, $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['language'] = field_valid_language($langcode);
+ $form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
+
+ // Add custom weight handling.
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ $form['#pre_render'][] = '_field_extra_fields_pre_render';
+ $form['#entity_type'] = $entity_type;
+ $form['#bundle'] = $bundle;
+
+ // Let other modules make changes to the form.
+ // Avoid module_invoke_all() to let parameters be taken by reference.
+ foreach (module_implements('field_attach_form') as $module) {
+ $function = $module . '_field_attach_form';
+ $function($entity_type, $entity, $form, $form_state, $langcode);
+ }
+}
+
+/**
+ * Loads fields for the current revisions of a group of entities.
+ *
+ * Loads all fields for each entity object in a group of a single entity type.
+ * The loaded field values are added directly to the entity objects.
+ *
+ * field_attach_load() is automatically called by the default entity controller
+ * class, and thus, in most cases, doesn't need to be explicitly called by the
+ * entity type module.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entities
+ * An array of entities for which to load fields, keyed by entity ID.
+ * Each entity needs to have its 'bundle', 'id' and (if applicable)
+ * 'revision' keys filled in. The function adds the loaded field data
+ * directly in the entity objects of the $entities array.
+ * @param $age
+ * FIELD_LOAD_CURRENT to load the most recent revision for all
+ * fields, or FIELD_LOAD_REVISION to load the version indicated by
+ * each entity. Defaults to FIELD_LOAD_CURRENT; use
+ * field_attach_load_revision() instead of passing FIELD_LOAD_REVISION.
+ * @param $options
+ * An associative array of additional options, with the following keys:
+ * - 'field_id': The field ID that should be loaded, instead of
+ * loading all fields, for each entity. Note that returned entities
+ * may contain data for other fields, for example if they are read
+ * from a cache.
+ * - 'deleted': If TRUE, the function will operate on deleted fields
+ * as well as non-deleted fields. If unset or FALSE, only
+ * non-deleted fields are operated on.
+ */
+function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
+ $load_current = $age == FIELD_LOAD_CURRENT;
+
+ // Merge default options.
+ $default_options = array(
+ 'deleted' => FALSE,
+ );
+ $options += $default_options;
+
+ $info = entity_get_info($entity_type);
+ // Only the most current revision of non-deleted fields for cacheable entity
+ // types can be cached.
+ $cache_read = $load_current && $info['field cache'] && empty($options['deleted']);
+ // In addition, do not write to the cache when loading a single field.
+ $cache_write = $cache_read && !isset($options['field_id']);
+
+ if (empty($entities)) {
+ return;
+ }
+
+ // Assume all entities will need to be queried. Entities found in the cache
+ // will be removed from the list.
+ $queried_entities = $entities;
+
+ // Fetch available entities from cache, if applicable.
+ if ($cache_read) {
+ // Build the list of cache entries to retrieve.
+ $cids = array();
+ foreach ($entities as $id => $entity) {
+ $cids[] = "field:$entity_type:$id";
+ }
+ $cache = cache_get_multiple($cids, 'cache_field');
+ // Put the cached field values back into the entities and remove them from
+ // the list of entities to query.
+ foreach ($entities as $id => $entity) {
+ $cid = "field:$entity_type:$id";
+ if (isset($cache[$cid])) {
+ unset($queried_entities[$id]);
+ foreach ($cache[$cid]->data as $field_name => $values) {
+ $entity->$field_name = $values;
+ }
+ }
+ }
+ }
+
+ // Fetch other entities from their storage location.
+ if ($queried_entities) {
+ // The invoke order is:
+ // - hook_field_storage_pre_load()
+ // - storage backend's hook_field_storage_load()
+ // - field-type module's hook_field_load()
+ // - hook_field_attach_load()
+
+ // Invoke hook_field_storage_pre_load(): let any module load field
+ // data before the storage engine, accumulating along the way.
+ $skip_fields = array();
+ foreach (module_implements('field_storage_pre_load') as $module) {
+ $function = $module . '_field_storage_pre_load';
+ $function($entity_type, $queried_entities, $age, $skip_fields, $options);
+ }
+
+ $instances = array();
+
+ // Collect the storage backends used by the remaining fields in the entities.
+ $storages = array();
+ foreach ($queried_entities as $entity) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+
+ foreach ($instances as $instance) {
+ $field_name = $instance['field_name'];
+ $field_id = $instance['field_id'];
+ // Make sure all fields are present at least as empty arrays.
+ if (!isset($queried_entities[$id]->{$field_name})) {
+ $queried_entities[$id]->{$field_name} = array();
+ }
+ // Collect the storage backend if the field has not been loaded yet.
+ if (!isset($skip_fields[$field_id])) {
+ $field = field_info_field_by_id($field_id);
+ $storages[$field['storage']['type']][$field_id][] = $load_current ? $id : $vid;
+ }
+ }
+ }
+
+ // Invoke hook_field_storage_load() on the relevant storage backends.
+ foreach ($storages as $storage => $fields) {
+ $storage_info = field_info_storage_types($storage);
+ module_invoke($storage_info['module'], 'field_storage_load', $entity_type, $queried_entities, $age, $fields, $options);
+ }
+
+ // Invoke field-type module's hook_field_load().
+ $null = NULL;
+ _field_invoke_multiple('load', $entity_type, $queried_entities, $age, $null, $options);
+
+ // Invoke hook_field_attach_load(): let other modules act on loading the
+ // entity.
+ module_invoke_all('field_attach_load', $entity_type, $queried_entities, $age, $options);
+
+ // Build cache data.
+ if ($cache_write) {
+ foreach ($queried_entities as $id => $entity) {
+ $data = array();
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ $instances = field_info_instances($entity_type, $bundle);
+ foreach ($instances as $instance) {
+ $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
+ }
+ $cid = "field:$entity_type:$id";
+ cache_set($cid, $data, 'cache_field');
+ }
+ }
+ }
+}
+
+/**
+ * Load all fields for previous versions of a group of entities.
+ *
+ * Loading different versions of the same entities is not supported, and should
+ * be done by separate calls to the function.
+ *
+ * field_attach_load_revision() is automatically called by the default entity
+ * controller class, and thus, in most cases, doesn't need to be explicitly
+ * called by the entity type module.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entities
+ * An array of entities for which to load fields, keyed by entity ID. Each
+ * entity needs to have its 'bundle', 'id' and (if applicable) 'revision'
+ * keys filled. The function adds the loaded field data directly in the
+ * entity objects of the $entities array.
+ * @param $options
+ * An associative array of additional options. See field_attach_load() for
+ * details.
+ */
+function field_attach_load_revision($entity_type, $entities, $options = array()) {
+ return field_attach_load($entity_type, $entities, FIELD_LOAD_REVISION, $options);
+}
+
+/**
+ * Perform field validation against the field data in an entity.
+ *
+ * This function does not perform field widget validation on form
+ * submissions. It is intended to be called during API save
+ * operations. Use field_attach_form_validate() to validate form
+ * submissions.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to validate.
+ * @throws FieldValidationException
+ * 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, $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.
+ $null = NULL;
+ _field_invoke_default('validate', $entity_type, $entity, $errors, $null, $options);
+ // Check field-type specific 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.
+ foreach (module_implements('field_attach_validate') as $module) {
+ $function = $module . '_field_attach_validate';
+ $function($entity_type, $entity, $errors);
+ }
+
+ if ($errors) {
+ throw new FieldValidationException($errors);
+ }
+}
+
+/**
+ * Perform field validation against form-submitted field values.
+ *
+ * There are two levels of validation for fields in forms: widget
+ * validation, and field validation.
+ * - Widget validation steps are specific to a given widget's own form
+ * structure and UI metaphors. They are executed through FAPI's
+ * #element_validate property during normal form validation.
+ * - Field validation steps are common to a given field type, independently of
+ * the specific widget being used in a given form. They are defined in the
+ * field type's implementation of hook_field_validate().
+ *
+ * This function performs field validation in the context of a form
+ * submission. It converts field validation errors into form errors
+ * on the correct form elements. Fieldable entity types should call
+ * this function during their own form validation function.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity being submitted. The 'bundle', 'id' and (if applicable)
+ * 'revision' keys should be present. The actual field values will be read
+ * from $form_state['values'].
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * 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, $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, $options);
+ }
+ catch (FieldValidationException $e) {
+ // Pass field-level validation errors back to widgets for accurate error
+ // flagging.
+ foreach ($e->errors as $field_name => $field_errors) {
+ foreach ($field_errors as $langcode => $errors) {
+ $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+ $field_state['errors'] = $errors;
+ field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
+ }
+ }
+ _field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state, $options);
+ }
+}
+
+/**
+ * Perform necessary operations on field data submitted by a form.
+ *
+ * Currently, this accounts for drag-and-drop reordering of
+ * field values, and filtering of empty values.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity being submitted. The 'bundle', 'id' and (if applicable)
+ * 'revision' keys should be present. The actual field values will be read
+ * from $form_state['values'].
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * 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, $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, $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.
+ foreach (module_implements('field_attach_submit') as $module) {
+ $function = $module . '_field_attach_submit';
+ $function($entity_type, $entity, $form, $form_state);
+ }
+}
+
+/**
+ * Perform necessary operations just before fields data get saved.
+ *
+ * We take no specific action here, we just give other
+ * modules the opportunity to act.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to process.
+ */
+function field_attach_presave($entity_type, $entity) {
+ _field_invoke('presave', $entity_type, $entity);
+
+ // Let other modules act on presaving the entity.
+ module_invoke_all('field_attach_presave', $entity_type, $entity);
+}
+
+/**
+ * Save field data for a new entity.
+ *
+ * The passed-in entity must already contain its id and (if applicable)
+ * revision id attributes.
+ * Default values (if any) will be saved for fields not present in the
+ * $entity.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to save.
+ * @return
+ * Default values (if any) will be added to the $entity parameter for fields
+ * it leaves unspecified.
+ */
+function field_attach_insert($entity_type, $entity) {
+ _field_invoke_default('insert', $entity_type, $entity);
+ _field_invoke('insert', $entity_type, $entity);
+
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Let any module insert field data before the storage engine, accumulating
+ // saved fields along the way.
+ $skip_fields = array();
+ foreach (module_implements('field_storage_pre_insert') as $module) {
+ $function = $module . '_field_storage_pre_insert';
+ $function($entity_type, $entity, $skip_fields);
+ }
+
+ // Collect the storage backends used by the remaining fields in the entities.
+ $storages = array();
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_id = $field['id'];
+ $field_name = $field['field_name'];
+ if (!empty($entity->$field_name)) {
+ // Collect the storage backend if the field has not been written yet.
+ if (!isset($skip_fields[$field_id])) {
+ $storages[$field['storage']['type']][$field_id] = $field_id;
+ }
+ }
+ }
+
+ // Field storage backends save any remaining unsaved fields.
+ foreach ($storages as $storage => $fields) {
+ $storage_info = field_info_storage_types($storage);
+ module_invoke($storage_info['module'], 'field_storage_write', $entity_type, $entity, FIELD_STORAGE_INSERT, $fields);
+ }
+
+ // Let other modules act on inserting the entity.
+ module_invoke_all('field_attach_insert', $entity_type, $entity);
+
+ $entity_info = entity_get_info($entity_type);
+}
+
+/**
+ * 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
+ * The entity with fields to save.
+ */
+function field_attach_update($entity_type, $entity) {
+ _field_invoke('update', $entity_type, $entity);
+
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Let any module update field data before the storage engine, accumulating
+ // saved fields along the way.
+ $skip_fields = array();
+ foreach (module_implements('field_storage_pre_update') as $module) {
+ $function = $module . '_field_storage_pre_update';
+ $function($entity_type, $entity, $skip_fields);
+ }
+
+ // Collect the storage backends used by the remaining fields in the entities.
+ $storages = array();
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_id = $field['id'];
+ $field_name = $field['field_name'];
+ // Leave the field untouched if $entity comes with no $field_name property,
+ // but empty the field if it comes as a NULL value or an empty array.
+ // Function property_exists() is slower, so we catch the more frequent
+ // cases where it's an empty array with the faster isset().
+ if (isset($entity->$field_name) || property_exists($entity, $field_name)) {
+ // Collect the storage backend if the field has not been written yet.
+ if (!isset($skip_fields[$field_id])) {
+ $storages[$field['storage']['type']][$field_id] = $field_id;
+ }
+ }
+ }
+
+ // Field storage backends save any remaining unsaved fields.
+ foreach ($storages as $storage => $fields) {
+ $storage_info = field_info_storage_types($storage);
+ module_invoke($storage_info['module'], 'field_storage_write', $entity_type, $entity, FIELD_STORAGE_UPDATE, $fields);
+ }
+
+ // Let other modules act on updating the entity.
+ module_invoke_all('field_attach_update', $entity_type, $entity);
+
+ $entity_info = entity_get_info($entity_type);
+ if ($entity_info['field cache']) {
+ cache_clear_all("field:$entity_type:$id", 'cache_field');
+ }
+}
+
+/**
+ * Delete field data for an existing entity. This deletes all
+ * revisions of field data for the entity.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity whose field data to delete.
+ */
+function field_attach_delete($entity_type, $entity) {
+ _field_invoke('delete', $entity_type, $entity);
+
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Collect the storage backends used by the fields in the entities.
+ $storages = array();
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_id = $field['id'];
+ $storages[$field['storage']['type']][$field_id] = $field_id;
+ }
+
+ // Field storage backends delete their data.
+ foreach ($storages as $storage => $fields) {
+ $storage_info = field_info_storage_types($storage);
+ module_invoke($storage_info['module'], 'field_storage_delete', $entity_type, $entity, $fields);
+ }
+
+ // Let other modules act on deleting the entity.
+ module_invoke_all('field_attach_delete', $entity_type, $entity);
+
+ $entity_info = entity_get_info($entity_type);
+ if ($entity_info['field cache']) {
+ cache_clear_all("field:$entity_type:$id", 'cache_field');
+ }
+}
+
+/**
+ * Delete field data for a single revision of an existing entity. The
+ * passed entity must have a revision id attribute.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to save.
+ */
+function field_attach_delete_revision($entity_type, $entity) {
+ _field_invoke('delete_revision', $entity_type, $entity);
+
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Collect the storage backends used by the fields in the entities.
+ $storages = array();
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_id = $field['id'];
+ $storages[$field['storage']['type']][$field_id] = $field_id;
+ }
+
+ // Field storage backends delete their data.
+ foreach ($storages as $storage => $fields) {
+ $storage_info = field_info_storage_types($storage);
+ module_invoke($storage_info['module'], 'field_storage_delete_revision', $entity_type, $entity, $fields);
+ }
+
+ // Let other modules act on deleting the revision.
+ module_invoke_all('field_attach_delete_revision', $entity_type, $entity);
+}
+
+/**
+ * Prepare field data prior to display.
+ *
+ * This function lets field types and formatters load additional data
+ * needed for display that is not automatically loaded during
+ * field_attach_load(). It accepts an array of entities to allow query
+ * optimisation when displaying lists of entities.
+ *
+ * field_attach_prepare_view() and field_attach_view() are two halves
+ * of the same operation. It is safe to call
+ * field_attach_prepare_view() multiple times on the same entity
+ * before calling field_attach_view() on it, but calling any Field
+ * API operation on an entity between passing that entity to these two
+ * functions may yield incorrect results.
+ *
+ * @param $entity_type
+ * The type of $entities; e.g. 'node' or 'user'.
+ * @param $entities
+ * An array of entities, keyed by entity id.
+ * @param $view_mode
+ * View mode, e.g. 'full', 'teaser'...
+ * @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()) {
+ // 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.
+ // @todo: resolve this more generally for both entity and field level hooks.
+ $prepare = array();
+ foreach ($entities as $id => $entity) {
+ if (empty($entity->_field_view_prepared)) {
+ // Add this entity to the items to be prepared.
+ $prepare[$id] = $entity;
+
+ // Determine the actual language to display for each field, given the
+ // languages available in the field data.
+ $options['language'][$id] = field_language($entity_type, $entity, NULL, $langcode);
+
+ // Mark this item as prepared.
+ $entity->_field_view_prepared = TRUE;
+ }
+ }
+
+ $null = NULL;
+ // First let the field types do their preparation.
+ _field_invoke_multiple('prepare_view', $entity_type, $prepare, $null, $null, $options);
+ // Then let the formatters do their own specific massaging.
+ // field_default_prepare_view() takes care of dispatching to the correct
+ // formatters according to the display settings for the view mode.
+ _field_invoke_multiple_default('prepare_view', $entity_type, $prepare, $view_mode, $null, $options);
+}
+
+/**
+ * Returns a renderable array for the fields on an entity.
+ *
+ * Each field is displayed according to the display options specified in the
+ * $instance definition for the given $view_mode.
+ *
+ * field_attach_prepare_view() and field_attach_view() are two halves
+ * of the same operation. It is safe to call
+ * field_attach_prepare_view() multiple times on the same entity
+ * before calling field_attach_view() on it, but calling any Field
+ * API operation on an entity between passing that entity to these two
+ * functions may yield incorrect results.
+ *
+ * Sample structure:
+ * @code
+ * array(
+ * 'field_foo' => array(
+ * '#theme' => 'field',
+ * '#title' => the label of the field instance,
+ * '#label_display' => the label display mode,
+ * '#object' => the fieldable entity being displayed,
+ * '#entity_type' => the type of the entity being displayed,
+ * '#language' => the language of the field values being displayed,
+ * '#view_mode' => the view mode,
+ * '#field_name' => the name of the field,
+ * '#field_type' => the type of the field,
+ * '#formatter' => the name of the formatter,
+ * '#items' => the field values being displayed,
+ * // The element's children are the formatted values returned by
+ * // hook_field_formatter_view().
+ * ),
+ * );
+ * @endcode
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to render.
+ * @param $view_mode
+ * View mode, e.g. 'full', 'teaser'...
+ * @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, $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['language'] = $display_language;
+
+ // Invoke field_default_view().
+ $null = NULL;
+ $output = _field_invoke_default('view', $entity_type, $entity, $view_mode, $null, $options);
+
+ // Add custom weight handling.
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ $output['#pre_render'][] = '_field_extra_fields_pre_render';
+ $output['#entity_type'] = $entity_type;
+ $output['#bundle'] = $bundle;
+
+ // Let other modules alter the renderable array.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'entity' => $entity,
+ 'view_mode' => $view_mode,
+ 'display' => $view_mode,
+ 'language' => $langcode,
+ );
+ drupal_alter('field_attach_view', $output, $context);
+
+ // Reset the _field_view_prepared flag set in field_attach_prepare_view(),
+ // in case the same entity is displayed with different settings later in
+ // the request.
+ unset($entity->_field_view_prepared);
+
+ return $output;
+}
+
+/**
+ * Populate the template variables with the field values available for rendering.
+ *
+ * The $variables array will be populated with all the field instance values
+ * associated with the given entity type, keyed by field name; in case of
+ * translatable fields the language currently chosen for display will be
+ * selected.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity with fields to render.
+ * @param $element
+ * The structured array containing the values ready for rendering.
+ * @param $variables
+ * The variables array is passed by reference and will be populated with field
+ * values.
+ */
+function field_attach_preprocess($entity_type, $entity, $element, &$variables) {
+ list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $field_name = $instance['field_name'];
+ if (isset($element[$field_name]['#language'])) {
+ $langcode = $element[$field_name]['#language'];
+ $variables[$field_name] = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : NULL;
+ }
+ }
+
+ // Let other modules make changes to the $variables array.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'entity' => $entity,
+ 'element' => $element,
+ );
+ drupal_alter('field_attach_preprocess', $variables, $context);
+}
+
+/**
+ * Prepares an entity for translation.
+ *
+ * This function is used to fill-in the form default values for Field API fields
+ * while performing entity translation. By default it copies all the source
+ * values in the given source language to the new entity and assigns them the
+ * target language.
+ *
+ * This is used as part of the 'per entity' translation pattern, which is
+ * implemented only for nodes by translation.module. Other entity types may be
+ * supported through contributed modules.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity to be prepared for translation.
+ * @param $langcode
+ * The language the entity has to be translated in.
+ * @param $source_entity
+ * The source entity holding the field values to be translated.
+ * @param $source_langcode
+ * The source language from which translate.
+ */
+function field_attach_prepare_translation($entity_type, $entity, $langcode, $source_entity, $source_langcode) {
+ $options = array('language' => $langcode);
+ // Copy source field values into the entity to be prepared.
+ _field_invoke_default('prepare_translation', $entity_type, $entity, $source_entity, $source_langcode, $options);
+ // Let field types handle their own advanced translation pattern if needed.
+ _field_invoke('prepare_translation', $entity_type, $entity, $source_entity, $source_langcode, $options);
+ // Let other modules alter the entity translation.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'langcode' => $langcode,
+ 'source_entity' => $source_entity,
+ 'source_langcode' => $source_langcode,
+ );
+ drupal_alter('field_attach_prepare_translation', $entity, $context);
+}
+
+/**
+ * Notify field.module that a new bundle was created.
+ *
+ * The default SQL-based storage doesn't need to do anything about it, but
+ * others might.
+ *
+ * @param $entity_type
+ * The entity type to which the bundle is bound.
+ * @param $bundle
+ * The name of the newly created bundle.
+ */
+function field_attach_create_bundle($entity_type, $bundle) {
+ // Clear the cache.
+ field_cache_clear();
+
+ // Let other modules act on creating the bundle.
+ module_invoke_all('field_attach_create_bundle', $entity_type, $bundle);
+}
+
+/**
+ * Notify field.module that a bundle was renamed.
+ *
+ * @param $entity_type
+ * The entity type to which the bundle is bound.
+ * @param $bundle_old
+ * The previous name of the bundle.
+ * @param $bundle_new
+ * The new name of the bundle.
+ */
+function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+ db_update('field_config_instance')
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle_old)
+ ->execute();
+
+ // Clear the cache.
+ field_cache_clear();
+
+ // Update bundle settings.
+ $settings = variable_get('field_bundle_settings_' . $entity_type . '__' . $bundle_old, array());
+ variable_set('field_bundle_settings_' . $entity_type . '__' . $bundle_new, $settings);
+ variable_del('field_bundle_settings_' . $entity_type . '__' . $bundle_old);
+
+ // Let other modules act on renaming the bundle.
+ module_invoke_all('field_attach_rename_bundle', $entity_type, $bundle_old, $bundle_new);
+}
+
+/**
+ * Notify field.module the a bundle was deleted.
+ *
+ * This deletes the data for the field instances as well as the field instances
+ * themselves. This function actually just marks the data and field instances
+ * and deleted, leaving the garbage collection for a separate process, because
+ * it is not always possible to delete this much data in a single page request
+ * (particularly since for some field types, the deletion is more than just a
+ * simple DELETE query).
+ *
+ * @param $entity_type
+ * The entity type to which the bundle is bound.
+ * @param $bundle
+ * The bundle to delete.
+ */
+function field_attach_delete_bundle($entity_type, $bundle) {
+ // First, delete the instances themselves. field_read_instances() must be
+ // used here since field_info_instances() does not return instances for
+ // disabled entity types or bundles.
+ $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle), array('include_inactive' => 1));
+ foreach ($instances as $instance) {
+ field_delete_instance($instance);
+ }
+
+ // Clear the cache.
+ field_cache_clear();
+
+ // Clear bundle display settings.
+ variable_del('field_bundle_settings_' . $entity_type . '__' . $bundle);
+
+ // Let other modules act on deleting the bundle.
+ module_invoke_all('field_attach_delete_bundle', $entity_type, $bundle, $instances);
+}
+
+
+/**
+ * @} End of "defgroup field_attach".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.crud.inc b/kolab.org/www/drupal-7.26/modules/field/field.crud.inc
new file mode 100644
index 0000000..83863d6
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.crud.inc
@@ -0,0 +1,1010 @@
+<?php
+
+/**
+ * @file
+ * Field CRUD API, handling field and field instance creation and deletion.
+ */
+
+/**
+ * @defgroup field_crud Field CRUD API
+ * @{
+ * Create, update, and delete Field API fields, bundles, and instances.
+ *
+ * Modules use this API, often in hook_install(), to create custom
+ * data structures. UI modules will use it to create a user interface.
+ *
+ * The Field CRUD API uses
+ * @link field Field API data structures @endlink.
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Creates a field.
+ *
+ * This function does not bind the field to any bundle; use
+ * field_create_instance() for that.
+ *
+ * @param $field
+ * A field definition array. The field_name and type properties are required.
+ * Other properties, if omitted, will be given the following default values:
+ * - cardinality: 1
+ * - locked: FALSE
+ * - indexes: the field-type indexes, specified by the field type's
+ * hook_field_schema(). The indexes specified in $field are added
+ * to those default indexes. It is possible to override the
+ * definition of a field-type index by providing an index with the
+ * same name, or to remove it by redefining it as an empty array
+ * of columns. Overriding field-type indexes should be done
+ * carefully, for it might seriously affect the site's performance.
+ * - settings: each omitted setting is given the default value defined in
+ * hook_field_info().
+ * - storage:
+ * - type: the storage backend specified in the 'field_storage_default'
+ * system variable.
+ * - settings: each omitted setting is given the default value specified in
+ * hook_field_storage_info().
+ *
+ * @return
+ * The $field array with the id property filled in.
+ *
+ * @throws FieldException
+ *
+ * See: @link field Field API data structures @endlink.
+ */
+function field_create_field($field) {
+ // Field name is required.
+ if (empty($field['field_name'])) {
+ throw new FieldException('Attempt to create an unnamed field.');
+ }
+ // Field type is required.
+ if (empty($field['type'])) {
+ throw new FieldException('Attempt to create a field with no type.');
+ }
+ // 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');
+ }
+
+ // Field name cannot be longer than 32 characters. We use drupal_strlen()
+ // because the DB layer assumes that column widths are given in characters,
+ // not bytes.
+ if (drupal_strlen($field['field_name']) > 32) {
+ throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
+ array('%name' => $field['field_name'])));
+ }
+
+ // Ensure the field name is unique over active and disabled fields.
+ // We do not care about deleted fields.
+ $prior_field = field_read_field($field['field_name'], array('include_inactive' => TRUE));
+ if (!empty($prior_field)) {
+ $message = $prior_field['active']?
+ t('Attempt to create field name %name which already exists and is active.', array('%name' => $field['field_name'])):
+ t('Attempt to create field name %name which already exists, although it is inactive.', array('%name' => $field['field_name']));
+ throw new FieldException($message);
+ }
+
+ // Disallow reserved field names. This can't prevent all field name
+ // collisions with existing entity properties, but some is better
+ // than none.
+ foreach (entity_get_info() as $type => $info) {
+ if (in_array($field['field_name'], $info['entity keys'])) {
+ throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
+ }
+ }
+
+ $field += array(
+ 'entity_types' => array(),
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ 'locked' => FALSE,
+ 'settings' => array(),
+ 'storage' => array(),
+ 'deleted' => 0,
+ );
+
+ // Check that the field type is known.
+ $field_type = field_info_field_types($field['type']);
+ if (!$field_type) {
+ throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
+ }
+ // Create all per-field-type properties (needed here as long as we have
+ // settings that impact column definitions).
+ $field['settings'] += field_info_field_settings($field['type']);
+ $field['module'] = $field_type['module'];
+ $field['active'] = 1;
+
+ // Provide default storage.
+ $field['storage'] += array(
+ 'type' => variable_get('field_storage_default', 'field_sql_storage'),
+ 'settings' => array(),
+ );
+ // Check that the storage type is known.
+ $storage_type = field_info_storage_types($field['storage']['type']);
+ if (!$storage_type) {
+ throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
+ }
+ // Provide default storage settings.
+ $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
+ $field['storage']['module'] = $storage_type['module'];
+ $field['storage']['active'] = 1;
+ // Collect storage information.
+ module_load_install($field['module']);
+ $schema = (array) module_invoke($field['module'], 'field_schema', $field);
+ $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(
+ 'indexes' => array(),
+ );
+ $field['indexes'] += $schema['indexes'];
+
+ // The serialized 'data' column contains everything from $field that does not
+ // have its own column and is not automatically populated when the field is
+ // read.
+ $data = $field;
+ unset($data['columns'], $data['field_name'], $data['type'], $data['active'], $data['module'], $data['storage_type'], $data['storage_active'], $data['storage_module'], $data['locked'], $data['cardinality'], $data['deleted']);
+ // Additionally, do not save the 'bundles' property populated by
+ // field_info_field().
+ unset($data['bundles']);
+
+ $record = array(
+ 'field_name' => $field['field_name'],
+ 'type' => $field['type'],
+ 'module' => $field['module'],
+ 'active' => $field['active'],
+ 'storage_type' => $field['storage']['type'],
+ 'storage_module' => $field['storage']['module'],
+ 'storage_active' => $field['storage']['active'],
+ 'locked' => $field['locked'],
+ 'data' => $data,
+ 'cardinality' => $field['cardinality'],
+ 'translatable' => $field['translatable'],
+ 'deleted' => $field['deleted'],
+ );
+
+ // Store the field and get the id back.
+ drupal_write_record('field_config', $record);
+ $field['id'] = $record['id'];
+
+ // Invoke hook_field_storage_create_field after the field is
+ // complete (e.g. it has its id).
+ try {
+ // Invoke hook_field_storage_create_field after
+ // drupal_write_record() sets the field id.
+ module_invoke($storage_type['module'], 'field_storage_create_field', $field);
+ }
+ catch (Exception $e) {
+ // If storage creation failed, remove the field_config record before
+ // rethrowing the exception.
+ db_delete('field_config')
+ ->condition('id', $field['id'])
+ ->execute();
+ throw $e;
+ }
+
+ // Clear caches
+ field_cache_clear(TRUE);
+
+ // Invoke external hooks after the cache is cleared for API consistency.
+ module_invoke_all('field_create_field', $field);
+
+ return $field;
+}
+
+/**
+ * Updates a field.
+ *
+ * Any module may forbid any update for any reason. For example, the
+ * field's storage module might forbid an update if it would change
+ * the storage schema while data for the field exists. A field type
+ * module might forbid an update if it would change existing data's
+ * semantics, or if there are external dependencies on field settings
+ * that cannot be updated.
+ *
+ * @param $field
+ * A field structure. $field['field_name'] must provided; it
+ * identifies the field that will be updated to match this
+ * structure. Any other properties of the field that are not
+ * specified in $field will be left unchanged, so it is not
+ * necessary to pass in a fully populated $field structure.
+ * @return
+ * Throws a FieldException if the update cannot be performed.
+ * @see field_create_field()
+ */
+function field_update_field($field) {
+ // Check that the specified field exists.
+ $prior_field = field_read_field($field['field_name']);
+ if (empty($prior_field)) {
+ throw new FieldException('Attempt to update a non-existent field.');
+ }
+
+ // Use the prior field values for anything not specifically set by the new
+ // field to be sure that all values are set.
+ $field += $prior_field;
+ $field['settings'] += $prior_field['settings'];
+
+ // Some updates are always disallowed.
+ if ($field['type'] != $prior_field['type']) {
+ throw new FieldException("Cannot change an existing field's type.");
+ }
+ if ($field['entity_types'] != $prior_field['entity_types']) {
+ throw new FieldException("Cannot change an existing field's entity_types property.");
+ }
+ if ($field['storage']['type'] != $prior_field['storage']['type']) {
+ throw new FieldException("Cannot change an existing field's storage type.");
+ }
+
+ // Collect the new storage information, since what is in
+ // $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(), '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(
+ 'indexes' => array(),
+ );
+ $field['indexes'] += $schema['indexes'];
+
+ $has_data = field_has_data($field);
+
+ // See if any module forbids the update by throwing an exception.
+ foreach (module_implements('field_update_forbid') as $module) {
+ $function = $module . '_field_update_forbid';
+ $function($field, $prior_field, $has_data);
+ }
+
+ // Tell the storage engine to update the field. Do this before
+ // saving the new definition since it still might fail.
+ $storage_type = field_info_storage_types($field['storage']['type']);
+ module_invoke($storage_type['module'], 'field_storage_update_field', $field, $prior_field, $has_data);
+
+ // Save the new field definition. @todo: refactor with
+ // field_create_field.
+
+ // The serialized 'data' column contains everything from $field that does not
+ // have its own column and is not automatically populated when the field is
+ // read.
+ $data = $field;
+ unset($data['columns'], $data['field_name'], $data['type'], $data['locked'], $data['module'], $data['cardinality'], $data['active'], $data['deleted']);
+ // Additionally, do not save the 'bundles' property populated by
+ // field_info_field().
+ unset($data['bundles']);
+
+ $field['data'] = $data;
+
+ // Store the field and create the id.
+ $primary_key = array('id');
+ drupal_write_record('field_config', $field, $primary_key);
+
+ // Clear caches
+ field_cache_clear(TRUE);
+
+ // Invoke external hooks after the cache is cleared for API consistency.
+ module_invoke_all('field_update_field', $field, $prior_field, $has_data);
+}
+
+/**
+ * Reads a single field record directly from the database.
+ *
+ * Generally, you should use the field_info_field() instead.
+ *
+ * This function will not return deleted fields. Use
+ * field_read_fields() instead for this purpose.
+ *
+ * @param $field_name
+ * The field name to read.
+ * @param array $include_additional
+ * The default behavior of this function is to not return a field that
+ * is inactive. Setting
+ * $include_additional['include_inactive'] to TRUE will override this
+ * behavior.
+ * @return
+ * A field definition array, or FALSE.
+ */
+function field_read_field($field_name, $include_additional = array()) {
+ $fields = field_read_fields(array('field_name' => $field_name), $include_additional);
+ return $fields ? current($fields) : FALSE;
+}
+
+/**
+ * Reads in fields that match an array of conditions.
+ *
+ * @param array $params
+ * 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
+ * $include_additional['include_inactive'] or
+ * $include_additional['include_deleted'] to TRUE will override this
+ * behavior.
+ * @return
+ * An array of fields matching $params. If
+ * $include_additional['include_deleted'] is TRUE, the array is keyed
+ * by field id, otherwise it is keyed by field name.
+ */
+function field_read_fields($params = array(), $include_additional = array()) {
+ $query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC));
+ $query->fields('fc');
+
+ // 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)
+ ->condition('fc.storage_active', 1);
+ }
+ $include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']);
+ if (!$include_deleted) {
+ $query->condition('fc.deleted', 0);
+ }
+
+ $fields = array();
+ $results = $query->execute();
+ foreach ($results as $record) {
+ $field = unserialize($record['data']);
+ $field['id'] = $record['id'];
+ $field['field_name'] = $record['field_name'];
+ $field['type'] = $record['type'];
+ $field['module'] = $record['module'];
+ $field['active'] = $record['active'];
+ $field['storage']['type'] = $record['storage_type'];
+ $field['storage']['module'] = $record['storage_module'];
+ $field['storage']['active'] = $record['storage_active'];
+ $field['locked'] = $record['locked'];
+ $field['cardinality'] = $record['cardinality'];
+ $field['translatable'] = $record['translatable'];
+ $field['deleted'] = $record['deleted'];
+
+ module_invoke_all('field_read_field', $field);
+
+ // Populate storage information.
+ module_load_install($field['module']);
+ $schema = (array) module_invoke($field['module'], 'field_schema', $field);
+ $schema += array('columns' => array(), 'indexes' => array());
+ $field['columns'] = $schema['columns'];
+
+ $field_name = $field['field_name'];
+ if ($include_deleted) {
+ $field_name = $field['id'];
+ }
+ $fields[$field_name] = $field;
+ }
+ return $fields;
+}
+
+/**
+ * Marks a field and its instances and data for deletion.
+ *
+ * @param $field_name
+ * The field name to delete.
+ */
+function field_delete_field($field_name) {
+ // Delete all non-deleted instances.
+ $field = field_info_field($field_name);
+ if (isset($field['bundles'])) {
+ foreach ($field['bundles'] as $entity_type => $bundles) {
+ foreach ($bundles as $bundle) {
+ $instance = field_info_instance($entity_type, $field_name, $bundle);
+ field_delete_instance($instance, FALSE);
+ }
+ }
+ }
+
+ // Mark field data for deletion.
+ module_invoke($field['storage']['module'], 'field_storage_delete_field', $field);
+
+ // Mark the field for deletion.
+ db_update('field_config')
+ ->fields(array('deleted' => 1))
+ ->condition('field_name', $field_name)
+ ->execute();
+
+ // Clear the cache.
+ field_cache_clear(TRUE);
+
+ module_invoke_all('field_delete_field', $field);
+}
+
+/**
+ * Creates an instance of a field, binding it to a bundle.
+ *
+ * @param $instance
+ * A field instance definition array. The field_name, entity_type and
+ * bundle properties are required. Other properties, if omitted,
+ * will be given the following default values:
+ * - label: the field name
+ * - description: empty string
+ * - required: FALSE
+ * - default_value_function: empty string
+ * - settings: each omitted setting is given the default value specified in
+ * hook_field_info().
+ * - widget:
+ * - type: the default widget specified in hook_field_info().
+ * - settings: each omitted setting is given the default value specified in
+ * hook_field_widget_info().
+ * - display:
+ * Settings for the 'default' view mode will be added if not present, and
+ * each view mode in the definition will be completed with the following
+ * default values:
+ * - label: 'above'
+ * - type: the default formatter specified in hook_field_info().
+ * - settings: each omitted setting is given the default value specified in
+ * hook_field_formatter_info().
+ * View modes not present in the definition are left empty, and the field
+ * will not be displayed in this mode.
+ *
+ * @return
+ * The $instance array with the id property filled in.
+ *
+ * @throws FieldException
+ *
+ * See: @link field Field API data structures @endlink.
+ */
+function field_create_instance($instance) {
+ $field = field_read_field($instance['field_name']);
+ if (empty($field)) {
+ throw new FieldException(t("Attempt to create an instance of a field @field_name that doesn't exist or is currently inactive.", array('@field_name' => $instance['field_name'])));
+ }
+ // Check that the required properties exists.
+ if (empty($instance['entity_type'])) {
+ throw new FieldException(t('Attempt to create an instance of field @field_name without an entity type.', array('@field_name' => $instance['field_name'])));
+ }
+ if (empty($instance['bundle'])) {
+ throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
+ }
+ // Check that the field can be attached to this entity type.
+ if (!empty($field['entity_types']) && !in_array($instance['entity_type'], $field['entity_types'])) {
+ throw new FieldException(t('Attempt to create an instance of field @field_name on forbidden entity type @entity_type.', array('@field_name' => $instance['field_name'], '@entity_type' => $instance['entity_type'])));
+ }
+
+ // Set the field id.
+ $instance['field_id'] = $field['id'];
+
+ // Note that we do *not* prevent creating a field on non-existing bundles,
+ // because that would break the 'Body as field' upgrade for contrib
+ // node types.
+
+ // TODO: Check that the widget type is known and can handle the field type ?
+ // TODO: Check that the formatters are known and can handle the field type ?
+ // TODO: Check that the display view modes are known for the entity type ?
+ // Those checks should probably happen in _field_write_instance() ?
+ // Problem : this would mean that a UI module cannot update an instance with a disabled formatter.
+
+ // Ensure the field instance is unique within the bundle.
+ // We only check for instances of active fields, since adding an instance of
+ // a disabled field is not supported.
+ $prior_instance = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
+ if (!empty($prior_instance)) {
+ $message = t('Attempt to create an instance of field @field_name on bundle @bundle that already has an instance of that field.', array('@field_name' => $instance['field_name'], '@bundle' => $instance['bundle']));
+ throw new FieldException($message);
+ }
+
+ _field_write_instance($instance);
+
+ // Clear caches
+ field_cache_clear();
+
+ // Invoke external hooks after the cache is cleared for API consistency.
+ module_invoke_all('field_create_instance', $instance);
+
+ return $instance;
+}
+
+/**
+ * Updates an instance of a field.
+ *
+ * @param $instance
+ * 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.
+ * 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['definition']['required'] = TRUE;
+ * // Write the changed definition back.
+ * field_update_instance($instance_info['definition']);
+ * @endcode
+ *
+ * @throws FieldException
+ *
+ * @see field_info_instance()
+ * @see field_create_instance()
+ */
+function field_update_instance($instance) {
+ // Check that the specified field exists.
+ $field = field_read_field($instance['field_name']);
+ if (empty($field)) {
+ throw new FieldException(t('Attempt to update an instance of a nonexistent field @field.', array('@field' => $instance['field_name'])));
+ }
+
+ // Check that the field instance exists (even if it is inactive, since we
+ // want to be able to replace inactive widgets with new ones).
+ $prior_instance = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle'], array('include_inactive' => TRUE));
+ if (empty($prior_instance)) {
+ throw new FieldException(t("Attempt to update an instance of field @field on bundle @bundle that doesn't exist.", array('@field' => $instance['field_name'], '@bundle' => $instance['bundle'])));
+ }
+
+ $instance['id'] = $prior_instance['id'];
+ $instance['field_id'] = $prior_instance['field_id'];
+
+ _field_write_instance($instance, TRUE);
+
+ // Clear caches.
+ field_cache_clear();
+
+ module_invoke_all('field_update_instance', $instance, $prior_instance);
+}
+
+/**
+ * Stores an instance record in the field configuration database.
+ *
+ * @param $instance
+ * An instance structure.
+ * @param $update
+ * Whether this is a new or existing instance.
+ */
+function _field_write_instance($instance, $update = FALSE) {
+ $field = field_read_field($instance['field_name']);
+ $field_type = field_info_field_types($field['type']);
+
+ // Set defaults.
+ $instance += array(
+ 'settings' => array(),
+ 'display' => array(),
+ 'widget' => array(),
+ 'required' => FALSE,
+ 'label' => $instance['field_name'],
+ 'description' => '',
+ 'deleted' => 0,
+ );
+
+ // Set default instance settings.
+ $instance['settings'] += field_info_instance_settings($field['type']);
+
+ // Set default widget and settings.
+ $instance['widget'] += array(
+ // TODO: what if no 'default_widget' specified ?
+ 'type' => $field_type['default_widget'],
+ 'settings' => array(),
+ );
+ // If no weight specified, make sure the field sinks at the bottom.
+ if (!isset($instance['widget']['weight'])) {
+ $max_weight = field_info_max_weight($instance['entity_type'], $instance['bundle'], 'form');
+ $instance['widget']['weight'] = isset($max_weight) ? $max_weight + 1 : 0;
+ }
+ // Check widget module.
+ $widget_type = field_info_widget_types($instance['widget']['type']);
+ $instance['widget']['module'] = $widget_type['module'];
+ $instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']);
+
+ // Make sure there are at least display settings for the 'default' view mode,
+ // and fill in defaults for each view mode specified in the definition.
+ $instance['display'] += array(
+ 'default' => array(),
+ );
+ foreach ($instance['display'] as $view_mode => $display) {
+ $display += array(
+ 'label' => 'above',
+ 'type' => isset($field_type['default_formatter']) ? $field_type['default_formatter'] : 'hidden',
+ 'settings' => array(),
+ );
+ if ($display['type'] != 'hidden') {
+ $formatter_type = field_info_formatter_types($display['type']);
+ $display['module'] = $formatter_type['module'];
+ $display['settings'] += field_info_formatter_settings($display['type']);
+ }
+ // If no weight specified, make sure the field sinks at the bottom.
+ if (!isset($display['weight'])) {
+ $max_weight = field_info_max_weight($instance['entity_type'], $instance['bundle'], $view_mode);
+ $display['weight'] = isset($max_weight) ? $max_weight + 1 : 0;
+ }
+ $instance['display'][$view_mode] = $display;
+ }
+
+ // The serialized 'data' column contains everything from $instance that does
+ // not have its own column and is not automatically populated when the
+ // instance is read.
+ $data = $instance;
+ unset($data['id'], $data['field_id'], $data['field_name'], $data['entity_type'], $data['bundle'], $data['deleted']);
+
+ $record = array(
+ 'field_id' => $instance['field_id'],
+ 'field_name' => $instance['field_name'],
+ 'entity_type' => $instance['entity_type'],
+ 'bundle' => $instance['bundle'],
+ 'data' => $data,
+ 'deleted' => $instance['deleted'],
+ );
+ // We need to tell drupal_update_record() the primary keys to trigger an
+ // update.
+ if ($update) {
+ $record['id'] = $instance['id'];
+ $primary_key = array('id');
+ }
+ else {
+ $primary_key = array();
+ }
+ drupal_write_record('field_config_instance', $record, $primary_key);
+}
+
+/**
+ * Reads a single instance record from the database.
+ *
+ * Generally, you should use field_info_instance() instead, as it
+ * provides caching and allows other modules the opportunity to
+ * append additional formatters, widgets, and other information.
+ *
+ * @param $entity_type
+ * The type of entity to which the field is bound.
+ * @param $field_name
+ * The field name to read.
+ * @param $bundle
+ * The bundle to which the field is bound.
+ * @param array $include_additional
+ * The default behavior of this function is to not return an instance that
+ * has been deleted, or whose field is inactive. Setting
+ * $include_additional['include_inactive'] or
+ * $include_additional['include_deleted'] to TRUE will override this
+ * behavior.
+ * @return
+ * An instance structure, or FALSE.
+ */
+function field_read_instance($entity_type, $field_name, $bundle, $include_additional = array()) {
+ $instances = field_read_instances(array('entity_type' => $entity_type, 'field_name' => $field_name, 'bundle' => $bundle), $include_additional);
+ return $instances ? current($instances) : FALSE;
+}
+
+/**
+ * Reads in field instances that match an array of conditions.
+ *
+ * @param $param
+ * An array of properties to use in selecting a field
+ * instance. Valid keys include any column of the
+ * field_config_instance table. If NULL, all instances will be returned.
+ * @param $include_additional
+ * The default behavior of this function is to not return field
+ * instances that have been marked deleted, or whose field is inactive.
+ * Setting $include_additional['include_inactive'] or
+ * $include_additional['include_deleted'] to TRUE will override this
+ * behavior.
+ * @return
+ * An array of instances matching the arguments.
+ */
+function field_read_instances($params = array(), $include_additional = array()) {
+ $include_inactive = isset($include_additional['include_inactive']) && $include_additional['include_inactive'];
+ $include_deleted = isset($include_additional['include_deleted']) && $include_additional['include_deleted'];
+
+ $query = db_select('field_config_instance', 'fci', array('fetch' => PDO::FETCH_ASSOC));
+ $query->join('field_config', 'fc', 'fc.id = fci.field_id');
+ $query->fields('fci');
+
+ // Turn the conditions into a query.
+ foreach ($params as $key => $value) {
+ $query->condition('fci.' . $key, $value);
+ }
+ if (!$include_inactive) {
+ $query
+ ->condition('fc.active', 1)
+ ->condition('fc.storage_active', 1);
+ }
+ if (!$include_deleted) {
+ $query->condition('fc.deleted', 0);
+ $query->condition('fci.deleted', 0);
+ }
+
+ $instances = array();
+ $results = $query->execute();
+
+ foreach ($results as $record) {
+ // Filter out instances on unknown entity types (for instance because the
+ // module exposing them was disabled).
+ $entity_info = entity_get_info($record['entity_type']);
+ if ($include_inactive || $entity_info) {
+ $instance = unserialize($record['data']);
+ $instance['id'] = $record['id'];
+ $instance['field_id'] = $record['field_id'];
+ $instance['field_name'] = $record['field_name'];
+ $instance['entity_type'] = $record['entity_type'];
+ $instance['bundle'] = $record['bundle'];
+ $instance['deleted'] = $record['deleted'];
+
+ module_invoke_all('field_read_instance', $instance);
+ $instances[] = $instance;
+ }
+ }
+ return $instances;
+}
+
+/**
+ * Marks a field instance and its data for deletion.
+ *
+ * @param $instance
+ * An instance structure.
+ * @param $field_cleanup
+ * If TRUE, the field will be deleted as well if its last instance is being
+ * deleted. If FALSE, it is the caller's responsibility to handle the case of
+ * fields left without instances. Defaults to TRUE.
+ */
+function field_delete_instance($instance, $field_cleanup = TRUE) {
+ // Mark the field instance for deletion.
+ db_update('field_config_instance')
+ ->fields(array('deleted' => 1))
+ ->condition('field_name', $instance['field_name'])
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+
+ // Clear the cache.
+ field_cache_clear();
+
+ // Mark instance data for deletion.
+ $field = field_info_field($instance['field_name']);
+ module_invoke($field['storage']['module'], 'field_storage_delete_instance', $instance);
+
+ // Let modules react to the deletion of the instance.
+ module_invoke_all('field_delete_instance', $instance);
+
+ // Delete the field itself if we just deleted its last instance.
+ if ($field_cleanup && count($field['bundles']) == 0) {
+ field_delete_field($field['field_name']);
+ }
+}
+
+/**
+ * @} End of "defgroup field_crud".
+ */
+
+/**
+ * @defgroup field_purge Field API bulk data deletion
+ * @{
+ * Clean up after Field API bulk deletion operations.
+ *
+ * Field API provides functions for deleting data attached to individual
+ * entities as well as deleting entire fields or field instances in a single
+ * operation.
+ *
+ * Deleting field data items for an entity with field_attach_delete() involves
+ * three separate operations:
+ * - Invoking the Field Type API hook_field_delete() for each field on the
+ * entity. The hook for each field type receives the entity and the specific
+ * field being deleted. A file field module might use this hook to delete
+ * uploaded files from the filesystem.
+ * - Invoking the Field Storage API hook_field_storage_delete() to remove
+ * data from the primary field storage. The hook implementation receives the
+ * entity being deleted and deletes data for all of the entity's bundle's
+ * fields.
+ * - Invoking the global Field Attach API hook_field_attach_delete() for all
+ * modules that implement it. Each hook implementation receives the entity
+ * being deleted and can operate on whichever subset of the entity's bundle's
+ * fields it chooses to.
+ *
+ * These hooks are invoked immediately when field_attach_delete() is
+ * called. Similar operations are performed for field_attach_delete_revision().
+ *
+ * When a field, bundle, or field instance is deleted, it is not practical to
+ * invoke these hooks immediately on every affected entity in a single page
+ * request; there could be thousands or millions of them. Instead, the
+ * appropriate field data items, instances, and/or fields are marked as deleted
+ * so that subsequent load or query operations will not return them. Later, a
+ * separate process cleans up, or "purges", the marked-as-deleted data by going
+ * through the three-step process described above and, finally, removing
+ * deleted field and instance records.
+ *
+ * Purging field data is made somewhat tricky by the fact that, while
+ * field_attach_delete() has a complete entity to pass to the various deletion
+ * hooks, the Field API purge process only has the field data it has previously
+ * stored. It cannot reconstruct complete original entities to pass to the
+ * deletion hooks. It is even possible that the original entity to which some
+ * Field API data was attached has been itself deleted before the field purge
+ * operation takes place.
+ *
+ * Field API resolves this problem by using "pseudo-entities" during purge
+ * operations. A pseudo-entity contains only the information from the original
+ * entity that Field API knows about: entity type, id, revision id, and
+ * bundle. It also contains the field data for whichever field instance is
+ * currently being purged. For example, suppose that the node type 'story' used
+ * to contain a field called 'subtitle' but the field was deleted. If node 37
+ * was a story with a subtitle, the pseudo-entity passed to the purge hooks
+ * would look something like this:
+ *
+ * @code
+ * $entity = stdClass Object(
+ * [nid] => 37,
+ * [vid] => 37,
+ * [type] => 'story',
+ * [subtitle] => array(
+ * [0] => array(
+ * 'value' => 'subtitle text',
+ * ),
+ * ),
+ * );
+ * @endcode
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Purges a batch of deleted Field API data, instances, or fields.
+ *
+ * This function will purge deleted field data in batches. The batch size
+ * is defined as an argument to the function, and once each batch is finished,
+ * it continues with the next batch until all have completed. If a deleted field
+ * instance with no remaining data records is found, the instance itself will
+ * be purged. If a deleted field with no remaining field instances is found, the
+ * field itself will be purged.
+ *
+ * @param $batch_size
+ * The maximum number of field data records to purge before returning.
+ */
+function field_purge_batch($batch_size) {
+ // Retrieve all deleted field instances. We cannot use field_info_instances()
+ // because that function does not return deleted instances.
+ $instances = field_read_instances(array('deleted' => 1), array('include_deleted' => 1));
+
+ foreach ($instances as $instance) {
+ // field_purge_data() will need the field array.
+ $field = field_info_field_by_id($instance['field_id']);
+ // Retrieve some entities.
+ $query = new EntityFieldQuery();
+ $results = $query
+ ->fieldCondition($field)
+ ->entityCondition('bundle', $instance['bundle'])
+ ->deleted(TRUE)
+ ->range(0, $batch_size)
+ ->execute();
+
+ if ($results) {
+ foreach ($results as $entity_type => $stub_entities) {
+ field_attach_load($entity_type, $stub_entities, FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1));
+ foreach ($stub_entities as $stub_entity) {
+ // Purge the data for the entity.
+ field_purge_data($entity_type, $stub_entity, $field, $instance);
+ }
+ }
+ }
+ else {
+ // No field data remains for the instance, so we can remove it.
+ field_purge_instance($instance);
+ }
+ }
+
+ // Retrieve all deleted fields. Any that have no instances can be purged.
+ $fields = field_read_fields(array('deleted' => 1), array('include_deleted' => 1));
+ foreach ($fields as $field) {
+ $instances = field_read_instances(array('field_id' => $field['id']), array('include_deleted' => 1));
+ if (empty($instances)) {
+ field_purge_field($field);
+ }
+ }
+}
+
+/**
+ * Purges the field data for a single field on a single pseudo-entity.
+ *
+ * This is basically the same as field_attach_delete() except it only applies
+ * to a single field. The entity itself is not being deleted, and it is quite
+ * possible that other field data will remain attached to it.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The pseudo-entity whose field data is being purged.
+ * @param $field
+ * The (possibly deleted) field whose data is being purged.
+ * @param $instance
+ * The deleted field instance whose data is being purged.
+ */
+function field_purge_data($entity_type, $entity, $field, $instance) {
+ // Each field type's hook_field_delete() only expects to operate on a single
+ // field at a time, so we can use it as-is for purging.
+ $options = array('field_id' => $instance['field_id'], 'deleted' => TRUE);
+ _field_invoke('delete', $entity_type, $entity, $dummy, $dummy, $options);
+
+ // Tell the field storage system to purge the data.
+ module_invoke($field['storage']['module'], 'field_storage_purge', $entity_type, $entity, $field, $instance);
+
+ // Let other modules act on purging the data.
+ foreach (module_implements('field_attach_purge') as $module) {
+ $function = $module . '_field_attach_purge';
+ $function($entity_type, $entity, $field, $instance);
+ }
+}
+
+/**
+ * Purges a field instance record from the database.
+ *
+ * This function assumes all data for the instance has already been purged, and
+ * should only be called by field_purge_batch().
+ *
+ * @param $instance
+ * The instance record to purge.
+ */
+function field_purge_instance($instance) {
+ db_delete('field_config_instance')
+ ->condition('id', $instance['id'])
+ ->execute();
+
+ // Notify the storage engine.
+ $field = field_info_field_by_id($instance['field_id']);
+ module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
+
+ // Clear the cache.
+ field_info_cache_clear();
+
+ // Invoke external hooks after the cache is cleared for API consistency.
+ module_invoke_all('field_purge_instance', $instance);
+}
+
+/**
+ * Purges a field record from the database.
+ *
+ * This function assumes all instances for the field has already been purged,
+ * and should only be called by field_purge_batch().
+ *
+ * @param $field
+ * The field record to purge.
+ */
+function field_purge_field($field) {
+ $instances = field_read_instances(array('field_id' => $field['id']), array('include_deleted' => 1));
+ if (count($instances) > 0) {
+ throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
+ }
+
+ db_delete('field_config')
+ ->condition('id', $field['id'])
+ ->execute();
+
+ // Notify the storage engine.
+ module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
+
+ // Clear the cache.
+ field_info_cache_clear();
+
+ // Invoke external hooks after the cache is cleared for API consistency.
+ module_invoke_all('field_purge_field', $field);
+}
+
+/**
+ * @} End of "defgroup field_purge".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.default.inc b/kolab.org/www/drupal-7.26/modules/field/field.default.inc
new file mode 100644
index 0000000..cb49bdb
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.default.inc
@@ -0,0 +1,268 @@
+<?php
+
+/**
+ * @file
+ * Default 'implementations' of hook_field_*(): common field housekeeping.
+ *
+ * Those implementations are special, as field.module does not define any field
+ * types. Those functions take care of default stuff common to all field types.
+ * They are called through the _field_invoke_default() iterator, generally in
+ * the corresponding field_attach_[operation]() function.
+ */
+
+/**
+ * Extracts field values from submitted form values.
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * The field values. This parameter is altered by reference to receive the
+ * incoming form values.
+ * @param $form
+ * The form structure where field elements are attached to. This might be a
+ * full form structure, or a sub-element of a larger form.
+ * @param $form_state
+ * The form state.
+ */
+function field_default_extract_form_values($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
+ $path = array_merge($form['#parents'], array($field['field_name'], $langcode));
+ $key_exists = NULL;
+ $values = drupal_array_get_nested_value($form_state['values'], $path, $key_exists);
+ if ($key_exists) {
+ // Remove the 'value' of the 'add more' button.
+ unset($values['add_more']);
+ $items = $values;
+ }
+}
+
+/**
+ * Generic field validation handler.
+ *
+ * Possible error codes:
+ * - 'field_cardinality': The number of values exceeds the field cardinality.
+ *
+ * @see _hook_field_validate()
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ * @param $errors
+ * The array of errors, keyed by field name and by value 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 field_default_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ // Filter out empty values.
+ $items = _field_filter_items($field, $items);
+
+ // Check that the number of values doesn't exceed the field cardinality.
+ // For form submitted values, this can only happen with 'multiple value'
+ // widgets.
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && count($items) > $field['cardinality']) {
+ $errors[$field['field_name']][$langcode][0][] = array(
+ 'error' => 'field_cardinality',
+ 'message' => t('%name: this field cannot hold more than @count values.', array('%name' => $instance['label'], '@count' => $field['cardinality'])),
+ );
+ }
+}
+
+function field_default_submit($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
+ // Filter out empty values.
+ $items = _field_filter_items($field, $items);
+ // Reorder items to account for drag-n-drop reordering.
+ $items = _field_sort_items($field, $items);
+}
+
+/**
+ * Default field 'insert' operation.
+ *
+ * Insert default value if no $entity->$field_name entry was provided.
+ * This can happen with programmatic saves, or on form-based creation where
+ * the current user doesn't have 'edit' permission for the field.
+ */
+function field_default_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ // _field_invoke() populates $items with an empty array if the $entity has no
+ // entry for the field, so we check on the $entity itself.
+ // We also check that the current field translation is actually defined before
+ // assigning it a default value. This way we ensure that only the intended
+ // languages get a default value. Otherwise we could have default values for
+ // not yet open languages.
+ if (empty($entity) || !property_exists($entity, $field['field_name']) ||
+ (isset($entity->{$field['field_name']}[$langcode]) && count($entity->{$field['field_name']}[$langcode]) == 0)) {
+ $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
+ }
+}
+
+/**
+ * Invokes hook_field_formatter_prepare_view() on the relevant formatters.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entities
+ * An array of entities being displayed, keyed by entity id.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instances
+ * Array of instance structures for $field for each entity, keyed by entity
+ * id.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * Array of field values already loaded for the entities, keyed by entity id.
+ * @param $display
+ * Can be either:
+ * - the name of a view mode
+ * - or an array of display settings to use for display, as found in the
+ * 'display' entry of $instance definitions.
+ */
+function field_default_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $display) {
+ // Group entities, instances and items by formatter module.
+ $modules = array();
+ foreach ($instances as $id => $instance) {
+ if (is_string($display)) {
+ $view_mode = $display;
+ $instance_display = field_get_display($instance, $view_mode, $entities[$id]);
+ }
+ else {
+ $instance_display = $display;
+ }
+
+ if ($instance_display['type'] !== 'hidden') {
+ $module = $instance_display['module'];
+ $modules[$module] = $module;
+ $grouped_entities[$module][$id] = $entities[$id];
+ $grouped_instances[$module][$id] = $instance;
+ $grouped_displays[$module][$id] = $instance_display;
+ // hook_field_formatter_prepare_view() alters $items by reference.
+ $grouped_items[$module][$id] = &$items[$id];
+ }
+ }
+
+ foreach ($modules as $module) {
+ // Invoke hook_field_formatter_prepare_view().
+ $function = $module . '_field_formatter_prepare_view';
+ if (function_exists($function)) {
+ $function($entity_type, $grouped_entities[$module], $field, $grouped_instances[$module], $langcode, $grouped_items[$module], $grouped_displays[$module]);
+ }
+ }
+}
+
+/**
+ * Builds a renderable array for one field on one entity instance.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * A single object of type $entity_type.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * An array containing each field on $entity's bundle.
+ * @param $langcode
+ * The language associated to $items.
+ * @param $items
+ * Array of field values already loaded for the entities, keyed by entity id.
+ * @param $display
+ * Can be either:
+ * - the name of a view mode;
+ * - or an array of custom display settings, as found in the 'display' entry
+ * of $instance definitions.
+ */
+function field_default_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ $addition = array();
+
+ // Prepare incoming display specifications.
+ if (is_string($display)) {
+ $view_mode = $display;
+ $display = field_get_display($instance, $view_mode, $entity);
+ }
+ else {
+ $view_mode = '_custom_display';
+ }
+
+ if ($display['type'] !== 'hidden') {
+ // Calling the formatter function through module_invoke() can have a
+ // performance impact on pages with many fields and values.
+ $function = $display['module'] . '_field_formatter_view';
+ if (function_exists($function)) {
+ $elements = $function($entity_type, $entity, $field, $instance, $langcode, $items, $display);
+
+ if ($elements) {
+ $info = array(
+ '#theme' => 'field',
+ '#weight' => $display['weight'],
+ '#title' => $instance['label'],
+ '#access' => field_access('view', $field, $entity_type, $entity),
+ '#label_display' => $display['label'],
+ '#view_mode' => $view_mode,
+ '#language' => $langcode,
+ '#field_name' => $field['field_name'],
+ '#field_type' => $field['type'],
+ '#field_translatable' => $field['translatable'],
+ '#entity_type' => $entity_type,
+ '#bundle' => $bundle,
+ '#object' => $entity,
+ '#items' => $items,
+ '#formatter' => $display['type']
+ );
+
+ $addition[$field['field_name']] = array_merge($info, $elements);
+ }
+ }
+ }
+
+ return $addition;
+}
+
+/**
+ * Copies source field values into the entity to be prepared.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity to be prepared for translation.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instance
+ * The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ * The language the entity has to be translated in.
+ * @param $items
+ * $entity->{$field['field_name']}[$langcode], or an empty array if unset.
+ * @param $source_entity
+ * The source entity holding the field values to be translated.
+ * @param $source_langcode
+ * The source language from which translate.
+ */
+function field_default_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
+ $field_name = $field['field_name'];
+ // If the field is untranslatable keep using LANGUAGE_NONE.
+ if ($langcode == LANGUAGE_NONE) {
+ $source_langcode = LANGUAGE_NONE;
+ }
+ if (isset($source_entity->{$field_name}[$source_langcode])) {
+ $items = $source_entity->{$field_name}[$source_langcode];
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.form.inc b/kolab.org/www/drupal-7.26/modules/field/field.form.inc
new file mode 100644
index 0000000..280f778
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.form.inc
@@ -0,0 +1,604 @@
+<?php
+
+/**
+ * @file
+ * Field forms management.
+ */
+
+/**
+ * Creates a form element for a field and can populate it with a default value.
+ *
+ * If the form element is not associated with an entity (i.e., $entity is NULL)
+ * field_get_default_value will be called to supply the default value for the
+ * field. Also allows other modules to alter the form element by implementing
+ * their own hooks.
+ *
+ * @param $entity_type
+ * The type of entity (for example 'node' or 'user') that the field belongs
+ * to.
+ * @param $entity
+ * The entity object that the field belongs to. This may be NULL if creating a
+ * form element with a default value.
+ * @param $field
+ * An array representing the field whose editing element is being created.
+ * @param $instance
+ * An array representing the structure for $field in its current context.
+ * @param $langcode
+ * The language associated with the field.
+ * @param $items
+ * An array of the field values. When creating a new entity this may be NULL
+ * or an empty array to use default values.
+ * @param $form
+ * An array representing the form that the editing element will be attached
+ * to.
+ * @param $form_state
+ * An array containing the current state of the form.
+ * @param $get_delta
+ * Used to get only a specific delta value of a multiple value field.
+ *
+ * @return
+ * The form element array created for this field.
+ */
+function field_default_form($entity_type, $entity, $field, $instance, $langcode, $items, &$form, &$form_state, $get_delta = NULL) {
+ // This could be called with no entity, as when a UI module creates a
+ // dummy form to set default values.
+ if ($entity) {
+ list($id, , ) = entity_extract_ids($entity_type, $entity);
+ }
+
+ $parents = $form['#parents'];
+
+ $addition = array();
+ $field_name = $field['field_name'];
+ $addition[$field_name] = array();
+
+ // Populate widgets with default values when creating a new entity.
+ if (empty($items) && empty($id)) {
+ $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
+ }
+
+ // Let modules alter the widget properties.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'entity' => $entity,
+ 'field' => $field,
+ 'instance' => $instance,
+ );
+ drupal_alter(array('field_widget_properties', 'field_widget_properties_' . $entity_type), $instance['widget'], $context);
+
+ // Collect widget elements.
+ $elements = array();
+
+ // Store field information in $form_state.
+ if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) {
+ $field_state = array(
+ 'field' => $field,
+ 'instance' => $instance,
+ 'items_count' => count($items),
+ 'array_parents' => array(),
+ 'errors' => array(),
+ );
+ field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+ }
+
+ // If field module handles multiple values for this form element, and we are
+ // displaying an individual element, process the multiple value form.
+ if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+ // Store the entity in the form.
+ $form['#entity'] = $entity;
+ $elements = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state);
+ }
+ // If the widget is handling multiple values (e.g Options), or if we are
+ // displaying an individual element, just get a single form element and make
+ // it the $delta value.
+ else {
+ $delta = isset($get_delta) ? $get_delta : 0;
+ $function = $instance['widget']['module'] . '_field_widget_form';
+ if (function_exists($function)) {
+ $element = array(
+ '#entity' => $entity,
+ '#entity_type' => $instance['entity_type'],
+ '#bundle' => $instance['bundle'],
+ '#field_name' => $field_name,
+ '#language' => $langcode,
+ '#field_parents' => $parents,
+ '#columns' => array_keys($field['columns']),
+ '#title' => check_plain($instance['label']),
+ '#description' => field_filter_xss($instance['description']),
+ // Only the first widget should be required.
+ '#required' => $delta == 0 && $instance['required'],
+ '#delta' => $delta,
+ );
+ if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
+ // Allow modules to alter the field widget form element.
+ $context = array(
+ 'form' => $form,
+ 'field' => $field,
+ 'instance' => $instance,
+ 'langcode' => $langcode,
+ 'items' => $items,
+ 'delta' => $delta,
+ );
+ drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context);
+
+ // If we're processing a specific delta value for a field where the
+ // field module handles multiples, set the delta in the result.
+ // For fields that handle their own processing, we can't make
+ // assumptions about how the field is structured, just merge in the
+ // returned element.
+ if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+ $elements[$delta] = $element;
+ }
+ else {
+ $elements = $element;
+ }
+ }
+ }
+ }
+
+ // Also aid in theming of field widgets by rendering a classified container.
+ $addition[$field_name] = array(
+ '#type' => 'container',
+ '#attributes' => array(
+ 'class' => array(
+ 'field-type-' . drupal_html_class($field['type']),
+ 'field-name-' . drupal_html_class($field_name),
+ 'field-widget-' . drupal_html_class($instance['widget']['type']),
+ ),
+ ),
+ '#weight' => $instance['widget']['weight'],
+ );
+
+ // Populate the 'array_parents' information in $form_state['field'] after
+ // the form is built, so that we catch changes in the form structure performed
+ // in alter() hooks.
+ $elements['#after_build'][] = 'field_form_element_after_build';
+ $elements['#field_name'] = $field_name;
+ $elements['#language'] = $langcode;
+ $elements['#field_parents'] = $parents;
+
+ $addition[$field_name] += array(
+ '#tree' => TRUE,
+ // The '#language' key can be used to access the field's form element
+ // when $langcode is unknown.
+ '#language' => $langcode,
+ $langcode => $elements,
+ '#access' => field_access('edit', $field, $entity_type, $entity),
+ );
+
+ return $addition;
+}
+
+/**
+ * Special handling to create form elements for multiple values.
+ *
+ * Handles generic features for multiple fields:
+ * - number of widgets
+ * - AHAH-'add more' button
+ * - drag-n-drop value reordering
+ */
+function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) {
+ $field_name = $field['field_name'];
+ $parents = $form['#parents'];
+
+ // Determine the number of widgets to display.
+ switch ($field['cardinality']) {
+ case FIELD_CARDINALITY_UNLIMITED:
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+ $max = $field_state['items_count'];
+ break;
+
+ default:
+ $max = $field['cardinality'] - 1;
+ break;
+ }
+
+ $title = check_plain($instance['label']);
+ $description = field_filter_xss($instance['description']);
+
+ $id_prefix = implode('-', array_merge($parents, array($field_name)));
+ $wrapper_id = drupal_html_id($id_prefix . '-add-more-wrapper');
+
+ $field_elements = array();
+
+ $function = $instance['widget']['module'] . '_field_widget_form';
+ if (function_exists($function)) {
+ for ($delta = 0; $delta <= $max; $delta++) {
+ $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
+ $element = array(
+ '#entity_type' => $instance['entity_type'],
+ '#entity' => $form['#entity'],
+ '#bundle' => $instance['bundle'],
+ '#field_name' => $field_name,
+ '#language' => $langcode,
+ '#field_parents' => $parents,
+ '#columns' => array_keys($field['columns']),
+ // For multiple fields, title and description are handled by the wrapping table.
+ '#title' => $multiple ? '' : $title,
+ '#description' => $multiple ? '' : $description,
+ // Only the first widget should be required.
+ '#required' => $delta == 0 && $instance['required'],
+ '#delta' => $delta,
+ '#weight' => $delta,
+ );
+ if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
+ // Input field for the delta (drag-n-drop reordering).
+ if ($multiple) {
+ // We name the element '_weight' to avoid clashing with elements
+ // defined by widget.
+ $element['_weight'] = array(
+ '#type' => 'weight',
+ '#title' => t('Weight for row @number', array('@number' => $delta + 1)),
+ '#title_display' => 'invisible',
+ // Note: this 'delta' is the FAPI 'weight' element's property.
+ '#delta' => $max,
+ '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
+ '#weight' => 100,
+ );
+ }
+
+ // Allow modules to alter the field widget form element.
+ $context = array(
+ 'form' => $form,
+ 'field' => $field,
+ 'instance' => $instance,
+ 'langcode' => $langcode,
+ 'items' => $items,
+ 'delta' => $delta,
+ );
+ drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context);
+
+ $field_elements[$delta] = $element;
+ }
+ }
+
+ if ($field_elements) {
+ $field_elements += array(
+ '#theme' => 'field_multiple_value_form',
+ '#field_name' => $field['field_name'],
+ '#cardinality' => $field['cardinality'],
+ '#title' => $title,
+ '#required' => $instance['required'],
+ '#description' => $description,
+ '#prefix' => '<div id="' . $wrapper_id . '">',
+ '#suffix' => '</div>',
+ '#max_delta' => $max,
+ );
+ // Add 'add more' button, if not working with a programmed form.
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
+ $field_elements['add_more'] = array(
+ '#type' => 'submit',
+ '#name' => strtr($id_prefix, '-', '_') . '_add_more',
+ '#value' => t('Add another item'),
+ '#attributes' => array('class' => array('field-add-more-submit')),
+ '#limit_validation_errors' => array(array_merge($parents, array($field_name, $langcode))),
+ '#submit' => array('field_add_more_submit'),
+ '#ajax' => array(
+ 'callback' => 'field_add_more_js',
+ 'wrapper' => $wrapper_id,
+ 'effect' => 'fade',
+ ),
+ );
+ }
+ }
+ }
+
+ return $field_elements;
+}
+
+/**
+ * Returns HTML for an individual form element.
+ *
+ * Combine multiple values into a table with drag-n-drop reordering.
+ * TODO : convert to a template.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - element: A render element representing the form element.
+ *
+ * @ingroup themeable
+ */
+function theme_field_multiple_value_form($variables) {
+ $element = $variables['element'];
+ $output = '';
+
+ if ($element['#cardinality'] > 1 || $element['#cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+ $table_id = drupal_html_id($element['#field_name'] . '_values');
+ $order_class = $element['#field_name'] . '-delta-order';
+ $required = !empty($element['#required']) ? theme('form_required_marker', $variables) : '';
+
+ $header = array(
+ array(
+ 'data' => '<label>' . t('!title !required', array('!title' => $element['#title'], '!required' => $required)) . "</label>",
+ 'colspan' => 2,
+ 'class' => array('field-label'),
+ ),
+ t('Order'),
+ );
+ $rows = array();
+
+ // Sort items according to '_weight' (needed when the form comes back after
+ // preview or failed validation)
+ $items = array();
+ foreach (element_children($element) as $key) {
+ if ($key === 'add_more') {
+ $add_more_button = &$element[$key];
+ }
+ else {
+ $items[] = &$element[$key];
+ }
+ }
+ usort($items, '_field_sort_items_value_helper');
+
+ // Add the items as table rows.
+ foreach ($items as $key => $item) {
+ $item['_weight']['#attributes']['class'] = array($order_class);
+ $delta_element = drupal_render($item['_weight']);
+ $cells = array(
+ array('data' => '', 'class' => array('field-multiple-drag')),
+ drupal_render($item),
+ array('data' => $delta_element, 'class' => array('delta-order')),
+ );
+ $rows[] = array(
+ 'data' => $cells,
+ 'class' => array('draggable'),
+ );
+ }
+
+ $output = '<div class="form-item">';
+ $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id, 'class' => array('field-multiple-table'))));
+ $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
+ $output .= '<div class="clearfix">' . drupal_render($add_more_button) . '</div>';
+ $output .= '</div>';
+
+ drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
+ }
+ else {
+ foreach (element_children($element) as $key) {
+ $output .= drupal_render($element[$key]);
+ }
+ }
+
+ return $output;
+}
+
+/**
+ * #after_build callback for field elements in a form.
+ *
+ * This stores the final location of the field within the form structure so
+ * that field_default_form_errors() can assign validation errors to the right
+ * form element.
+ *
+ * @see field_default_form_errors()
+ */
+function field_form_element_after_build($element, &$form_state) {
+ $parents = $element['#field_parents'];
+ $field_name = $element['#field_name'];
+ $langcode = $element['#language'];
+
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+ $field_state['array_parents'] = $element['#array_parents'];
+ field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+
+ return $element;
+}
+
+/**
+ * Transfer field-level validation errors to widgets.
+ */
+function field_default_form_errors($entity_type, $entity, $field, $instance, $langcode, $items, $form, &$form_state) {
+ $field_state = field_form_get_state($form['#parents'], $field['field_name'], $langcode, $form_state);
+
+ if (!empty($field_state['errors'])) {
+ // Locate the correct element in the form.
+ $element = drupal_array_get_nested_value($form_state['complete form'], $field_state['array_parents']);
+ // Only set errors if the element is accessible.
+ if (!isset($element['#access']) || $element['#access']) {
+ $function = $instance['widget']['module'] . '_field_widget_error';
+ $function_exists = function_exists($function);
+
+ $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT;
+ foreach ($field_state['errors'] as $delta => $delta_errors) {
+ // For multiple single-value widgets, pass errors by delta.
+ // For a multiple-value widget, pass all errors to the main widget.
+ $error_element = $multiple_widget ? $element : $element[$delta];
+ foreach ($delta_errors as $error) {
+ if ($function_exists) {
+ $function($error_element, $error, $form, $form_state);
+ }
+ else {
+ // Make sure that errors are reported (even incorrectly flagged) if
+ // the widget module fails to implement hook_field_widget_error().
+ form_error($error_element, $error['message']);
+ }
+ }
+ }
+ // Reinitialize the errors list for the next submit.
+ $field_state['errors'] = array();
+ field_form_set_state($form['#parents'], $field['field_name'], $langcode, $form_state, $field_state);
+ }
+ }
+}
+
+/**
+ * Submit handler for the "Add another item" button of a field form.
+ *
+ * This handler is run regardless of whether JS is enabled or not. It makes
+ * changes to the form state. If the button was clicked with JS disabled, then
+ * the page is reloaded with the complete rebuilt form. If the button was
+ * clicked with JS enabled, then ajax_form_callback() calls field_add_more_js()
+ * to return just the changed part of the form.
+ */
+function field_add_more_submit($form, &$form_state) {
+ $button = $form_state['triggering_element'];
+
+ // Go one level up in the form, to the widgets container.
+ $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+ $field_name = $element['#field_name'];
+ $langcode = $element['#language'];
+ $parents = $element['#field_parents'];
+
+ // Increment the items count.
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+ $field_state['items_count']++;
+ field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Ajax callback in response to a new empty widget being added to the form.
+ *
+ * This returns the new page content to replace the page content made obsolete
+ * by the form submission.
+ *
+ * @see field_add_more_submit()
+ */
+function field_add_more_js($form, $form_state) {
+ $button = $form_state['triggering_element'];
+
+ // Go one level up in the form, to the widgets container.
+ $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+ $field_name = $element['#field_name'];
+ $langcode = $element['#language'];
+ $parents = $element['#field_parents'];
+
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+
+ $field = $field_state['field'];
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
+ return;
+ }
+
+ // Add a DIV around the delta receiving the Ajax effect.
+ $delta = $element['#max_delta'];
+ $element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
+ $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
+
+ return $element;
+}
+
+/**
+ * Retrieves processing information about a field from $form_state.
+ *
+ * @param $parents
+ * The array of #parents where the field lives in the form.
+ * @param $field_name
+ * The field name.
+ * @param $langcode
+ * The language in which the field values are entered.
+ * @param $form_state
+ * The form state.
+ *
+ * @return
+ * An array with the following key/data pairs:
+ * - field: the field definition array,
+ * - instance: the field instance definition array,
+ * - items_count: the number of widgets to display for the field,
+ * - array_parents: the location of the field's widgets within the $form
+ * structure. This entry is populated at '#after_build' time.
+ * - errors: the array of field validation errors reported on the field. This
+ * entry is populated at field_attach_form_validate() time.
+ *
+ * @see field_form_set_state()
+ */
+function field_form_get_state($parents, $field_name, $langcode, &$form_state) {
+ $form_state_parents = _field_form_state_parents($parents, $field_name, $langcode);
+ return drupal_array_get_nested_value($form_state, $form_state_parents);
+}
+
+/**
+ * Stores processing information about a field in $form_state.
+ *
+ * @param $parents
+ * The array of #parents where the field lives in the form.
+ * @param $field_name
+ * The field name.
+ * @param $langcode
+ * The language in which the field values are entered.
+ * @param $form_state
+ * The form state.
+ * @param $field_state
+ * The array of data to store. See field_form_get_state() for the structure
+ * and content of the array.
+ *
+ * @see field_form_get_state()
+ */
+function field_form_set_state($parents, $field_name, $langcode, &$form_state, $field_state) {
+ $form_state_parents = _field_form_state_parents($parents, $field_name, $langcode);
+ drupal_array_set_nested_value($form_state, $form_state_parents, $field_state);
+}
+
+/**
+ * Returns the location of processing information within $form_state.
+ */
+function _field_form_state_parents($parents, $field_name, $langcode) {
+ // To ensure backwards compatibility on regular entity forms for widgets that
+ // still access $form_state['field'][$field_name] directly,
+ // - top-level fields (empty $parents) are placed directly under
+ // $form_state['fields'][$field_name].
+ // - Other fields are placed under
+ // $form_state['field']['#parents'][...$parents...]['#fields'][$field_name]
+ // to avoid clashes between field names and $parents parts.
+ // @todo Remove backwards compatibility in Drupal 8, and use a unique
+ // $form_state['field'][...$parents...]['#fields'][$field_name] structure.
+ if (!empty($parents)) {
+ $form_state_parents = array_merge(array('#parents'), $parents, array('#fields'));
+ }
+ else {
+ $form_state_parents = array();
+ }
+ $form_state_parents = array_merge(array('field'), $form_state_parents, array($field_name, $langcode));
+
+ return $form_state_parents;
+}
+
+/**
+ * Retrieves the field definition for a widget's helper callbacks.
+ *
+ * Widgets helper element callbacks (such as #process, #element_validate,
+ * #value_callback, ...) should use field_widget_field() and
+ * field_widget_instance() instead of field_info_field() and
+ * field_info_instance() when they need to access field or instance properties.
+ * See hook_field_widget_form() for more details.
+ *
+ * @param $element
+ * The structured array for the widget.
+ * @param $form_state
+ * The form state.
+ *
+ * @return
+ * The $field definition array for the current widget.
+ *
+ * @see field_widget_instance()
+ * @see hook_field_widget_form()
+ */
+function field_widget_field($element, $form_state) {
+ $field_state = field_form_get_state($element['#field_parents'], $element['#field_name'], $element['#language'], $form_state);
+ return $field_state['field'];
+}
+
+/**
+ * Retrieves the instance definition array for a widget's helper callbacks.
+ *
+ * Widgets helper element callbacks (such as #process, #element_validate,
+ * #value_callback, ...) should use field_widget_field() and
+ * field_widget_instance() instead of field_info_field() and
+ * field_info_instance() when they need to access field or instance properties.
+ * See hook_field_widget_form() for more details.
+ *
+ * @param $element
+ * The structured array for the widget.
+ * @param $form_state
+ * The form state.
+ *
+ * @return
+ * The $instance definition array for the current widget.
+ *
+ * @see field_widget_field()
+ * @see hook_field_widget_form()
+ */
+function field_widget_instance($element, $form_state) {
+ $field_state = field_form_get_state($element['#field_parents'], $element['#field_name'], $element['#language'], $form_state);
+ return $field_state['instance'];
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.info b/kolab.org/www/drupal-7.26/modules/field/field.info
new file mode 100644
index 0000000..8b6db5e
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.info
@@ -0,0 +1,18 @@
+name = Field
+description = Field API to add fields to entities like nodes and users.
+package = Core
+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 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.info.class.inc b/kolab.org/www/drupal-7.26/modules/field/field.info.class.inc
new file mode 100644
index 0000000..3b89898
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.info.class.inc
@@ -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;
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.info.inc b/kolab.org/www/drupal-7.26/modules/field/field.info.inc
new file mode 100644
index 0000000..02b3c9c
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.info.inc
@@ -0,0 +1,812 @@
+<?php
+
+/**
+ * @file
+ * 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
+ * @{
+ * Obtain information about Field API configuration.
+ *
+ * The Field Info API exposes information about field types, fields,
+ * instances, bundles, widget types, display formatters, behaviors,
+ * and settings defined by or with the Field API.
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Clears the field info cache without clearing the field data cache.
+ *
+ * This is useful when deleted fields or instances are purged. We
+ * need to remove the purged records, but no actual field data items
+ * are affected.
+ */
+function field_info_cache_clear() {
+ drupal_static_reset('field_view_mode_settings');
+ drupal_static_reset('field_available_languages');
+
+ // @todo: Remove this when field_attach_*_bundle() bundle management
+ // functions are moved to the entity API.
+ entity_info_cache_clear();
+
+ _field_info_collate_types(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,
+ );
+}
+
+/**
+ * Collates all information on field types, widget types and related structures.
+ *
+ * @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:
+ * - 'field types': Array of hook_field_info() results, keyed by field_type.
+ * Each element has the following components: label, description, settings,
+ * instance_settings, default_widget, default_formatter, and behaviors
+ * from hook_field_info(), as well as module, giving the module that exposes
+ * the field type.
+ * - 'widget types': Array of hook_field_widget_info() results, keyed by
+ * widget_type. Each element has the following components: label, field
+ * types, settings, weight, and behaviors from hook_field_widget_info(),
+ * as well as module, giving the module that exposes the widget type.
+ * - 'formatter types': Array of hook_field_formatter_info() results, keyed by
+ * formatter_type. Each element has the following components: label, field
+ * types, and behaviors from hook_field_formatter_info(), as well as
+ * module, giving the module that exposes the formatter type.
+ * - 'storage types': Array of hook_field_storage_info() results, keyed by
+ * storage type names. Each element has the following components: label,
+ * description, and settings from hook_field_storage_info(), as well as
+ * module, giving the module that exposes the storage type.
+ * - 'fieldable types': Array of hook_entity_info() results, keyed by
+ * entity_type. Each element has the following components: name, id key,
+ * revision key, bundle key, cacheable, and bundles from hook_entity_info(),
+ * as well as module, giving the module that exposes the entity type.
+ */
+function _field_info_collate_types($reset = FALSE) {
+ global $language;
+ static $info;
+
+ // The _info() hooks invoked below include translated strings, so each
+ // language is cached separately.
+ $langcode = $language->language;
+
+ if ($reset) {
+ $info = NULL;
+ // Clear all languages.
+ cache_clear_all('field_info_types:', 'cache_field', TRUE);
+ return;
+ }
+
+ if (!isset($info)) {
+ if ($cached = cache_get("field_info_types:$langcode", 'cache_field')) {
+ $info = $cached->data;
+ }
+ else {
+ $info = array(
+ 'field types' => array(),
+ 'widget types' => array(),
+ 'formatter types' => array(),
+ 'storage types' => array(),
+ );
+
+ // Populate field types.
+ foreach (module_implements('field_info') as $module) {
+ $field_types = (array) module_invoke($module, 'field_info');
+ foreach ($field_types as $name => $field_info) {
+ // Provide defaults.
+ $field_info += array(
+ 'settings' => array(),
+ 'instance_settings' => array(),
+ );
+ $info['field types'][$name] = $field_info;
+ $info['field types'][$name]['module'] = $module;
+ }
+ }
+ drupal_alter('field_info', $info['field types']);
+
+ // Populate widget types.
+ foreach (module_implements('field_widget_info') as $module) {
+ $widget_types = (array) module_invoke($module, 'field_widget_info');
+ foreach ($widget_types as $name => $widget_info) {
+ // Provide defaults.
+ $widget_info += array(
+ 'settings' => array(),
+ );
+ $info['widget types'][$name] = $widget_info;
+ $info['widget types'][$name]['module'] = $module;
+ }
+ }
+ drupal_alter('field_widget_info', $info['widget types']);
+ uasort($info['widget types'], 'drupal_sort_weight');
+
+ // Populate formatter types.
+ foreach (module_implements('field_formatter_info') as $module) {
+ $formatter_types = (array) module_invoke($module, 'field_formatter_info');
+ foreach ($formatter_types as $name => $formatter_info) {
+ // Provide defaults.
+ $formatter_info += array(
+ 'settings' => array(),
+ );
+ $info['formatter types'][$name] = $formatter_info;
+ $info['formatter types'][$name]['module'] = $module;
+ }
+ }
+ drupal_alter('field_formatter_info', $info['formatter types']);
+
+ // Populate storage types.
+ foreach (module_implements('field_storage_info') as $module) {
+ $storage_types = (array) module_invoke($module, 'field_storage_info');
+ foreach ($storage_types as $name => $storage_info) {
+ // Provide defaults.
+ $storage_info += array(
+ 'settings' => array(),
+ );
+ $info['storage types'][$name] = $storage_info;
+ $info['storage types'][$name]['module'] = $module;
+ }
+ }
+ drupal_alter('field_storage_info', $info['storage types']);
+
+ cache_set("field_info_types:$langcode", $info, 'cache_field');
+ }
+ }
+
+ return $info;
+}
+
+/**
+ * Prepares a field definition for the current run-time context.
+ *
+ * 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::prepareField()
+ */
+function _field_info_prepare_field($field) {
+ $cache = _field_info_field_cache();
+ return $cache->prepareField($field);
+}
+
+/**
+ * Prepares an instance definition for the current run-time context.
+ *
+ * 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::prepareInstance()
+ */
+function _field_info_prepare_instance($instance, $field) {
+ $cache = _field_info_field_cache();
+ return $cache->prepareInstance($instance, $field['type']);
+}
+
+/**
+ * Adapts display specifications to the current run-time context.
+ *
+ * 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) {
+ $cache = _field_info_field_cache();
+ return $cache->prepareInstanceDisplay($display, $field['type']);
+}
+
+/**
+ * Prepares widget specifications for the current run-time context.
+ *
+ * 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) {
+ $cache = _field_info_field_cache();
+ return $cache->prepareInstanceWidget($widget, $field['type']);
+}
+
+/**
+ * Prepares 'extra fields' for the current run-time context.
+ *
+ * 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) {
+ $cache = _field_info_field_cache();
+ return $cache->prepareExtraFields($extra_fields, $entity_type, $bundle);
+}
+
+/**
+ * Determines the behavior of a widget with respect to an operation.
+ *
+ * @param $op
+ * The name of the operation. Currently supported: 'default value',
+ * 'multiple values'.
+ * @param $instance
+ * The field instance array.
+ *
+ * @return
+ * One of these values:
+ * - FIELD_BEHAVIOR_NONE: Do nothing for this operation.
+ * - FIELD_BEHAVIOR_CUSTOM: Use the widget's callback function.
+ * - FIELD_BEHAVIOR_DEFAULT: Use field.module default behavior.
+ */
+function field_behaviors_widget($op, $instance) {
+ $info = field_info_widget_types($instance['widget']['type']);
+ return isset($info['behaviors'][$op]) ? $info['behaviors'][$op] : FIELD_BEHAVIOR_DEFAULT;
+}
+
+/**
+ * Returns information about field types from hook_field_info().
+ *
+ * @param $field_type
+ * (optional) A field type name. If omitted, all field types will be
+ * returned.
+ *
+ * @return
+ * Either a field type description, as provided by hook_field_info(), or an
+ * array of all existing field types, keyed by field type name.
+ */
+function field_info_field_types($field_type = NULL) {
+ $info = _field_info_collate_types();
+ $field_types = $info['field types'];
+ if ($field_type) {
+ if (isset($field_types[$field_type])) {
+ return $field_types[$field_type];
+ }
+ }
+ else {
+ return $field_types;
+ }
+}
+
+/**
+ * Returns information about field widgets from hook_field_widget_info().
+ *
+ * @param $widget_type
+ * (optional) A widget type name. If omitted, all widget types will be
+ * returned.
+ *
+ * @return
+ * Either a single widget type description, as provided by
+ * hook_field_widget_info(), or an array of all existing widget types, keyed
+ * by widget type name.
+ */
+function field_info_widget_types($widget_type = NULL) {
+ $info = _field_info_collate_types();
+ $widget_types = $info['widget types'];
+ if ($widget_type) {
+ if (isset($widget_types[$widget_type])) {
+ return $widget_types[$widget_type];
+ }
+ }
+ else {
+ return $widget_types;
+ }
+}
+
+/**
+ * Returns information about field formatters from hook_field_formatter_info().
+ *
+ * @param $formatter_type
+ * (optional) A formatter type name. If omitted, all formatter types will be
+ * returned.
+ *
+ * @return
+ * Either a single formatter type description, as provided by
+ * hook_field_formatter_info(), or an array of all existing formatter types,
+ * keyed by formatter type name.
+ */
+function field_info_formatter_types($formatter_type = NULL) {
+ $info = _field_info_collate_types();
+ $formatter_types = $info['formatter types'];
+ if ($formatter_type) {
+ if (isset($formatter_types[$formatter_type])) {
+ return $formatter_types[$formatter_type];
+ }
+ }
+ else {
+ return $formatter_types;
+ }
+}
+
+/**
+ * Returns information about field storage from hook_field_storage_info().
+ *
+ * @param $storage_type
+ * (optional) A storage type name. If omitted, all storage types will be
+ * returned.
+ *
+ * @return
+ * Either a storage type description, as provided by
+ * hook_field_storage_info(), or an array of all existing storage types,
+ * keyed by storage type name.
+ */
+function field_info_storage_types($storage_type = NULL) {
+ $info = _field_info_collate_types();
+ $storage_types = $info['storage types'];
+ if ($storage_type) {
+ if (isset($storage_types[$storage_type])) {
+ return $storage_types[$storage_type];
+ }
+ }
+ else {
+ return $storage_types;
+ }
+}
+
+/**
+ * Returns information about existing bundles.
+ *
+ * @param $entity_type
+ * The type of entity; e.g. 'node' or 'user'.
+ *
+ * @return
+ * An array of bundles for the $entity_type keyed by bundle name,
+ * or, if no $entity_type was provided, the array of all existing bundles,
+ * keyed by entity type.
+ */
+function field_info_bundles($entity_type = NULL) {
+ $info = entity_get_info();
+
+ if ($entity_type) {
+ return isset($info[$entity_type]['bundles']) ? $info[$entity_type]['bundles'] : array();
+ }
+
+ $bundles = array();
+ foreach ($info as $type => $entity_info) {
+ $bundles[$type] = $entity_info['bundles'];
+ }
+ 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();
+ foreach ($info as $key => $field) {
+ if (!$field['deleted']) {
+ $fields[$field['field_name']] = $field;
+ }
+ }
+
+ return $fields;
+}
+
+/**
+ * Returns data about an individual field, given a field name.
+ *
+ * @param $field_name
+ * The name of the field to retrieve. $field_name can only refer to a
+ * non-deleted, active field. For deleted fields, use
+ * field_info_field_by_id(). To retrieve information about inactive fields,
+ * use field_read_fields().
+ *
+ * @return
+ * The field array, as returned by field_read_fields(), with an
+ * additional element 'bundles', whose value is an array of all the bundles
+ * this field belongs to keyed by entity type. NULL if the field was not
+ * found.
+ *
+ * @see field_info_field_by_id()
+ */
+function field_info_field($field_name) {
+ $cache = _field_info_field_cache();
+ return $cache->getField($field_name);
+}
+
+/**
+ * Returns data about an individual field, given a field ID.
+ *
+ * @param $field_id
+ * The id of the field to retrieve. $field_id can refer to a
+ * deleted field, but not an inactive one.
+ *
+ * @return
+ * The field array, as returned by field_read_fields(), with an
+ * additional element 'bundles', whose value is an array of all the bundles
+ * this field belongs to.
+ *
+ * @see field_info_field()
+ */
+function field_info_field_by_id($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.
+ *
+ * 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
+ * returned by field_read_fields(), with an additional element 'bundles',
+ * whose value is an array of all the bundle this field belongs to.
+ *
+ * @see field_info_field()
+ * @see field_info_field_by_id()
+ */
+function field_info_field_by_ids() {
+ $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
+ * (optional) The entity type for which to return instances.
+ * @param $bundle_name
+ * (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) {
+ $cache = _field_info_field_cache();
+
+ if (!isset($entity_type)) {
+ return $cache->getInstances();
+ }
+ 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
+ * The field name for the instance.
+ * @param $bundle_name
+ * The bundle name for the instance.
+ *
+ * @return
+ * An associative array of instance data for the specific field and bundle;
+ * NULL if the instance does not exist.
+ */
+function field_info_instance($entity_type, $field_name, $bundle_name) {
+ $cache = _field_info_field_cache();
+ $info = $cache->getBundleInstances($entity_type, $bundle_name);
+ if (isset($info[$field_name])) {
+ return $info[$field_name];
+ }
+}
+
+/**
+ * Returns a list and settings of pseudo-field elements in a given bundle.
+ *
+ * If $context is 'form', an array with the following structure:
+ * @code
+ * array(
+ * 'name_of_pseudo_field_component' => array(
+ * 'label' => The human readable name of the component,
+ * 'description' => A short description of the component content,
+ * 'weight' => The weight of the component in edit forms,
+ * ),
+ * 'name_of_other_pseudo_field_component' => array(
+ * // ...
+ * ),
+ * );
+ * @endcode
+ *
+ * If $context is 'display', an array with the following structure:
+ * @code
+ * array(
+ * 'name_of_pseudo_field_component' => array(
+ * 'label' => The human readable name of the component,
+ * 'description' => A short description of the component content,
+ * // One entry per view mode, including the 'default' mode:
+ * 'display' => array(
+ * 'default' => array(
+ * 'weight' => The weight of the component in displayed entities in
+ * this view mode,
+ * 'visible' => TRUE if the component is visible, FALSE if hidden, in
+ * displayed entities in this view mode,
+ * ),
+ * 'teaser' => array(
+ * // ...
+ * ),
+ * ),
+ * ),
+ * 'name_of_other_pseudo_field_component' => array(
+ * // ...
+ * ),
+ * );
+ * @endcode
+ *
+ * @param $entity_type
+ * The type of entity; e.g. 'node' or 'user'.
+ * @param $bundle
+ * The bundle name.
+ * @param $context
+ * The context for which the list of pseudo-fields is requested. Either
+ * 'form' or 'display'.
+ *
+ * @return
+ * The array of pseudo-field elements in the bundle.
+ */
+function field_info_extra_fields($entity_type, $bundle, $context) {
+ $cache = _field_info_field_cache();
+ $info = $cache->getBundleExtraFields($entity_type, $bundle);
+
+ return isset($info[$context]) ? $info[$context] : array();
+}
+
+/**
+ * Returns the maximum weight of all the components in an entity.
+ *
+ * This includes fields, 'extra_fields', and other components added by
+ * third-party modules (e.g. field_group).
+ *
+ * @param $entity_type
+ * The type of entity; e.g. 'node' or 'user'.
+ * @param $bundle
+ * The bundle name.
+ * @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.
+ */
+function field_info_max_weight($entity_type, $bundle, $context) {
+ $weights = array();
+
+ // Collect weights for fields.
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ if ($context == 'form') {
+ $weights[] = $instance['widget']['weight'];
+ }
+ elseif (isset($instance['display'][$context]['weight'])) {
+ $weights[] = $instance['display'][$context]['weight'];
+ }
+ }
+ // Collect weights for extra fields.
+ foreach (field_info_extra_fields($entity_type, $bundle, $context) as $extra) {
+ $weights[] = $extra['weight'];
+ }
+
+ // Let other modules feedback about their own additions.
+ $weights = array_merge($weights, module_invoke_all('field_info_max_weight', $entity_type, $bundle, $context));
+ $max_weight = $weights ? max($weights) : NULL;
+
+ return $max_weight;
+}
+
+/**
+ * Returns a field type's default settings.
+ *
+ * @param $type
+ * A field type name.
+ *
+ * @return
+ * The field type's default settings, as provided by hook_field_info(), or an
+ * empty array if type or settings are not defined.
+ */
+function field_info_field_settings($type) {
+ $info = field_info_field_types($type);
+ return isset($info['settings']) ? $info['settings'] : array();
+}
+
+/**
+ * Returns a field type's default instance settings.
+ *
+ * @param $type
+ * A field type name.
+ *
+ * @return
+ * The field type's default instance settings, as provided by
+ * hook_field_info(), or an empty array if type or settings are not defined.
+ */
+function field_info_instance_settings($type) {
+ $info = field_info_field_types($type);
+ return isset($info['instance_settings']) ? $info['instance_settings'] : array();
+}
+
+/**
+ * Returns a field widget's default settings.
+ *
+ * @param $type
+ * A widget type name.
+ *
+ * @return
+ * The widget type's default settings, as provided by
+ * hook_field_widget_info(), or an empty array if type or settings are
+ * undefined.
+ */
+function field_info_widget_settings($type) {
+ $info = field_info_widget_types($type);
+ return isset($info['settings']) ? $info['settings'] : array();
+}
+
+/**
+ * Returns a field formatter's default settings.
+ *
+ * @param $type
+ * A field formatter type name.
+ *
+ * @return
+ * The formatter type's default settings, as provided by
+ * hook_field_formatter_info(), or an empty array if type or settings are
+ * undefined.
+ */
+function field_info_formatter_settings($type) {
+ $info = field_info_formatter_types($type);
+ return isset($info['settings']) ? $info['settings'] : array();
+}
+
+/**
+ * Returns a field storage type's default settings.
+ *
+ * @param $type
+ * A field storage type name.
+ *
+ * @return
+ * The storage type's default settings, as provided by
+ * hook_field_storage_info(), or an empty array if type or settings are
+ * undefined.
+ */
+function field_info_storage_settings($type) {
+ $info = field_info_storage_types($type);
+ return isset($info['settings']) ? $info['settings'] : array();
+}
+
+/**
+ * @} End of "defgroup field_info".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.install b/kolab.org/www/drupal-7.26/modules/field/field.install
new file mode 100644
index 0000000..c5c5005
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.install
@@ -0,0 +1,471 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the field module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function field_schema() {
+ // Static (meta) tables.
+ $schema['field_config'] = array(
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'The primary identifier for a field',
+ ),
+ 'field_name' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'description' => 'The name of this field. Non-deleted field names are unique, but multiple deleted fields can have the same name.',
+ ),
+ 'type' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'description' => 'The type of this field.',
+ ),
+ 'module' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The module that implements the field type.',
+ ),
+ 'active' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Boolean indicating whether the module that implements the field type is enabled.',
+ ),
+ 'storage_type' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'description' => 'The storage backend for the field.',
+ ),
+ 'storage_module' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The module that implements the storage backend.',
+ ),
+ 'storage_active' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Boolean indicating whether the module that implements the storage backend is enabled.',
+ ),
+ 'locked' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => '@TODO',
+ ),
+ 'data' => array(
+ 'type' => 'blob',
+ 'size' => 'big',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'description' => 'Serialized data containing the field properties that do not warrant a dedicated column.',
+ ),
+ 'cardinality' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'translatable' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'deleted' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(
+ 'field_name' => array('field_name'),
+ // Used by field_sync_field_status().
+ 'active' => array('active'),
+ 'storage_active' => array('storage_active'),
+ 'deleted' => array('deleted'),
+ // Used by field_modules_disabled().
+ 'module' => array('module'),
+ 'storage_module' => array('storage_module'),
+ 'type' => array('type'),
+ 'storage_type' => array('storage_type'),
+ ),
+ );
+ $schema['field_config_instance'] = array(
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'The primary identifier for a field instance',
+ ),
+ 'field_id' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'description' => 'The identifier of the field attached by this instance',
+ ),
+ 'field_name' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'entity_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'bundle' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'data' => array(
+ 'type' => 'blob',
+ 'size' => 'big',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ ),
+ 'deleted' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(
+ // Used by field_delete_instance().
+ 'field_name_bundle' => array('field_name', 'entity_type', 'bundle'),
+ // Used by field_read_instances().
+ 'deleted' => array('deleted'),
+ ),
+ );
+ $schema['cache_field'] = drupal_get_schema_unprocessed('system', 'cache');
+
+ return $schema;
+}
+
+/**
+ * Utility function: create a field by writing directly to the database.
+ *
+ * This function can be used for databases whose schema is at field module
+ * version 7000 or higher.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_create_field(&$field) {
+ // Merge in default values.`
+ $field += array(
+ 'entity_types' => array(),
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ 'locked' => FALSE,
+ 'settings' => array(),
+ 'indexes' => array(),
+ 'deleted' => 0,
+ 'active' => 1,
+ );
+
+ // Set storage.
+ $field['storage'] = array(
+ 'type' => 'field_sql_storage',
+ 'settings' => array(),
+ 'module' => 'field_sql_storage',
+ 'active' => 1,
+ );
+
+ // Fetch the field schema to initialize columns and indexes. The field module
+ // is not guaranteed to be loaded at this point.
+ module_load_install($field['module']);
+ $schema = (array) module_invoke($field['module'], 'field_schema', $field);
+ $schema += array('columns' => array(), 'indexes' => array());
+ // 'columns' are hardcoded in the field type.
+ $field['columns'] = $schema['columns'];
+ // 'indexes' can be both hardcoded in the field type, and specified in the
+ // incoming $field definition.
+ $field['indexes'] += $schema['indexes'];
+
+ // The serialized 'data' column contains everything from $field that does not
+ // have its own column and is not automatically populated when the field is
+ // read.
+ $data = $field;
+ unset($data['columns'], $data['field_name'], $data['type'], $data['active'], $data['module'], $data['storage_type'], $data['storage_active'], $data['storage_module'], $data['locked'], $data['cardinality'], $data['deleted']);
+ // Additionally, do not save the 'bundles' property populated by
+ // field_info_field().
+ unset($data['bundles']);
+
+ // Write the field to the database.
+ $record = array(
+ 'field_name' => $field['field_name'],
+ 'type' => $field['type'],
+ 'module' => $field['module'],
+ 'active' => (int) $field['active'],
+ 'storage_type' => $field['storage']['type'],
+ 'storage_module' => $field['storage']['module'],
+ 'storage_active' => (int) $field['storage']['active'],
+ 'locked' => (int) $field['locked'],
+ 'data' => serialize($data),
+ 'cardinality' => $field['cardinality'],
+ 'translatable' => (int) $field['translatable'],
+ 'deleted' => (int) $field['deleted'],
+ );
+ // We don't use drupal_write_record() here because it depends on the schema.
+ $field['id'] = db_insert('field_config')
+ ->fields($record)
+ ->execute();
+
+ // Create storage for the field.
+ field_sql_storage_field_storage_create_field($field);
+}
+
+/**
+ * Utility function: delete a field stored in SQL storage directly from the database.
+ *
+ * To protect user data, this function can only be used to delete fields once
+ * all information it stored is gone. Delete all data from the
+ * field_data_$field_name table before calling by either manually issuing
+ * delete queries against it or using _update_7000_field_delete_instance().
+ *
+ * This function can be used for databases whose schema is at field module
+ * version 7000 or higher.
+ *
+ * @param $field_name
+ * The field name to delete.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_delete_field($field_name) {
+ $table_name = 'field_data_' . $field_name;
+ if (db_select($table_name)->range(0, 1)->countQuery()->execute()->fetchField()) {
+ $t = get_t();
+ throw new Exception($t('This function can only be used to delete fields without data'));
+ }
+ // Delete all instances.
+ db_delete('field_config_instance')
+ ->condition('field_name', $field_name)
+ ->execute();
+
+ // Nuke field data and revision tables.
+ db_drop_table($table_name);
+ db_drop_table('field_revision_' . $field_name);
+
+ // Delete the field.
+ db_delete('field_config')
+ ->condition('field_name', $field_name)
+ ->execute();
+}
+
+
+/**
+ * Utility function: delete an instance and all its data of a field stored in SQL Storage.
+ *
+ * BEWARE: this function deletes user data from the field storage tables.
+ *
+ * This function is valid for a database schema version 7000.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_delete_instance($field_name, $entity_type, $bundle) {
+ // Delete field instance configuration data.
+ db_delete('field_config_instance')
+ ->condition('field_name', $field_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle)
+ ->execute();
+
+ // Nuke data.
+ db_delete('field_data_' . $field_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle)
+ ->execute();
+ db_delete('field_revision_' . $field_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle)
+ ->execute();
+}
+
+/**
+ * Utility function: fetch all the field definitions from the database.
+ *
+ * Warning: unlike the field_read_fields() API function, this function returns
+ * all fields by default, including deleted and inactive fields, unless
+ * specified otherwise in the $conditions parameter.
+ *
+ * @param $conditions
+ * An array of conditions to limit the select query to.
+ * @param $key
+ * The name of the field property the return array is indexed by. Using
+ * anything else than 'id' might cause incomplete results if the $conditions
+ * do not filter out deleted fields.
+ *
+ * @return
+ * An array of fields matching $conditions, keyed by the property specified
+ * by the $key parameter.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_read_fields(array $conditions = array(), $key = 'id') {
+ $fields = array();
+ $query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('fc');
+ foreach ($conditions as $column => $value) {
+ $query->condition($column, $value);
+ }
+ foreach ($query->execute() as $record) {
+ $field = unserialize($record['data']);
+ $field['id'] = $record['id'];
+ $field['field_name'] = $record['field_name'];
+ $field['type'] = $record['type'];
+ $field['module'] = $record['module'];
+ $field['active'] = $record['active'];
+ $field['storage']['type'] = $record['storage_type'];
+ $field['storage']['module'] = $record['storage_module'];
+ $field['storage']['active'] = $record['storage_active'];
+ $field['locked'] = $record['locked'];
+ $field['cardinality'] = $record['cardinality'];
+ $field['translatable'] = $record['translatable'];
+ $field['deleted'] = $record['deleted'];
+
+ $fields[$field[$key]] = $field;
+ }
+ return $fields;
+}
+
+/**
+ * Utility function: write a field instance directly to the database.
+ *
+ * This function can be used for databases whose schema is at field module
+ * version 7000 or higher.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_create_instance($field, &$instance) {
+ // Merge in defaults.
+ $instance += array(
+ 'field_id' => $field['id'],
+ 'field_name' => $field['field_name'],
+ 'deleted' => 0,
+ );
+
+ // The serialized 'data' column contains everything from $instance that does
+ // not have its own column and is not automatically populated when the
+ // instance is read.
+ $data = $instance;
+ unset($data['id'], $data['field_id'], $data['field_name'], $data['entity_type'], $data['bundle'], $data['deleted']);
+
+ $record = array(
+ 'field_id' => $instance['field_id'],
+ 'field_name' => $instance['field_name'],
+ 'entity_type' => $instance['entity_type'],
+ 'bundle' => $instance['bundle'],
+ 'data' => serialize($data),
+ 'deleted' => (int) $instance['deleted'],
+ );
+ $instance['id'] = db_insert('field_config_instance')
+ ->fields($record)
+ ->execute();
+}
+
+/**
+ * @addtogroup updates-6.x-to-7.x
+ * @{
+ */
+
+/**
+ * Field update version placeholder.
+ */
+function field_update_7000() {
+ // Some update helper functions (such as _update_7000_field_create_field())
+ // modify the database directly. They can be used safely only if the database
+ // schema matches the field module schema established for Drupal 7.0 (i.e.
+ // version 7000). This function exists solely to set the schema version to
+ // 7000, so that update functions calling those helpers can do so safely
+ // by declaring a dependency on field_update_7000().
+}
+
+/**
+ * Fix fields definitions created during the d6 to d7 upgrade path.
+ */
+function field_update_7001() {
+ $fields = _update_7000_field_read_fields();
+ foreach ($fields as $field) {
+ // _update_7000_field_create_field() was broken in d7 RC2, and the fields
+ // created during a d6 to d7 upgrade do not correcly store the 'index'
+ // entry. See http://drupal.org/node/996160.
+
+ module_load_install($field['module']);
+ $schema = (array) module_invoke($field['module'], 'field_schema', $field);
+ $schema += array('indexes' => array());
+ // 'indexes' can be both hardcoded in the field type, and specified in the
+ // incoming $field definition.
+ $field['indexes'] += $schema['indexes'];
+
+ // Place the updated entries in the existing serialized 'data' column.
+ $data = db_query("SELECT data FROM {field_config} WHERE id = :id", array(':id' => $field['id']))->fetchField();
+ $data = unserialize($data);
+ $data['columns'] = $field['columns'];
+ $data['indexes'] = $field['indexes'];
+
+ // Save the new data.
+ $query = db_update('field_config')
+ ->condition('id', $field['id'])
+ ->fields(array('data' => serialize($data)))
+ ->execute();
+ }
+}
+
+/**
+ * @} End of "addtogroup updates-6.x-to-7.x".
+ */
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Split the all-inclusive field_bundle_settings variable per bundle.
+ */
+function field_update_7002() {
+ $settings = variable_get('field_bundle_settings', array());
+ if ($settings) {
+ foreach ($settings as $entity_type => $entity_type_settings) {
+ foreach ($entity_type_settings as $bundle => $bundle_settings) {
+ variable_set('field_bundle_settings_' . $entity_type . '__' . $bundle, $bundle_settings);
+ }
+ }
+ variable_del('field_bundle_settings');
+ }
+}
+
+/**
+ * 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".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.module b/kolab.org/www/drupal-7.26/modules/field/field.module
new file mode 100644
index 0000000..52faf35
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.module
@@ -0,0 +1,1217 @@
+<?php
+/**
+ * @file
+ * Attach custom data fields to Drupal entities.
+ */
+
+/**
+ * Base class for all exceptions thrown by Field API functions.
+ *
+ * This class has no functionality of its own other than allowing all
+ * Field API exceptions to be caught by a single catch block.
+ */
+class FieldException extends Exception {}
+
+/*
+ * Load all public Field API functions. Drupal currently has no
+ * mechanism for auto-loading core APIs, so we have to load them on
+ * every page request.
+ */
+require_once DRUPAL_ROOT . '/modules/field/field.crud.inc';
+require_once DRUPAL_ROOT . '/modules/field/field.default.inc';
+require_once DRUPAL_ROOT . '/modules/field/field.info.inc';
+require_once DRUPAL_ROOT . '/modules/field/field.multilingual.inc';
+require_once DRUPAL_ROOT . '/modules/field/field.attach.inc';
+require_once DRUPAL_ROOT . '/modules/field/field.form.inc';
+
+/**
+ * @defgroup field Field API
+ * @{
+ * Attach custom data fields to Drupal entities.
+ *
+ * The Field API allows custom data fields to be attached to Drupal
+ * entities and takes care of storing, loading, editing, and rendering
+ * field data. Any entity type (node, user, etc.) can use the Field
+ * API to make itself "fieldable" and thus allow fields to be attached
+ * to it. Other modules can provide a user interface for managing custom
+ * fields via a web browser as well as a wide and flexible variety of
+ * data type, form element, and display format capabilities.
+ *
+ * The Field API defines two primary data structures, Field and
+ * Instance, and the concept of a Bundle. A Field defines a
+ * particular type of data that can be attached to entities. A Field
+ * Instance is a Field attached to a single Bundle. A Bundle is a set
+ * of fields that are treated as a group by the Field Attach API and
+ * is related to a single fieldable entity type.
+ *
+ * For example, suppose a site administrator wants Article nodes to
+ * have a subtitle and photo. Using the Field API or Field UI module,
+ * the administrator creates a field named 'subtitle' of type 'text'
+ * and a field named 'photo' of type 'image'. The administrator
+ * (again, via a UI) creates two Field Instances, one attaching the
+ * field 'subtitle' to the 'node' bundle 'article' and one attaching
+ * the field 'photo' to the 'node' bundle 'article'. When the node
+ * system uses the Field Attach API to load all fields for an Article
+ * node, it passes the node's entity type (which is 'node') and
+ * content type (which is 'article') as the node's bundle.
+ * field_attach_load() then loads the 'subtitle' and 'photo' fields
+ * because they are both attached to the 'node' bundle 'article'.
+ *
+ * Field definitions are represented as an array of key/value pairs.
+ *
+ * array $field:
+ * - id (integer, read-only): The primary identifier of the field. It is
+ * assigned automatically by field_create_field().
+ * - field_name (string): The name of the field. Each field name is unique
+ * within Field API. When a field is attached to an entity, the field's data
+ * is stored in $entity->$field_name. Maximum length is 32 characters.
+ * - type (string): The type of the field, such as 'text' or 'image'. Field
+ * types are defined by modules that implement hook_field_info().
+ * - entity_types (array): The array of entity types that can hold instances
+ * of this field. If empty or not specified, the field can have instances
+ * in any entity type.
+ * - cardinality (integer): The number of values the field can hold. Legal
+ * values are any positive integer or FIELD_CARDINALITY_UNLIMITED.
+ * - translatable (integer): Whether the field is translatable.
+ * - locked (integer): Whether or not the field is available for editing. If
+ * TRUE, users can't change field settings or create new instances of the
+ * field in the UI. Defaults to FALSE.
+ * - module (string, read-only): The name of the module that implements the
+ * field type.
+ * - active (integer, read-only): TRUE if the module that implements the field
+ * type is currently enabled, FALSE otherwise.
+ * - deleted (integer, read-only): TRUE if this field has been deleted, FALSE
+ * otherwise. Deleted fields are ignored by the Field Attach API. This
+ * property exists because fields can be marked for deletion but only
+ * actually destroyed by a separate garbage-collection process.
+ * - columns (array, read-only): An array of the Field API columns used to
+ * store each value of this field. The column list may depend on field
+ * settings; it is not constant per field type. Field API column
+ * specifications are exactly like Schema API column specifications but,
+ * depending on the field storage module in use, the name of the column may
+ * not represent an actual column in an SQL database.
+ * - indexes (array): An array of indexes on data columns, using the same
+ * definition format as Schema API index specifications. Only columns that
+ * appear in the 'columns' setting are allowed. Note that field types can
+ * specify default indexes, which can be modified or added to when
+ * creating a field.
+ * - foreign keys: (optional) An associative array of relations, using the same
+ * structure as the 'foreign keys' definition of hook_schema(). Note,
+ * however, that the field data is not necessarily stored in SQL. Also, the
+ * possible usage is limited, as you cannot specify another field as
+ * related, only existing SQL tables, such as filter formats.
+ * - settings (array): A sub-array of key/value pairs of field-type-specific
+ * settings. Each field type module defines and documents its own field
+ * settings.
+ * - storage (array): A sub-array of key/value pairs identifying the storage
+ * backend to use for the for the field:
+ * - type (string): The storage backend used by the field. Storage backends
+ * are defined by modules that implement hook_field_storage_info().
+ * - module (string, read-only): The name of the module that implements the
+ * storage backend.
+ * - active (integer, read-only): TRUE if the module that implements the
+ * storage backend is currently enabled, FALSE otherwise.
+ * - settings (array): A sub-array of key/value pairs of settings. Each
+ * storage backend defines and documents its own settings.
+ *
+ * Field instance definitions are represented as an array of key/value pairs.
+ *
+ * array $instance:
+ * - id (integer, read-only): The primary identifier of this field instance.
+ * It is assigned automatically by field_create_instance().
+ * - field_id (integer, read-only): The foreign key of the field attached to
+ * the bundle by this instance. It is populated automatically by
+ * field_create_instance().
+ * - field_name (string): The name of the field attached to the bundle by this
+ * instance.
+ * - entity_type (string): The name of the entity type the instance is attached
+ * to.
+ * - bundle (string): The name of the bundle that the field is attached to.
+ * - label (string): A human-readable label for the field when used with this
+ * bundle. For example, the label will be the title of Form API elements
+ * for this instance.
+ * - description (string): A human-readable description for the field when
+ * used with this bundle. For example, the description will be the help
+ * text of Form API elements for this instance.
+ * - required (integer): TRUE if a value for this field is required when used
+ * with this bundle, FALSE otherwise. Currently, required-ness is only
+ * enforced during Form API operations, not by field_attach_load(),
+ * field_attach_insert(), or field_attach_update().
+ * - default_value_function (string): The name of the function, if any, that
+ * will provide a default value.
+ * - default_value (array): If default_value_function is not set, then fixed
+ * values can be provided.
+ * - deleted (integer, read-only): TRUE if this instance has been deleted,
+ * FALSE otherwise. Deleted instances are ignored by the Field Attach API.
+ * This property exists because instances can be marked for deletion but
+ * only actually destroyed by a separate garbage-collection process.
+ * - settings (array): A sub-array of key/value pairs of field-type-specific
+ * instance settings. Each field type module defines and documents its own
+ * instance settings.
+ * - widget (array): A sub-array of key/value pairs identifying the Form API
+ * input widget for the field when used by this bundle:
+ * - type (string): The type of the widget, such as text_textfield. Widget
+ * types are defined by modules that implement hook_field_widget_info().
+ * - settings (array): A sub-array of key/value pairs of
+ * widget-type-specific settings. Each field widget type module defines
+ * and documents its own widget settings.
+ * - weight (float): The weight of the widget relative to the other elements
+ * in entity edit forms.
+ * - module (string, read-only): The name of the module that implements the
+ * widget type.
+ * - display (array): A sub-array of key/value pairs identifying the way field
+ * values should be displayed in each of the entity type's view modes, plus
+ * the 'default' mode. For each view mode, Field UI lets site administrators
+ * define whether they want to use a dedicated set of display options or the
+ * 'default' options to reduce the number of displays to maintain as they
+ * add new fields. For nodes, on a fresh install, only the 'teaser' view
+ * mode is configured to use custom display options, all other view modes
+ * defined use the 'default' options by default. When programmatically
+ * adding field instances on nodes, it is therefore recommended to at least
+ * specify display options for 'default' and 'teaser':
+ * - default (array): A sub-array of key/value pairs describing the display
+ * options to be used when the field is being displayed in view modes
+ * that are not configured to use dedicated display options:
+ * - label (string): Position of the label. 'inline', 'above' and
+ * 'hidden' are the values recognized by the default 'field' theme
+ * implementation.
+ * - type (string): The type of the display formatter, or 'hidden' for
+ * no display.
+ * - settings (array): A sub-array of key/value pairs of display
+ * options specific to the formatter.
+ * - weight (float): The weight of the field relative to the other entity
+ * components displayed in this view mode.
+ * - module (string, read-only): The name of the module which implements
+ * the display formatter.
+ * - some_mode: A sub-array of key/value pairs describing the display
+ * options to be used when the field is being displayed in the 'some_mode'
+ * view mode. Those options will only be actually applied at run time if
+ * the view mode is not configured to use default settings for this bundle:
+ * - ...
+ * - other_mode:
+ * - ...
+ *
+ * The (default) render arrays produced for field instances are documented at
+ * field_attach_view().
+ *
+ * Bundles are represented by two strings, an entity type and a bundle name.
+ *
+ * - @link field_types Field Types API @endlink. Defines field types,
+ * widget types, and display formatters. Field modules use this API
+ * to provide field types like Text and Node Reference along with the
+ * associated form elements and display formatters.
+ *
+ * - @link field_crud Field CRUD API @endlink. Create, updates, and
+ * deletes fields, bundles (a.k.a. "content types"), and instances.
+ * Modules use this API, often in hook_install(), to create
+ * custom data structures.
+ *
+ * - @link field_attach Field Attach API @endlink. Connects entity
+ * types to the Field API. Field Attach API functions load, store,
+ * generate Form API structures, display, and perform a variety of
+ * other functions for field data connected to individual entities.
+ * Fieldable entity types like node and user use this API to make
+ * themselves fieldable.
+ *
+ * - @link field_info Field Info API @endlink. Exposes information
+ * about all fields, instances, widgets, and related information
+ * defined by or with the Field API.
+ *
+ * - @link field_storage Field Storage API @endlink. Provides a
+ * pluggable back-end storage system for actual field data. The
+ * default implementation, field_sql_storage.module, stores field data
+ * in the local SQL database.
+ *
+ * - @link field_purge Field API bulk data deletion @endlink. Cleans
+ * up after bulk deletion operations such as field_delete_field()
+ * and field_delete_instance().
+ *
+ * - @link field_language Field language API @endlink. Provides native
+ * multilingual support for the Field API.
+ */
+
+/**
+ * Value for field API indicating a field accepts an unlimited number of values.
+ */
+define('FIELD_CARDINALITY_UNLIMITED', -1);
+
+/**
+ * Value for field API indicating a widget doesn't accept default values.
+ *
+ * @see hook_field_widget_info()
+ */
+define('FIELD_BEHAVIOR_NONE', 0x0001);
+
+/**
+ * Value for field API concerning widget default and multiple value settings.
+ *
+ * @see hook_field_widget_info()
+ *
+ * When used in a widget default context, indicates the widget accepts default
+ * values. When used in a multiple value context for a widget that allows the
+ * input of one single field value, indicates that the widget will be repeated
+ * for each value input.
+ */
+define('FIELD_BEHAVIOR_DEFAULT', 0x0002);
+
+/**
+ * Value for field API indicating a widget can receive several field values.
+ *
+ * @see hook_field_widget_info()
+ */
+define('FIELD_BEHAVIOR_CUSTOM', 0x0004);
+
+/**
+ * Age argument for loading the most recent version of an entity's
+ * field data with field_attach_load().
+ */
+define('FIELD_LOAD_CURRENT', 'FIELD_LOAD_CURRENT');
+
+/**
+ * Age argument for loading the version of an entity's field data
+ * specified in the entity with field_attach_load().
+ */
+define('FIELD_LOAD_REVISION', 'FIELD_LOAD_REVISION');
+
+/**
+ * Exception class thrown by hook_field_update_forbid().
+ */
+class FieldUpdateForbiddenException extends FieldException {}
+
+/**
+ * Implements hook_help().
+ */
+function field_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#field':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Field module allows custom data fields to be defined for <em>entity</em> types (entities include content items, comments, user accounts, and taxonomy terms). The Field module takes care of storing, loading, editing, and rendering field data. Most users will not interact with the Field module directly, but will instead use the <a href="@field-ui-help">Field UI module</a> user interface. Module developers can use the Field API to make new entity types "fieldable" and thus allow fields to be attached to them. For more information, see the online handbook entry for <a href="@field">Field module</a>.', array('@field-ui-help' => url('admin/help/field_ui'), '@field' => 'http://drupal.org/documentation/modules/field')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Enabling field types') . '</dt>';
+ $output .= '<dd>' . t('The Field module provides the infrastructure for fields and field attachment; the field types and input widgets themselves are provided by additional modules. Some of the modules are required; the optional modules can be enabled from the <a href="@modules">Modules administration page</a>. Drupal core includes the following field type modules: Number (required), Text (required), List (required), Taxonomy (optional), Image (optional), and File (optional); the required Options module provides input widgets for other field modules. Additional fields and widgets may be provided by contributed modules, which you can find in the <a href="@contrib">contributed module section of Drupal.org</a>. Currently enabled field and input widget modules:', array('@modules' => url('admin/modules'), '@contrib' => 'http://drupal.org/project/modules', '@options' => url('admin/help/options')));
+
+ // Make a list of all widget and field modules currently enabled, in
+ // order by displayed module name (module names are not translated).
+ $items = array();
+ $info = system_get_info('module');
+ $modules = array_merge(module_implements('field_info'), module_implements('field_widget_info'));
+ $modules = array_unique($modules);
+ sort($modules);
+ foreach ($modules as $module) {
+ $display = $info[$module]['name'];
+ if (module_hook($module, 'help')) {
+ $items['items'][] = l($display, 'admin/help/' . $module);
+ }
+ else {
+ $items['items'][] = $display;
+ }
+ }
+ $output .= theme('item_list', $items) . '</dd>';
+ $output .= '<dt>' . t('Managing field data storage') . '</dt>';
+ $output .= '<dd>' . t('Developers of field modules can either use the default <a href="@sql-store">Field SQL storage module</a> to store data for their fields, or a contributed or custom module developed using the <a href="@storage-api">field storage API</a>.', array('@storage-api' => 'http://api.drupal.org/api/group/field_storage/7', '@sql-store' => url('admin/help/field_sql_storage'))) . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function field_theme() {
+ return array(
+ 'field' => array(
+ 'render element' => 'element',
+ ),
+ 'field_multiple_value_form' => array(
+ 'render element' => 'element',
+ ),
+ );
+}
+
+/**
+ * Implements hook_cron().
+ */
+function field_cron() {
+ // Refresh the 'active' status of fields.
+ field_sync_field_status();
+
+ // Do a pass of purging on deleted Field API data, if any exists.
+ $limit = variable_get('field_purge_batch_size', 10);
+ 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().
+ *
+ * Goes through a list of all modules that provide a field type, and makes them
+ * required if there are any active fields of that type.
+ */
+function field_system_info_alter(&$info, $file, $type) {
+ if ($type == 'module' && module_hook($file->name, 'field_info')) {
+ $fields = field_read_fields(array('module' => $file->name), array('include_deleted' => TRUE));
+ if ($fields) {
+ $info['required'] = TRUE;
+
+ // Provide an explanation message (only mention pending deletions if there
+ // remains no actual, non-deleted fields)
+ $non_deleted = FALSE;
+ foreach ($fields as $field) {
+ if (empty($field['deleted'])) {
+ $non_deleted = TRUE;
+ break;
+ }
+ }
+ if ($non_deleted) {
+ if (module_exists('field_ui')) {
+ $explanation = t('Field type(s) in use - see <a href="@fields-page">Field list</a>', array('@fields-page' => url('admin/reports/fields')));
+ }
+ else {
+ $explanation = t('Fields type(s) in use');
+ }
+ }
+ else {
+ $explanation = t('Fields pending deletion');
+ }
+ $info['explanation'] = $explanation;
+ }
+ }
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function field_flush_caches() {
+ // Refresh the 'active' status of fields.
+ field_sync_field_status();
+
+ // Request a flush of our cache table.
+ return array('cache_field');
+}
+
+/**
+ * Implements hook_modules_enabled().
+ */
+function field_modules_enabled($modules) {
+ // Refresh the 'active' status of fields.
+ field_sync_field_status();
+}
+
+/**
+ * Implements hook_modules_disabled().
+ */
+function field_modules_disabled($modules) {
+ // Refresh the 'active' status of fields.
+ field_sync_field_status();
+}
+
+/**
+ * Refreshes the 'active' and 'storage_active' columns for fields.
+ */
+function field_sync_field_status() {
+ // Refresh the 'active' and 'storage_active' columns according to the current
+ // set of enabled modules.
+ $modules = module_list();
+ foreach ($modules as $module_name) {
+ field_associate_fields($module_name);
+ }
+ db_update('field_config')
+ ->fields(array('active' => 0))
+ ->condition('module', $modules, 'NOT IN')
+ ->execute();
+ db_update('field_config')
+ ->fields(array('storage_active' => 0))
+ ->condition('storage_module', $modules, 'NOT IN')
+ ->execute();
+}
+
+/**
+ * Allows a module to update the database for fields and columns it controls.
+ *
+ * @param $module
+ * The name of the module to update on.
+ */
+function field_associate_fields($module) {
+ // Associate field types.
+ $field_types = (array) module_invoke($module, 'field_info');
+ if ($field_types) {
+ db_update('field_config')
+ ->fields(array('module' => $module, 'active' => 1))
+ ->condition('type', array_keys($field_types))
+ ->execute();
+ }
+ // Associate storage backends.
+ $storage_types = (array) module_invoke($module, 'field_storage_info');
+ if ($storage_types) {
+ db_update('field_config')
+ ->fields(array('storage_module' => $module, 'storage_active' => 1))
+ ->condition('storage_type', array_keys($storage_types))
+ ->execute();
+ }
+}
+
+/**
+ * Helper function to get the default value for a field on an entity.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ * The entity for the operation.
+ * @param $field
+ * The field structure.
+ * @param $instance
+ * The instance structure.
+ * @param $langcode
+ * The field language to fill-in with the default value.
+ */
+function field_get_default_value($entity_type, $entity, $field, $instance, $langcode = NULL) {
+ $items = array();
+ if (!empty($instance['default_value_function'])) {
+ $function = $instance['default_value_function'];
+ if (function_exists($function)) {
+ $items = $function($entity_type, $entity, $field, $instance, $langcode);
+ }
+ }
+ elseif (!empty($instance['default_value'])) {
+ $items = $instance['default_value'];
+ }
+ return $items;
+}
+
+/**
+ * Helper function to filter out empty field values.
+ *
+ * @param $field
+ * The field definition.
+ * @param $items
+ * The field values to filter.
+ *
+ * @return
+ * The array of items without empty field values. The function also renumbers
+ * the array keys to ensure sequential deltas.
+ */
+function _field_filter_items($field, $items) {
+ $function = $field['module'] . '_field_is_empty';
+ foreach ((array) $items as $delta => $item) {
+ // Explicitly break if the function is undefined.
+ if ($function($item, $field)) {
+ unset($items[$delta]);
+ }
+ }
+ return array_values($items);
+}
+
+/**
+ * Helper function to sort items in a field according to
+ * user drag-n-drop reordering.
+ */
+function _field_sort_items($field, $items) {
+ if (($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) && isset($items[0]['_weight'])) {
+ usort($items, '_field_sort_items_helper');
+ foreach ($items as $delta => $item) {
+ if (is_array($items[$delta])) {
+ unset($items[$delta]['_weight']);
+ }
+ }
+ }
+ return $items;
+}
+
+/**
+ * Sort function for items order.
+ * (copied form element_sort(), which acts on #weight keys)
+ */
+function _field_sort_items_helper($a, $b) {
+ $a_weight = (is_array($a) ? $a['_weight'] : 0);
+ $b_weight = (is_array($b) ? $b['_weight'] : 0);
+ return $a_weight - $b_weight;
+}
+
+/**
+ * Same as above, using ['_weight']['#value']
+ */
+function _field_sort_items_value_helper($a, $b) {
+ $a_weight = (is_array($a) && isset($a['_weight']['#value']) ? $a['_weight']['#value'] : 0);
+ $b_weight = (is_array($b) && isset($b['_weight']['#value']) ? $b['_weight']['#value'] : 0);
+ return $a_weight - $b_weight;
+}
+
+/**
+ * Gets or sets administratively defined bundle settings.
+ *
+ * @param string $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param string $bundle
+ * The bundle name.
+ * @param array|null $settings
+ * (optional) The settings to store, an associative array with the following
+ * elements:
+ * - view_modes: An associative array keyed by view mode, with the following
+ * key/value pairs:
+ * - custom_settings: Boolean specifying whether the view mode uses a
+ * dedicated set of display options (TRUE), or the 'default' options
+ * (FALSE). Defaults to FALSE.
+ * - extra_fields: An associative array containing the form and display
+ * settings for extra fields (also known as pseudo-fields):
+ * - form: An associative array whose keys are the names of extra fields,
+ * and whose values are associative arrays with the following elements:
+ * - weight: The weight of the extra field, determining its position on an
+ * entity form.
+ * - display: An associative array whose keys are the names of extra fields,
+ * and whose values are associative arrays keyed by the name of view
+ * modes. This array must include an item for the 'default' view mode.
+ * Each view mode sub-array contains the following elements:
+ * - weight: The weight of the extra field, determining its position when
+ * an entity is viewed.
+ * - visible: TRUE if the extra field is visible, FALSE otherwise.
+ *
+ * @return array|null
+ * If no $settings are passed, the current settings are returned.
+ */
+function field_bundle_settings($entity_type, $bundle, $settings = NULL) {
+ if (isset($settings)) {
+ variable_set('field_bundle_settings_' . $entity_type . '__' . $bundle, $settings);
+ field_info_cache_clear();
+ }
+ else {
+ $settings = variable_get('field_bundle_settings_' . $entity_type . '__' . $bundle, array());
+ $settings += array(
+ 'view_modes' => array(),
+ 'extra_fields' => array(),
+ );
+ $settings['extra_fields'] += array(
+ 'form' => array(),
+ 'display' => array(),
+ );
+
+ return $settings;
+ }
+}
+
+/**
+ * Returns view mode settings in a given bundle.
+ *
+ * @param $entity_type
+ * The type of entity; e.g. 'node' or 'user'.
+ * @param $bundle
+ * The bundle name to return view mode settings for.
+ *
+ * @return
+ * An array keyed by view mode, with the following key/value pairs:
+ * - custom_settings: Boolean specifying whether the view mode uses a
+ * dedicated set of display options (TRUE), or the 'default' options
+ * (FALSE). Defaults to FALSE.
+ */
+function field_view_mode_settings($entity_type, $bundle) {
+ $cache = &drupal_static(__FUNCTION__, array());
+
+ if (!isset($cache[$entity_type][$bundle])) {
+ $bundle_settings = field_bundle_settings($entity_type, $bundle);
+ $settings = $bundle_settings['view_modes'];
+ // Include view modes for which nothing has been stored yet, but whose
+ // definition in hook_entity_info() specify they should use custom settings
+ // by default.
+ $entity_info = entity_get_info($entity_type);
+ foreach ($entity_info['view modes'] as $view_mode => $view_mode_info) {
+ if (!isset($settings[$view_mode]['custom_settings']) && $view_mode_info['custom settings']) {
+ $settings[$view_mode]['custom_settings'] = TRUE;
+ }
+ }
+ $cache[$entity_type][$bundle] = $settings;
+ }
+
+ return $cache[$entity_type][$bundle];
+}
+
+/**
+ * Returns the display settings to use for an instance in a given view mode.
+ *
+ * @param $instance
+ * The field instance being displayed.
+ * @param $view_mode
+ * The view mode.
+ * @param $entity
+ * The entity being displayed.
+ *
+ * @return
+ * The display settings to be used when displaying the field values.
+ */
+function field_get_display($instance, $view_mode, $entity) {
+ // Check whether the view mode uses custom display settings or the 'default'
+ // mode.
+ $view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
+ $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default');
+ $display = $instance['display'][$actual_mode];
+
+ // Let modules alter the display settings.
+ $context = array(
+ 'entity_type' => $instance['entity_type'],
+ 'field' => field_info_field($instance['field_name']),
+ 'instance' => $instance,
+ 'entity' => $entity,
+ 'view_mode' => $view_mode,
+ );
+ drupal_alter(array('field_display', 'field_display_' . $instance['entity_type']), $display, $context);
+
+ return $display;
+}
+
+/**
+ * Returns the display settings to use for pseudo-fields in a given view mode.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $bundle
+ * The bundle name.
+ * @param $view_mode
+ * The view mode.
+ *
+ * @return
+ * The display settings to be used when viewing the bundle's pseudo-fields.
+ */
+function field_extra_fields_get_display($entity_type, $bundle, $view_mode) {
+ // Check whether the view mode uses custom display settings or the 'default'
+ // mode.
+ $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
+ $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings'])) ? $view_mode : 'default';
+ $extra_fields = field_info_extra_fields($entity_type, $bundle, 'display');
+
+ $displays = array();
+ foreach ($extra_fields as $name => $value) {
+ $displays[$name] = $extra_fields[$name]['display'][$actual_mode];
+ }
+
+ // Let modules alter the display settings.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'bundle' => $bundle,
+ 'view_mode' => $view_mode,
+ );
+ drupal_alter('field_extra_fields_display', $displays, $context);
+
+ return $displays;
+}
+
+/**
+ * Pre-render callback to adjust weights and visibility of non-field elements.
+ */
+function _field_extra_fields_pre_render($elements) {
+ $entity_type = $elements['#entity_type'];
+ $bundle = $elements['#bundle'];
+
+ if (isset($elements['#type']) && $elements['#type'] == 'form') {
+ $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form');
+ foreach ($extra_fields as $name => $settings) {
+ if (isset($elements[$name])) {
+ $elements[$name]['#weight'] = $settings['weight'];
+ }
+ }
+ }
+ elseif (isset($elements['#view_mode'])) {
+ $view_mode = $elements['#view_mode'];
+ $extra_fields = field_extra_fields_get_display($entity_type, $bundle, $view_mode);
+ foreach ($extra_fields as $name => $settings) {
+ if (isset($elements[$name])) {
+ $elements[$name]['#weight'] = $settings['weight'];
+ // Visibility: make sure we do not accidentally show a hidden element.
+ $elements[$name]['#access'] = isset($elements[$name]['#access']) ? ($elements[$name]['#access'] && $settings['visible']) : $settings['visible'];
+ }
+ }
+ }
+
+ return $elements;
+}
+
+/**
+ * Clear the field info and field data caches.
+ */
+function field_cache_clear() {
+ cache_clear_all('*', 'cache_field', TRUE);
+ field_info_cache_clear();
+}
+
+/**
+ * Like filter_xss_admin(), but with a shorter list of allowed tags.
+ *
+ * Used for items entered by administrators, like field descriptions,
+ * allowed values, where some (mainly inline) mark-up may be desired
+ * (so check_plain() is not acceptable).
+ */
+function field_filter_xss($string) {
+ return filter_xss($string, _field_filter_xss_allowed_tags());
+}
+
+/**
+ * List of tags allowed by field_filter_xss().
+ */
+function _field_filter_xss_allowed_tags() {
+ return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img');
+}
+
+/**
+ * Human-readable list of allowed tags, for display in help texts.
+ */
+function _field_filter_xss_display_allowed_tags() {
+ return '<' . implode('> <', _field_filter_xss_allowed_tags()) . '>';
+}
+
+/**
+ * Returns a renderable array for a single field value.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ * The entity containing the field to display. Must at least contain the id
+ * key and the field data to display.
+ * @param $field_name
+ * The name of the field to display.
+ * @param $item
+ * The field value to display, as found in
+ * $entity->field_name[$langcode][$delta].
+ * @param $display
+ * Can be either the name of a view mode, or an array of display settings.
+ * See field_view_field() for more information.
+ * @param $langcode
+ * (Optional) The language of the value in $item. If not provided, the
+ * current language will be assumed.
+ * @return
+ * A renderable array for the field value.
+ */
+function field_view_value($entity_type, $entity, $field_name, $item, $display = array(), $langcode = NULL) {
+ $output = array();
+
+ if ($field = field_info_field($field_name)) {
+ // Determine the langcode that will be used by language fallback.
+ $langcode = field_language($entity_type, $entity, $field_name, $langcode);
+
+ // Push the item as the single value for the field, and defer to
+ // field_view_field() to build the render array for the whole field.
+ $clone = clone $entity;
+ $clone->{$field_name}[$langcode] = array($item);
+ $elements = field_view_field($entity_type, $clone, $field_name, $display, $langcode);
+
+ // Extract the part of the render array we need.
+ $output = isset($elements[0]) ? $elements[0] : array();
+ if (isset($elements['#access'])) {
+ $output['#access'] = $elements['#access'];
+ }
+ }
+
+ return $output;
+}
+
+/**
+ * Returns a renderable array for the value of a single field in an entity.
+ *
+ * The resulting output is a fully themed field with label and multiple values.
+ *
+ * This function can be used by third-party modules that need to output an
+ * isolated field.
+ * - 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
+ * 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.
+ *
+ * The function takes care of invoking the prepare_view steps. It also respects
+ * field access permissions.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ * The entity containing the field to display. Must at least contain the id
+ * key and the field data to display.
+ * @param $field_name
+ * The name of the field to display.
+ * @param $display
+ * Can be either:
+ * - The name of a view mode. The field will be displayed according to the
+ * display settings specified for this view mode in the $instance
+ * definition for the field in the entity's bundle.
+ * If no display settings are found for the view mode, the settings for
+ * the 'default' view mode will be used.
+ * - An array of display settings, as found in the 'display' entry of
+ * $instance definitions. The following key/value pairs are allowed:
+ * - label: (string) Position of the label. The default 'field' theme
+ * implementation supports the values 'inline', 'above' and 'hidden'.
+ * Defaults to 'above'.
+ * - type: (string) The formatter to use. Defaults to the
+ * 'default_formatter' for the field type, specified in
+ * hook_field_info(). The default formatter will also be used if the
+ * requested formatter is not available.
+ * - settings: (array) Settings specific to the formatter. Defaults to the
+ * formatter's default settings, specified in
+ * hook_field_formatter_info().
+ * - weight: (float) The weight to assign to the renderable element.
+ * Defaults to 0.
+ * @param $langcode
+ * (Optional) The language the field values are to be shown in. The site's
+ * current language fallback logic will be applied no values are available
+ * for the language. If no language is provided the current language will be
+ * used.
+ * @return
+ * A renderable array for the field value.
+ *
+ * @see field_view_value()
+ */
+function field_view_field($entity_type, $entity, $field_name, $display = array(), $langcode = NULL) {
+ $output = array();
+
+ if ($field = field_info_field($field_name)) {
+ if (is_array($display)) {
+ // When using custom display settings, fill in default values.
+ $cache = _field_info_field_cache();
+ $display = $cache->prepareInstanceDisplay($display, $field["type"]);
+ }
+
+ // Hook invocations are done through the _field_invoke() functions in
+ // 'single field' mode, to reuse the language fallback logic.
+ // Determine the actual language to display for the field, given the
+ // languages available in the field data.
+ $display_language = field_language($entity_type, $entity, $field_name, $langcode);
+ $options = array('field_name' => $field_name, 'language' => $display_language);
+ $null = NULL;
+
+ // Invoke prepare_view steps if needed.
+ if (empty($entity->_field_view_prepared)) {
+ list($id) = entity_extract_ids($entity_type, $entity);
+
+ // First let the field types do their preparation.
+ _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
+ // Then let the formatters do their own specific massaging.
+ _field_invoke_multiple_default('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
+ }
+
+ // Build the renderable array.
+ $result = _field_invoke_default('view', $entity_type, $entity, $display, $null, $options);
+
+ // Invoke hook_field_attach_view_alter() to let other modules alter the
+ // renderable array, as in a full field_attach_view() execution.
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'entity' => $entity,
+ 'view_mode' => '_custom',
+ 'display' => $display,
+ );
+ drupal_alter('field_attach_view', $result, $context);
+
+ if (isset($result[$field_name])) {
+ $output = $result[$field_name];
+ }
+ }
+
+ return $output;
+}
+
+/**
+ * Returns the field items in the language they currently would be displayed.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ * The entity containing the data to be displayed.
+ * @param $field_name
+ * The field to be displayed.
+ * @param $langcode
+ * (optional) The language code $entity->{$field_name} has to be displayed in.
+ * Defaults to the current language.
+ *
+ * @return
+ * An array of field items keyed by delta if available, FALSE otherwise.
+ */
+function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ $langcode = field_language($entity_type, $entity, $field_name, $langcode);
+ return isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : FALSE;
+}
+
+/**
+ * Determine whether a field has any data.
+ *
+ * @param $field
+ * A field structure.
+ * @return
+ * TRUE if the field has data for any entity; FALSE otherwise.
+ */
+function field_has_data($field) {
+ $query = new EntityFieldQuery();
+ return (bool) $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')
+ ->execute();
+}
+
+/**
+ * Determine whether the user has access to a given field.
+ *
+ * @param $op
+ * The operation to be performed. Possible values:
+ * - 'edit'
+ * - 'view'
+ * @param $field
+ * The full field structure array for the field on which the operation is to
+ * be performed. See field_info_field().
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ * (optional) The entity for the operation.
+ * @param $account
+ * (optional) The account to check, if not given use currently logged in user.
+ *
+ * @return
+ * TRUE if the operation is allowed;
+ * FALSE if the operation is denied.
+ */
+function field_access($op, $field, $entity_type, $entity = NULL, $account = NULL) {
+ global $user;
+
+ if (!isset($account)) {
+ $account = $user;
+ }
+
+ foreach (module_implements('field_access') as $module) {
+ $function = $module . '_field_access';
+ $access = $function($op, $field, $entity_type, $entity, $account);
+ if ($access === FALSE) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Helper function to extract the bundle name of from a bundle object.
+ *
+ * @param $entity_type
+ * The type of $entity; e.g., 'node' or 'user'.
+ * @param $bundle
+ * The bundle object (or string if bundles for this entity type do not exist
+ * as standalone objects).
+ * @return
+ * The bundle name.
+ */
+function field_extract_bundle($entity_type, $bundle) {
+ if (is_string($bundle)) {
+ return $bundle;
+ }
+
+ $info = entity_get_info($entity_type);
+ if (is_object($bundle) && isset($info['bundle keys']['bundle']) && isset($bundle->{$info['bundle keys']['bundle']})) {
+ return $bundle->{$info['bundle keys']['bundle']};
+ }
+}
+
+/**
+ * Theme preprocess function for theme_field() and field.tpl.php.
+ *
+ * @see theme_field()
+ * @see field.tpl.php
+ */
+function template_preprocess_field(&$variables, $hook) {
+ $element = $variables['element'];
+
+ // There's some overhead in calling check_plain() so only call it if the label
+ // variable is being displayed. Otherwise, set it to NULL to avoid PHP
+ // warnings if a theme implementation accesses the variable even when it's
+ // supposed to be hidden. If a theme implementation needs to print a hidden
+ // label, it needs to supply a preprocess function that sets it to the
+ // sanitized element title or whatever else is wanted in its place.
+ $variables['label_hidden'] = ($element['#label_display'] == 'hidden');
+ $variables['label'] = $variables['label_hidden'] ? NULL : check_plain($element['#title']);
+
+ // We want other preprocess functions and the theme implementation to have
+ // fast access to the field item render arrays. The item render array keys
+ // (deltas) should always be a subset of the keys in #items, and looping on
+ // those keys is faster than calling element_children() or looping on all keys
+ // within $element, since that requires traversal of all element properties.
+ $variables['items'] = array();
+ foreach ($element['#items'] as $delta => $item) {
+ if (!empty($element[$delta])) {
+ $variables['items'][$delta] = $element[$delta];
+ }
+ }
+
+ // Add default CSS classes. Since there can be many fields rendered on a page,
+ // save some overhead by calling strtr() directly instead of
+ // drupal_html_class().
+ $variables['field_name_css'] = strtr($element['#field_name'], '_', '-');
+ $variables['field_type_css'] = strtr($element['#field_type'], '_', '-');
+ $variables['classes_array'] = array(
+ 'field',
+ 'field-name-' . $variables['field_name_css'],
+ 'field-type-' . $variables['field_type_css'],
+ 'field-label-' . $element['#label_display'],
+ );
+ // Add a "clearfix" class to the wrapper since we float the label and the
+ // field items in field.css if the label is inline.
+ if ($element['#label_display'] == 'inline') {
+ $variables['classes_array'][] = 'clearfix';
+ }
+
+ // Add specific suggestions that can override the default implementation.
+ $variables['theme_hook_suggestions'] = array(
+ 'field__' . $element['#field_type'],
+ 'field__' . $element['#field_name'],
+ 'field__' . $element['#bundle'],
+ 'field__' . $element['#field_name'] . '__' . $element['#bundle'],
+ );
+}
+
+/**
+ * Theme process function for theme_field() and field.tpl.php.
+ *
+ * @see theme_field()
+ * @see field.tpl.php
+ */
+function template_process_field(&$variables, $hook) {
+ // The default theme implementation is a function, so template_process() does
+ // not automatically run, so we need to flatten the classes and attributes
+ // here. For best performance, only call drupal_attributes() when needed, and
+ // note that template_preprocess_field() does not initialize the
+ // *_attributes_array variables.
+ $variables['classes'] = implode(' ', $variables['classes_array']);
+ $variables['attributes'] = empty($variables['attributes_array']) ? '' : drupal_attributes($variables['attributes_array']);
+ $variables['title_attributes'] = empty($variables['title_attributes_array']) ? '' : drupal_attributes($variables['title_attributes_array']);
+ $variables['content_attributes'] = empty($variables['content_attributes_array']) ? '' : drupal_attributes($variables['content_attributes_array']);
+ foreach ($variables['items'] as $delta => $item) {
+ $variables['item_attributes'][$delta] = empty($variables['item_attributes_array'][$delta]) ? '' : drupal_attributes($variables['item_attributes_array'][$delta]);
+ }
+}
+/**
+ * @} End of "defgroup field".
+ */
+
+/**
+ * Returns HTML for a field.
+ *
+ * This is the default theme implementation to display the value of a field.
+ * Theme developers who are comfortable with overriding theme functions may do
+ * so in order to customize this markup. This function can be overridden with
+ * varying levels of specificity. For example, for a field named 'body'
+ * displayed on the 'article' content type, any of the following functions will
+ * override this default implementation. The first of these functions that
+ * exists is used:
+ * - THEMENAME_field__body__article()
+ * - THEMENAME_field__article()
+ * - THEMENAME_field__body()
+ * - THEMENAME_field()
+ *
+ * Theme developers who prefer to customize templates instead of overriding
+ * functions may copy the "field.tpl.php" from the "modules/field/theme" folder
+ * of the Drupal installation to somewhere within the theme's folder and
+ * customize it, just like customizing other Drupal templates such as
+ * page.tpl.php or node.tpl.php. However, it takes longer for the server to
+ * process templates than to call a function, so for websites with many fields
+ * displayed on a page, this can result in a noticeable slowdown of the website.
+ * For these websites, developers are discouraged from placing a field.tpl.php
+ * file into the theme's folder, but may customize templates for specific
+ * fields. For example, for a field named 'body' displayed on the 'article'
+ * content type, any of the following templates will override this default
+ * implementation. The first of these templates that exists is used:
+ * - field--body--article.tpl.php
+ * - field--article.tpl.php
+ * - field--body.tpl.php
+ * - field.tpl.php
+ * So, if the body field on the article content type needs customization, a
+ * field--body--article.tpl.php file can be added within the theme's folder.
+ * Because it's a template, it will result in slightly more time needed to
+ * display that field, but it will not impact other fields, and therefore,
+ * is unlikely to cause a noticeable change in website performance. A very rough
+ * guideline is that if a page is being displayed with more than 100 fields and
+ * they are all themed with a template instead of a function, it can add up to
+ * 5% to the time it takes to display that page. This is a guideline only and
+ * the exact performance impact depends on the server configuration and the
+ * details of the website.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - label_hidden: A boolean indicating to show or hide the field label.
+ * - title_attributes: A string containing the attributes for the title.
+ * - label: The label for the field.
+ * - content_attributes: A string containing the attributes for the content's
+ * div.
+ * - items: An array of field items.
+ * - item_attributes: An array of attributes for each item.
+ * - classes: A string containing the classes for the wrapping div.
+ * - attributes: A string containing the attributes for the wrapping div.
+ *
+ * @see template_preprocess_field()
+ * @see template_process_field()
+ * @see field.tpl.php
+ *
+ * @ingroup themeable
+ */
+function theme_field($variables) {
+ $output = '';
+
+ // Render the label, if it's not hidden.
+ if (!$variables['label_hidden']) {
+ $output .= '<div class="field-label"' . $variables['title_attributes'] . '>' . $variables['label'] . ':&nbsp;</div>';
+ }
+
+ // Render the items.
+ $output .= '<div class="field-items"' . $variables['content_attributes'] . '>';
+ foreach ($variables['items'] as $delta => $item) {
+ $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even');
+ $output .= '<div class="' . $classes . '"' . $variables['item_attributes'][$delta] . '>' . drupal_render($item) . '</div>';
+ }
+ $output .= '</div>';
+
+ // Render the top-level DIV.
+ $output = '<div class="' . $variables['classes'] . '"' . $variables['attributes'] . '>' . $output . '</div>';
+
+ return $output;
+}
+
+/**
+ * DEPRECATED: Helper form element validator: integer.
+ *
+ * Use element_validate_integer() instead.
+ *
+ * @deprecated
+ * @see element_validate_integer()
+ */
+function _element_validate_integer($element, &$form_state) {
+ element_validate_integer($element, $form_state);
+}
+
+/**
+ * DEPRECATED: Helper form element validator: integer > 0.
+ *
+ * Use element_validate_integer_positive() instead.
+ *
+ * @deprecated
+ * @see element_validate_integer_positive()
+ */
+function _element_validate_integer_positive($element, &$form_state) {
+ element_validate_integer_positive($element, $form_state);
+}
+
+/**
+ * DEPRECATED: Helper form element validator: number.
+ *
+ * Use element_validate_number() instead.
+ *
+ * @deprecated
+ * @see element_validate_number()
+ */
+function _element_validate_number($element, &$form_state) {
+ element_validate_number($element, $form_state);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/field.multilingual.inc b/kolab.org/www/drupal-7.26/modules/field/field.multilingual.inc
new file mode 100644
index 0000000..4b4f01d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/field.multilingual.inc
@@ -0,0 +1,301 @@
+<?php
+
+/**
+ * @file
+ * Functions implementing Field API multilingual support.
+ */
+
+/**
+ * @defgroup field_language Field Language API
+ * @{
+ * Handling of multilingual fields.
+ *
+ * Fields natively implement multilingual support, and all fields use the
+ * following structure:
+ * @code
+ * $entity->{$field_name}[$langcode][$delta][$column_name]
+ * @endcode
+ * Every field can hold a single or multiple value for each language belonging
+ * to the available languages set:
+ * - For untranslatable fields this set only contains LANGUAGE_NONE.
+ * - For translatable fields this set can contain any language code. By default
+ * it is the list returned by field_content_languages(), which contains all
+ * installed languages with the addition of LANGUAGE_NONE. This default can be
+ * altered by modules implementing hook_field_available_languages_alter().
+ *
+ * The available languages for a particular field are returned by
+ * field_available_languages(). Whether a field is translatable is determined by
+ * calling field_is_translatable(), which checks the $field['translatable']
+ * property returned by field_info_field(), and whether there is at least one
+ * translation handler available for the field. A translation handler is a
+ * module registering itself via hook_entity_info() to handle field
+ * translations.
+ *
+ * By default, _field_invoke() and _field_invoke_multiple() are processing a
+ * field in all available languages, unless they are given a language
+ * suggestion. Based on that suggestion, _field_language_suggestion() determines
+ * the languages to act on.
+ *
+ * Most field_attach_*() functions act on all available languages, except for
+ * the following:
+ * - field_attach_form() only takes a single language code, specifying which
+ * language the field values will be submitted in.
+ * - field_attach_view() requires the language the entity will be displayed in.
+ * Since it is unknown whether a field translation exists for the requested
+ * language, the translation handler is responsible for performing one of the
+ * following actions:
+ * - Ignore missing translations, i.e. do not show any field values for the
+ * requested language. For example, see locale_field_language_alter().
+ * - Provide a value in a different language as fallback. By default, the
+ * fallback logic is applied separately to each field to ensure that there
+ * is a value for each field to display.
+ * The field language fallback logic relies on the global language fallback
+ * configuration. Therefore, the displayed field values can be in the
+ * requested language, but may be different if no values for the requested
+ * language are available. The default language fallback rules inspect all the
+ * enabled languages ordered by their weight. This behavior can be altered or
+ * even disabled by modules implementing hook_field_language_alter(), making
+ * it possible to choose the first approach. The display language for each
+ * field is returned by field_language().
+ *
+ * See @link field Field API @endlink for information about the other parts of
+ * the Field API.
+ */
+
+/**
+ * Implements hook_multilingual_settings_changed().
+ */
+function field_multilingual_settings_changed() {
+ field_info_cache_clear();
+}
+
+/**
+ * Collects the available languages for the given entity type and field.
+ *
+ * If the given field has language support enabled, an array of available
+ * languages will be returned, otherwise only LANGUAGE_NONE will be returned.
+ * Since the default value for a 'translatable' entity property is FALSE, we
+ * ensure that only entities that are able to handle translations actually get
+ * translatable fields.
+ *
+ * @param $entity_type
+ * The type of the entity the field is attached to, e.g. 'node' or 'user'.
+ * @param $field
+ * A field structure.
+ *
+ * @return
+ * An array of valid language codes.
+ */
+function field_available_languages($entity_type, $field) {
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast)) {
+ $drupal_static_fast['field_languages'] = &drupal_static(__FUNCTION__);
+ }
+ $field_languages = &$drupal_static_fast['field_languages'];
+ $field_name = $field['field_name'];
+
+ if (!isset($field_languages[$entity_type][$field_name])) {
+ // If the field has language support enabled we retrieve an (alterable) list
+ // of enabled languages, otherwise we return just LANGUAGE_NONE.
+ if (field_is_translatable($entity_type, $field)) {
+ $languages = field_content_languages();
+ // Let other modules alter the available languages.
+ $context = array('entity_type' => $entity_type, 'field' => $field);
+ drupal_alter('field_available_languages', $languages, $context);
+ $field_languages[$entity_type][$field_name] = $languages;
+ }
+ else {
+ $field_languages[$entity_type][$field_name] = array(LANGUAGE_NONE);
+ }
+ }
+
+ return $field_languages[$entity_type][$field_name];
+}
+
+/**
+ * Process the given language suggestion based on the available languages.
+ *
+ * If a non-empty language suggestion is provided it must appear among the
+ * available languages, otherwise it will be ignored.
+ *
+ * @param $available_languages
+ * An array of valid language codes.
+ * @param $language_suggestion
+ * A language code or an array of language codes keyed by field name.
+ * @param $field_name
+ * The name of the field being processed.
+ *
+ * @return
+ * An array of valid language codes.
+ */
+function _field_language_suggestion($available_languages, $language_suggestion, $field_name) {
+ // Handle possible language suggestions.
+ if (!empty($language_suggestion)) {
+ // We might have an array of language suggestions keyed by field name.
+ if (is_array($language_suggestion) && isset($language_suggestion[$field_name])) {
+ $language_suggestion = $language_suggestion[$field_name];
+ }
+
+ // If we have a language suggestion and the suggested language is available,
+ // we return only it.
+ if (in_array($language_suggestion, $available_languages)) {
+ $available_languages = array($language_suggestion);
+ }
+ }
+
+ return $available_languages;
+}
+
+/**
+ * Returns available content languages.
+ *
+ * The languages that may be associated to fields include LANGUAGE_NONE.
+ *
+ * @return
+ * An array of language codes.
+ */
+function field_content_languages() {
+ return array_keys(language_list() + array(LANGUAGE_NONE => NULL));
+}
+
+/**
+ * Checks whether a field has language support.
+ *
+ * A field has language support enabled if its 'translatable' property is set to
+ * TRUE, and its entity type has at least one translation handler registered.
+ *
+ * @param $entity_type
+ * The type of the entity the field is attached to.
+ * @param $field
+ * A field data structure.
+ *
+ * @return
+ * TRUE if the field can be translated.
+ */
+function field_is_translatable($entity_type, $field) {
+ return $field['translatable'] && field_has_translation_handler($entity_type);
+}
+
+/**
+ * Checks if a module is registered as a translation handler for a given entity.
+ *
+ * If no handler is passed, simply check if there is any translation handler
+ * enabled for the given entity type.
+ *
+ * @param $entity_type
+ * The type of the entity whose fields are to be translated.
+ * @param $handler
+ * (optional) The name of the handler to be checked. Defaults to NULL.
+ *
+ * @return
+ * TRUE, if the given handler is allowed to manage field translations. If no
+ * handler is passed, TRUE means there is at least one registered translation
+ * handler.
+ */
+function field_has_translation_handler($entity_type, $handler = NULL) {
+ $entity_info = entity_get_info($entity_type);
+
+ if (isset($handler)) {
+ return !empty($entity_info['translation'][$handler]);
+ }
+ elseif (isset($entity_info['translation'])) {
+ foreach ($entity_info['translation'] as $handler_info) {
+ // The translation handler must use a non-empty data structure.
+ if (!empty($handler_info)) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Ensures that a given language code is valid.
+ *
+ * Checks whether the given language is one of the enabled languages. Otherwise,
+ * it returns the current, global language; or the site's default language, if
+ * the additional parameter $default is TRUE.
+ *
+ * @param $langcode
+ * The language code to validate.
+ * @param $default
+ * Whether to return the default language code or the current language code in
+ * case $langcode is invalid.
+ * @return
+ * A valid language code.
+ */
+function field_valid_language($langcode, $default = TRUE) {
+ $enabled_languages = field_content_languages();
+ if (in_array($langcode, $enabled_languages)) {
+ return $langcode;
+ }
+ global $language_content;
+ return $default ? language_default('language') : $language_content->language;
+}
+
+/**
+ * Returns the display language for the fields attached to the given entity.
+ *
+ * The actual language for each given field is determined based on the requested
+ * language and the actual data available in the fields themselves.
+ * If there is no registered translation handler for the given entity type, the
+ * display language to be used is just LANGUAGE_NONE, as no other language code
+ * is allowed by field_available_languages().
+ * If translation handlers are found, we let modules provide alternative display
+ * languages for fields not having the requested language available.
+ * Core language fallback rules are provided by locale_field_language_fallback()
+ * which is called by locale_field_language_alter().
+ *
+ * @param $entity_type
+ * The type of $entity.
+ * @param $entity
+ * The entity to be displayed.
+ * @param $field_name
+ * (optional) The name of the field to be displayed. Defaults to NULL. If
+ * no value is specified, the display languages for every field attached to
+ * the given entity will be returned.
+ * @param $langcode
+ * (optional) The language code $entity has to be displayed in. Defaults to
+ * NULL. If no value is given the current language will be used.
+ *
+ * @return
+ * A language code if a field name is specified, an array of language codes
+ * keyed by field name otherwise.
+ */
+function field_language($entity_type, $entity, $field_name = NULL, $langcode = NULL) {
+ $display_languages = &drupal_static(__FUNCTION__, array());
+ list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
+ $langcode = field_valid_language($langcode, FALSE);
+
+ if (!isset($display_languages[$entity_type][$id][$langcode])) {
+ $display_language = array();
+
+ // By default display language is set to LANGUAGE_NONE if the field
+ // translation is not available. It is up to translation handlers to
+ // implement language fallback rules.
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ $display_language[$instance['field_name']] = isset($entity->{$instance['field_name']}[$langcode]) ? $langcode : LANGUAGE_NONE;
+ }
+
+ if (field_has_translation_handler($entity_type)) {
+ $context = array(
+ 'entity_type' => $entity_type,
+ 'entity' => $entity,
+ 'language' => $langcode,
+ );
+ drupal_alter('field_language', $display_language, $context);
+ }
+
+ $display_languages[$entity_type][$id][$langcode] = $display_language;
+ }
+
+ $display_language = $display_languages[$entity_type][$id][$langcode];
+
+ // Single-field mode.
+ if (isset($field_name)) {
+ return isset($display_language[$field_name]) ? $display_language[$field_name] : FALSE;
+ }
+
+ return $display_language;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info
new file mode 100644
index 0000000..a5a8eaf
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info
@@ -0,0 +1,14 @@
+name = Field SQL storage
+description = Stores field data in an SQL database.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = field_sql_storage.test
+required = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install
new file mode 100644
index 0000000..24973ab
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the field_sql_storage module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function field_sql_storage_schema() {
+ $schema = array();
+
+ // Dynamic (data) tables.
+ if (db_table_exists('field_config')) {
+ $fields = field_read_fields(array(), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+ drupal_load('module', 'field_sql_storage');
+ foreach ($fields as $field) {
+ if ($field['storage']['type'] == 'field_sql_storage') {
+ $schema += _field_sql_storage_schema($field);
+ }
+ }
+ }
+ return $schema;
+}
+
+/**
+ * Utility function: write field data directly to SQL storage.
+ *
+ * This function can be used for databases whose schema is at field module
+ * version 7000 or higher.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
+ $table_name = "field_data_{$field_name}";
+ $revision_name = "field_revision_{$field_name}";
+
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $entity_id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $entity_id)
+ ->condition('revision_id', $revision_id)
+ ->execute();
+
+ $columns = array();
+ foreach ($data as $langcode => $items) {
+ foreach ($items as $delta => $item) {
+ $record = array(
+ 'entity_type' => $entity_type,
+ 'entity_id' => $entity_id,
+ 'revision_id' => $revision_id,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'language' => $langcode,
+ );
+ foreach ($item as $column => $value) {
+ $record[_field_sql_storage_columnname($field_name, $column)] = $value;
+ }
+
+ $records[] = $record;
+ // Record the columns used.
+ $columns += $record;
+ }
+ }
+
+ if ($columns) {
+ $query = db_insert($table_name)->fields(array_keys($columns));
+ $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+ foreach ($records as $record) {
+ $query->values($record);
+ if ($revision_id) {
+ $revision_query->values($record);
+ }
+ }
+ $query->execute();
+ $revision_query->execute();
+ }
+}
+
+/**
+ * @addtogroup updates-6.x-to-7.x
+ * @{
+ */
+
+/**
+ * Field SQL storage update version placeholder.
+ */
+function field_sql_storage_update_7000() {
+ // Some update helper functions (such as
+ // _update_7000_field_sql_storage_write()) modify the database directly. They
+ // can be used safely only if the database schema matches the field module
+ // schema established for Drupal 7.0 (i.e. version 7000). This function exists
+ // solely to set the schema version to 7000, so that update functions calling
+ // those helpers can do so safely by declaring a dependency on
+ // field_sql_storage_update_7000().
+}
+
+/**
+ * Remove the field_config_entity_type table and store 'entity_type' strings.
+ */
+function field_sql_storage_update_7001(&$sandbox) {
+ if (!isset($sandbox['progress'])) {
+ // Collect current etids.
+ $sandbox['etids'] = db_query('SELECT etid, type FROM {field_config_entity_type}')->fetchAllKeyed();
+
+ // Collect affected tables: field data, field revision data, 'deleted'
+ // tables.
+ $sandbox['tables'] = array();
+ $results = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('fc')
+ ->condition('storage_module', 'field_sql_storage')
+ ->execute();
+ foreach ($results as $field) {
+ if ($field['deleted']) {
+ $sandbox['tables']["field_deleted_data_{$field['id']}"] = 'data';
+ $sandbox['tables']["field_deleted_revision_{$field['id']}"] = 'revision';
+ }
+ else {
+ $sandbox['tables']["field_data_{$field['field_name']}"] = 'data';
+ $sandbox['tables']["field_revision_{$field['field_name']}"] = 'revision';
+ }
+ }
+ reset($sandbox['tables']);
+
+ $sandbox['total'] = count($sandbox['tables']);
+ $sandbox['progress'] = 0;
+ }
+
+ if ($sandbox['tables']) {
+ // Grab the next table to process.
+ $table = key($sandbox['tables']);
+ $type = array_shift($sandbox['tables']);
+
+ if (db_table_exists($table)) {
+ // Add the 'entity_type' column.
+ if (!db_field_exists($table, 'entity_type')) {
+ $column = array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The entity type this data is attached to.',
+ );
+ db_add_field($table, 'entity_type', $column);
+
+ // Populate the 'entity_type' column based on the 'etid' column.
+ foreach ($sandbox['etids'] as $etid => $entity_type) {
+ db_update($table)
+ ->fields(array('entity_type' => $entity_type))
+ ->condition('etid', $etid)
+ ->execute();
+ }
+
+ // Index the new column.
+ db_add_index($table, 'entity_type', array('entity_type'));
+ }
+
+ // Use the 'entity_type' column in the primary key.
+ db_drop_primary_key($table);
+ $primary_keys = array(
+ 'data' => array('entity_type', 'entity_id', 'deleted', 'delta', 'language'),
+ 'revision' => array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language'),
+ );
+ db_add_primary_key($table, $primary_keys[$type]);
+
+ // Drop the 'etid' column.
+ if (db_field_exists($table, 'etid')) {
+ db_drop_field($table, 'etid');
+ }
+ }
+
+ // Report progress.
+ $sandbox['progress']++;
+ $sandbox['#finished'] = min(0.99, $sandbox['progress'] / $sandbox['total']);
+ }
+ else {
+ // No more tables left: drop the field_config_entity_type table.
+ db_drop_table('field_config_entity_type');
+
+ // Drop the previous 'field_sql_storage_ENTITYTYPE_etid' system variables.
+ foreach ($sandbox['etids'] as $etid => $entity_type) {
+ variable_del('field_sql_storage_' . $entity_type . '_etid');
+ }
+
+ // We're done.
+ $sandbox['#finished'] = 1;
+ }
+}
+
+/**
+ * Fix primary keys in field revision data tables.
+ */
+function field_sql_storage_update_7002() {
+ $results = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('fc')
+ ->condition('storage_module', 'field_sql_storage')
+ ->execute();
+ foreach ($results as $field) {
+ // Revision tables of deleted fields do not need to be fixed, since no new
+ // data is written to them.
+ if (!$field['deleted']) {
+ $table = "field_revision_{$field['field_name']}";
+ db_drop_primary_key($table);
+ db_add_primary_key($table, array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language'));
+ }
+ }
+}
+
+/**
+ * @} End of "addtogroup updates-6.x-to-7.x".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module
new file mode 100644
index 0000000..93f2077
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module
@@ -0,0 +1,758 @@
+<?php
+
+/**
+ * @file
+ * Default implementation of the field storage API.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function field_sql_storage_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#field_sql_storage':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Field SQL storage module stores field data in the database. It is the default field storage module; other field storage mechanisms may be available as contributed modules. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_storage_info().
+ */
+function field_sql_storage_field_storage_info() {
+ return array(
+ 'field_sql_storage' => array(
+ 'label' => t('Default SQL storage'),
+ 'description' => t('Stores fields in the local SQL database, using per-field tables.'),
+ ),
+ );
+}
+
+/**
+ * Generate a table name for a field data table.
+ *
+ * @param $field
+ * The field structure.
+ * @return
+ * A string containing the generated name for the database table
+ */
+function _field_sql_storage_tablename($field) {
+ if ($field['deleted']) {
+ return "field_deleted_data_{$field['id']}";
+ }
+ else {
+ return "field_data_{$field['field_name']}";
+ }
+}
+
+/**
+ * Generate a table name for a field revision archive table.
+ *
+ * @param $name
+ * The field structure.
+ * @return
+ * A string containing the generated name for the database table
+ */
+function _field_sql_storage_revision_tablename($field) {
+ if ($field['deleted']) {
+ return "field_deleted_revision_{$field['id']}";
+ }
+ else {
+ return "field_revision_{$field['field_name']}";
+ }
+}
+
+/**
+ * Generate a column name for a field data table.
+ *
+ * @param $name
+ * The name of the field
+ * @param $column
+ * The name of the column
+ * @return
+ * A string containing a generated column name for a field data
+ * table that is unique among all other fields.
+ */
+function _field_sql_storage_columnname($name, $column) {
+ return $name . '_' . $column;
+}
+
+/**
+ * Generate an index name for a field data table.
+ *
+ * @param $name
+ * The name of the field
+ * @param $column
+ * The name of the index
+ * @return
+ * A string containing a generated index name for a field data
+ * table that is unique among all other fields.
+ */
+function _field_sql_storage_indexname($name, $index) {
+ return $name . '_' . $index;
+}
+
+/**
+ * Return the database schema for a field. This may contain one or
+ * more tables. Each table will contain the columns relevant for the
+ * specified field. Leave the $field's 'columns' and 'indexes' keys
+ * empty to get only the base schema.
+ *
+ * @param $field
+ * The field structure for which to generate a database schema.
+ * @return
+ * One or more tables representing the schema for the field.
+ */
+function _field_sql_storage_schema($field) {
+ $deleted = $field['deleted'] ? 'deleted ' : '';
+ $current = array(
+ 'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})",
+ 'fields' => array(
+ 'entity_type' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The entity type this data is attached to',
+ ),
+ 'bundle' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+ ),
+ 'deleted' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'A boolean indicating whether this data item has been deleted'
+ ),
+ 'entity_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The entity id this data is attached to',
+ ),
+ 'revision_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => FALSE,
+ 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+ ),
+ // @todo Consider storing language as integer.
+ 'language' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The language for this data item.',
+ ),
+ 'delta' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The sequence number for this data item, used for multi-value fields',
+ ),
+ ),
+ 'primary key' => array('entity_type', 'entity_id', 'deleted', 'delta', 'language'),
+ 'indexes' => array(
+ 'entity_type' => array('entity_type'),
+ 'bundle' => array('bundle'),
+ 'deleted' => array('deleted'),
+ 'entity_id' => array('entity_id'),
+ 'revision_id' => array('revision_id'),
+ 'language' => array('language'),
+ ),
+ );
+
+ $field += array('columns' => array(), 'indexes' => array(), 'foreign keys' => array());
+ // Add field columns.
+ foreach ($field['columns'] as $column_name => $attributes) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $current['fields'][$real_name] = $attributes;
+ }
+
+ // Add indexes.
+ foreach ($field['indexes'] as $index_name => $columns) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
+ foreach ($columns as $column_name) {
+ $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ }
+ }
+
+ // Add foreign keys.
+ 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_name => $referenced) {
+ $sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+ }
+ }
+
+ // Construct the revision table.
+ $revision = $current;
+ $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})";
+ $revision['primary key'] = array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language');
+ $revision['fields']['revision_id']['not null'] = TRUE;
+ $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+ return array(
+ _field_sql_storage_tablename($field) => $current,
+ _field_sql_storage_revision_tablename($field) => $revision,
+ );
+}
+
+/**
+ * Implements hook_field_storage_create_field().
+ */
+function field_sql_storage_field_storage_create_field($field) {
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_update_forbid().
+ *
+ * Forbid any field update that changes column definitions if there is
+ * any data.
+ */
+function field_sql_storage_field_update_forbid($field, $prior_field, $has_data) {
+ if ($has_data && $field['columns'] != $prior_field['columns']) {
+ throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
+ }
+}
+
+/**
+ * Implements hook_field_storage_update_field().
+ */
+function field_sql_storage_field_storage_update_field($field, $prior_field, $has_data) {
+ if (! $has_data) {
+ // There is no data. Re-create the tables completely.
+
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ // If the database supports transactional DDL, we can go ahead and rely
+ // on it. If not, we will have to rollback manually if something fails.
+ $transaction = db_transaction();
+ }
+
+ try {
+ $prior_schema = _field_sql_storage_schema($prior_field);
+ foreach ($prior_schema as $name => $table) {
+ db_drop_table($name, $table);
+ }
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ }
+ catch (Exception $e) {
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ $transaction->rollback();
+ }
+ else {
+ // Recreate tables.
+ $prior_schema = _field_sql_storage_schema($prior_field);
+ foreach ($prior_schema as $name => $table) {
+ if (!db_table_exists($name)) {
+ db_create_table($name, $table);
+ }
+ }
+ }
+ throw $e;
+ }
+ }
+ else {
+ // There is data, so there are no column changes. Drop all the
+ // prior indexes and create all the new ones, except for all the
+ // priors that exist unchanged.
+ $table = _field_sql_storage_tablename($prior_field);
+ $revision_table = _field_sql_storage_revision_tablename($prior_field);
+ foreach ($prior_field['indexes'] as $name => $columns) {
+ if (!isset($field['indexes'][$name]) || $columns != $field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ db_drop_index($table, $real_name);
+ db_drop_index($revision_table, $real_name);
+ }
+ }
+ $table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+ foreach ($field['indexes'] as $name => $columns) {
+ if (!isset($prior_field['indexes'][$name]) || $columns != $prior_field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ $real_columns = array();
+ foreach ($columns as $column_name) {
+ $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ }
+ db_add_index($table, $real_name, $real_columns);
+ db_add_index($revision_table, $real_name, $real_columns);
+ }
+ }
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_storage_delete_field().
+ */
+function field_sql_storage_field_storage_delete_field($field) {
+ // Mark all data associated with the field for deletion.
+ $field['deleted'] = 0;
+ $table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+ db_update($table)
+ ->fields(array('deleted' => 1))
+ ->execute();
+
+ // Move the table to a unique name while the table contents are being deleted.
+ $field['deleted'] = 1;
+ $new_table = _field_sql_storage_tablename($field);
+ $revision_new_table = _field_sql_storage_revision_tablename($field);
+ db_rename_table($table, $new_table);
+ db_rename_table($revision_table, $revision_new_table);
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_storage_load().
+ */
+function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) {
+ $load_current = $age == FIELD_LOAD_CURRENT;
+
+ foreach ($fields as $field_id => $ids) {
+ // 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);
+
+ $query = db_select($table, 't')
+ ->fields('t')
+ ->condition('entity_type', $entity_type)
+ ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+ ->condition('language', field_available_languages($entity_type, $field), 'IN')
+ ->orderBy('delta');
+
+ if (empty($options['deleted'])) {
+ $query->condition('deleted', 0);
+ }
+
+ $results = $query->execute();
+
+ $delta_count = array();
+ foreach ($results as $row) {
+ if (!isset($delta_count[$row->entity_id][$row->language])) {
+ $delta_count[$row->entity_id][$row->language] = 0;
+ }
+
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) {
+ $item = array();
+ // For each column declared by the field, populate the item
+ // from the prefixed database column.
+ foreach ($field['columns'] as $column => $attributes) {
+ $column_name = _field_sql_storage_columnname($field_name, $column);
+ $item[$column] = $row->$column_name;
+ }
+
+ // Add the item to the field values for the entity.
+ $entities[$row->entity_id]->{$field_name}[$row->language][] = $item;
+ $delta_count[$row->entity_id][$row->language]++;
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_write().
+ */
+function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ if (!isset($vid)) {
+ $vid = $id;
+ }
+
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $field_name = $field['field_name'];
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+
+ $all_languages = field_available_languages($entity_type, $field);
+ $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
+
+ // Delete and insert, rather than update, in case a value was added.
+ if ($op == FIELD_STORAGE_UPDATE) {
+ // Delete languages present in the incoming $entity->$field_name.
+ // Delete all languages if $entity->$field_name is empty.
+ $languages = !empty($entity->$field_name) ? $field_languages : $all_languages;
+ if ($languages) {
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ }
+ }
+
+ // Prepare the multi-insert query.
+ $do_insert = FALSE;
+ $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'language');
+ foreach ($field['columns'] as $column => $attributes) {
+ $columns[] = _field_sql_storage_columnname($field_name, $column);
+ }
+ $query = db_insert($table_name)->fields($columns);
+ $revision_query = db_insert($revision_name)->fields($columns);
+
+ foreach ($field_languages as $langcode) {
+ $items = (array) $entity->{$field_name}[$langcode];
+ $delta_count = 0;
+ foreach ($items as $delta => $item) {
+ // We now know we have someting to insert.
+ $do_insert = TRUE;
+ $record = array(
+ 'entity_type' => $entity_type,
+ 'entity_id' => $id,
+ 'revision_id' => $vid,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'language' => $langcode,
+ );
+ foreach ($field['columns'] as $column => $attributes) {
+ $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
+ }
+ $query->values($record);
+ if (isset($vid)) {
+ $revision_query->values($record);
+ }
+
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+ break;
+ }
+ }
+ }
+
+ // Execute the query if we have values to insert.
+ if ($do_insert) {
+ $query->execute();
+ $revision_query->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_delete().
+ *
+ * This function deletes data for all fields for an entity from the database.
+ */
+function field_sql_storage_field_storage_delete($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ if (isset($fields[$instance['field_id']])) {
+ $field = field_info_field_by_id($instance['field_id']);
+ field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_purge().
+ *
+ * This function deletes data from the database for a single field on
+ * an entity.
+ */
+function field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+}
+
+/**
+ * Implements hook_field_storage_query().
+ */
+function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
+ if ($query->age == FIELD_LOAD_CURRENT) {
+ $tablename_function = '_field_sql_storage_tablename';
+ $id_key = 'entity_id';
+ }
+ else {
+ $tablename_function = '_field_sql_storage_revision_tablename';
+ $id_key = 'revision_id';
+ }
+ $table_aliases = array();
+ // 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_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");
+ }
+ else {
+ $select_query = db_select($tablename, $table_alias);
+ // 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.
+ if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) {
+ $select_query->addTag('entity_field_access');
+ }
+ $select_query->addMetaData('base_table', $tablename);
+ $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
+ $field_base_table = $table_alias;
+ }
+ if ($field['cardinality'] != 1 || $field['translatable']) {
+ $select_query->distinct();
+ }
+ }
+
+ // Add field conditions. We need a fresh grouping cache.
+ drupal_static_reset('_field_sql_storage_query_field_conditions');
+ _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldConditions, $table_aliases, '_field_sql_storage_columnname');
+
+ // Add field meta conditions.
+ _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldMetaConditions, $table_aliases, '_field_sql_storage_query_columnname');
+
+ if (isset($query->deleted)) {
+ $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
+ }
+
+ // Is there a need to sort the query by property?
+ $has_property_order = FALSE;
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'property') {
+ $has_property_order = TRUE;
+ }
+ }
+
+ if ($query->propertyConditions || $has_property_order) {
+ if (empty($query->entityConditions['entity_type']['value'])) {
+ throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
+ }
+ $entity_type = $query->entityConditions['entity_type']['value'];
+ $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
+ $query->entityConditions['entity_type']['operator'] = '=';
+ foreach ($query->propertyConditions as $property_condition) {
+ $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
+ }
+ }
+ foreach ($query->entityConditions as $key => $condition) {
+ $query->addCondition($select_query, "$field_base_table.$key", $condition);
+ }
+
+ // Order the query.
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'entity') {
+ $key = $order['specifier'];
+ $select_query->orderBy("$field_base_table.$key", $order['direction']);
+ }
+ elseif ($order['type'] == 'field') {
+ $specifier = $order['specifier'];
+ $field = $specifier['field'];
+ $table_alias = $table_aliases[$specifier['index']];
+ $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
+ $select_query->orderBy($sql_field, $order['direction']);
+ }
+ elseif ($order['type'] == 'property') {
+ $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
+ }
+ }
+
+ return $query->finishQuery($select_query, $id_key);
+}
+
+/**
+ * Adds the base entity table to a field query object.
+ *
+ * @param SelectQuery $select_query
+ * A SelectQuery containing at least one table as specified by
+ * _field_sql_storage_tablename().
+ * @param $entity_type
+ * The entity type for which the base table should be joined.
+ * @param $field_base_table
+ * Name of a table in $select_query. As only INNER JOINs are used, it does
+ * not matter which.
+ *
+ * @return
+ * The name of the entity base table joined in.
+ */
+function _field_sql_storage_query_join_entity(SelectQuery $select_query, $entity_type, $field_base_table) {
+ $entity_info = entity_get_info($entity_type);
+ $entity_base_table = $entity_info['base table'];
+ $entity_field = $entity_info['entity keys']['id'];
+ $select_query->join($entity_base_table, $entity_base_table, "$entity_base_table.$entity_field = $field_base_table.entity_id");
+ return $entity_base_table;
+}
+
+/**
+ * Adds field (meta) conditions to the given query objects respecting groupings.
+ *
+ * @param EntityFieldQuery $query
+ * The field query object to be processed.
+ * @param SelectQuery $select_query
+ * The SelectQuery that should get grouping conditions.
+ * @param condtions
+ * The conditions to be added.
+ * @param $table_aliases
+ * An associative array of table aliases keyed by field index.
+ * @param $column_callback
+ * A callback that should return the column name to be used for the field
+ * conditions. Accepts a field name and a field column name as parameters.
+ */
+function _field_sql_storage_query_field_conditions(EntityFieldQuery $query, SelectQuery $select_query, $conditions, $table_aliases, $column_callback) {
+ $groups = &drupal_static(__FUNCTION__, array());
+ foreach ($conditions as $key => $condition) {
+ $table_alias = $table_aliases[$key];
+ $field = $condition['field'];
+ // Add the specified condition.
+ $sql_field = "$table_alias." . $column_callback($field['field_name'], $condition['column']);
+ $query->addCondition($select_query, $sql_field, $condition);
+ // Add delta / language group conditions.
+ foreach (array('delta', 'language') as $column) {
+ if (isset($condition[$column . '_group'])) {
+ $group_name = $condition[$column . '_group'];
+ if (!isset($groups[$column][$group_name])) {
+ $groups[$column][$group_name] = $table_alias;
+ }
+ else {
+ $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Field meta condition column callback.
+ */
+function _field_sql_storage_query_columnname($field_name, $column) {
+ return $column;
+}
+
+/**
+ * Implements hook_field_storage_delete_revision().
+ *
+ * This function actually deletes the data from the database.
+ */
+function field_sql_storage_field_storage_delete_revision($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ if (isset($vid)) {
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_delete_instance().
+ *
+ * This function simply marks for deletion all data associated with the field.
+ */
+function field_sql_storage_field_storage_delete_instance($instance) {
+ $field = field_info_field($instance['field_name']);
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_update($table_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+ db_update($revision_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+}
+
+/**
+ * Implements hook_field_attach_rename_bundle().
+ */
+function field_sql_storage_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+ // We need to account for deleted or inactive fields and instances.
+ $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+ foreach ($instances as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ if ($field['storage']['type'] == 'field_sql_storage') {
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_update($table_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle_old)
+ ->execute();
+ db_update($revision_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle_old)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_purge_field().
+ *
+ * All field data items and instances have already been purged, so all
+ * that is left is to delete the table.
+ */
+function field_sql_storage_field_storage_purge_field($field) {
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_drop_table($table_name);
+ db_drop_table($revision_name);
+}
+
+/**
+ * Implements hook_field_storage_details().
+ */
+function field_sql_storage_field_storage_details($field) {
+ $details = array();
+ if (!empty($field['columns'])) {
+ // Add field columns.
+ foreach ($field['columns'] as $column_name => $attributes) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $columns[$column_name] = $real_name;
+ }
+ return array(
+ 'sql' => array(
+ FIELD_LOAD_CURRENT => array(
+ _field_sql_storage_tablename($field) => $columns,
+ ),
+ FIELD_LOAD_REVISION => array(
+ _field_sql_storage_revision_tablename($field) => $columns,
+ ),
+ ),
+ );
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test
new file mode 100644
index 0000000..12c54ba
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test
@@ -0,0 +1,441 @@
+<?php
+
+/**
+ * @file
+ * Tests for field_sql_storage.module.
+ *
+ * Field_sql_storage.module implements the default back-end storage plugin
+ * for the Field Strage API.
+ */
+
+/**
+ * Tests field storage.
+ */
+class FieldSqlStorageTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field SQL storage tests',
+ 'description' => "Test field SQL storage module.",
+ 'group' => 'Field API'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_sql_storage', 'field', 'field_test', 'text');
+ $this->field_name = strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
+ $this->field = field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle'
+ );
+ $this->instance = field_create_instance($this->instance);
+ $this->table = _field_sql_storage_tablename($this->field);
+ $this->revision_table = _field_sql_storage_revision_tablename($this->field);
+
+ }
+
+ /**
+ * Uses the mysql tables and records to verify
+ * field_load_revision works correctly.
+ */
+ function testFieldAttachLoad() {
+ $entity_type = 'test_entity';
+ $eid = 0;
+ $langcode = LANGUAGE_NONE;
+
+ $columns = array('entity_type', 'entity_id', 'revision_id', 'delta', 'language', $this->field_name . '_value');
+
+ // Insert data for four revisions to the field revisions table
+ $query = db_insert($this->revision_table)->fields($columns);
+ for ($evid = 0; $evid < 4; ++$evid) {
+ $values[$evid] = array();
+ // Note: we insert one extra value ('<=' instead of '<').
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $value = mt_rand(1, 127);
+ $values[$evid][] = $value;
+ $query->values(array($entity_type, $eid, $evid, $delta, $langcode, $value));
+ }
+ }
+ $query->execute();
+
+ // Insert data for the "most current revision" into the field table
+ $query = db_insert($this->table)->fields($columns);
+ foreach ($values[0] as $delta => $value) {
+ $query->values(array($entity_type, $eid, 0, $delta, $langcode, $value));
+ }
+ $query->execute();
+
+ // Load the "most current revision"
+ $entity = field_test_create_stub_entity($eid, 0, $this->instance['bundle']);
+ field_attach_load($entity_type, array($eid => $entity));
+ foreach ($values[0] as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $value, "Value $delta is loaded correctly for current revision");
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}[$langcode]), "No extraneous value gets loaded for current revision.");
+ }
+ }
+
+ // Load every revision
+ for ($evid = 0; $evid < 4; ++$evid) {
+ $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array($eid => $entity));
+ foreach ($values[$evid] as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $value, "Value $delta for revision $evid is loaded correctly");
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}[$langcode]), "No extraneous value gets loaded for revision $evid.");
+ }
+ }
+ }
+
+ // Add a translation in an unavailable language and verify it is not loaded.
+ $eid = $evid = 1;
+ $unavailable_language = 'xx';
+ $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
+ $values = array($entity_type, $eid, $evid, 0, $unavailable_language, mt_rand(1, 127));
+ db_insert($this->table)->fields($columns)->values($values)->execute();
+ db_insert($this->revision_table)->fields($columns)->values($values)->execute();
+ field_attach_load($entity_type, array($eid => $entity));
+ $this->assertFalse(array_key_exists($unavailable_language, $entity->{$this->field_name}), 'Field translation in an unavailable language ignored');
+ }
+
+ /**
+ * Reads mysql to verify correct data is
+ * written when using insert and update.
+ */
+ function testFieldAttachInsertAndUpdate() {
+ $entity_type = 'test_entity';
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+
+ // Test insert.
+ $values = array();
+ // Note: we try to insert one extra value ('<=' instead of '<').
+ // TODO : test empty values filtering and "compression" (store consecutive deltas).
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $values[$delta]['value'] = mt_rand(1, 127);
+ }
+ $entity->{$this->field_name}[$langcode] = $rev_values[0] = $values;
+ field_attach_insert($entity_type, $entity);
+
+ $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'], format_string("Value %delta is inserted correctly", array('%delta' => $delta)));
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted.");
+ }
+ }
+
+ // Test update.
+ $entity = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
+ $values = array();
+ // Note: we try to update one extra value ('<=' instead of '<').
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $values[$delta]['value'] = mt_rand(1, 127);
+ }
+ $entity->{$this->field_name}[$langcode] = $rev_values[1] = $values;
+ field_attach_update($entity_type, $entity);
+ $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'], format_string("Value %delta is updated correctly", array('%delta' => $delta)));
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated.");
+ }
+ }
+
+ // Check that data for both revisions are in the revision table.
+ // We make sure each value is stored correctly, then unset it.
+ // When an entire revision's values are unset (remembering that we
+ // put one extra value in $values per revision), unset the entire
+ // revision. Then, if $rev_values is empty at the end, all
+ // revision data was found.
+ $results = db_select($this->revision_table, 't')->fields('t')->execute();
+ foreach ($results as $row) {
+ $this->assertEqual($row->{$this->field_name . '_value'}, $rev_values[$row->revision_id][$row->delta]['value'], "Value {$row->delta} for revision {$row->revision_id} stored correctly");
+ unset($rev_values[$row->revision_id][$row->delta]);
+ if (count($rev_values[$row->revision_id]) == 1) {
+ unset($rev_values[$row->revision_id]);
+ }
+ }
+ $this->assertTrue(empty($rev_values), "All values for all revisions are stored in revision table {$this->revision_table}");
+
+ // Check that update leaves the field data untouched if
+ // $entity->{$field_name} is absent.
+ unset($entity->{$this->field_name});
+ field_attach_update($entity_type, $entity);
+ $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'], format_string("Update with no field_name entry leaves value %delta untouched", array('%delta' => $delta)));
+ }
+ }
+
+ // Check that update with an empty $entity->$field_name empties the field.
+ $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, "Update with an empty field_name entry empties the field.");
+ }
+
+ /**
+ * Tests insert and update with missing or NULL fields.
+ */
+ function testFieldAttachSaveMissingData() {
+ $entity_type = 'test_entity';
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+
+ // Insert: Field is missing
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'Missing field results in no inserts');
+
+ // Insert: Field is NULL
+ $entity->{$this->field_name} = NULL;
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'NULL field results in no inserts');
+
+ // Add some real data
+ $entity->{$this->field_name}[$langcode] = array(0 => array('value' => 1));
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Field data saved');
+
+ // Update: Field is missing. Data should survive.
+ unset($entity->{$this->field_name});
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Missing field leaves data in table');
+
+ // Update: Field is NULL. Data should be wiped.
+ $entity->{$this->field_name} = NULL;
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'NULL field leaves no data in table');
+
+ // Add a translation in an unavailable language.
+ $unavailable_language = 'xx';
+ db_insert($this->table)
+ ->fields(array('entity_type', 'bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'language'))
+ ->values(array($entity_type, $this->instance['bundle'], 0, 0, 0, 0, $unavailable_language))
+ ->execute();
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Field translation in an unavailable language saved.');
+
+ // Again add some real data.
+ $entity->{$this->field_name}[$langcode] = array(0 => array('value' => 1));
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 2, 'Field data saved.');
+
+ // Update: Field translation is missing but field is not empty. Translation
+ // data should survive.
+ $entity->{$this->field_name}[$unavailable_language] = array(mt_rand(1, 127));
+ unset($entity->{$this->field_name}[$langcode]);
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 2, 'Missing field translation leaves data in table.');
+
+ // Update: Field translation is NULL but field is not empty. Translation
+ // data should be wiped.
+ $entity->{$this->field_name}[$langcode] = NULL;
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'NULL field translation is wiped.');
+ }
+
+ /**
+ * Test trying to update a field with data.
+ */
+ function testUpdateFieldSchemaWithData() {
+ // Create a decimal 5.2 field and add some data.
+ $field = array('field_name' => 'decimal52', 'type' => 'number_decimal', 'settings' => array('precision' => 5, 'scale' => 2));
+ $field = field_create_field($field);
+ $instance = array('field_name' => 'decimal52', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
+ $instance = field_create_instance($instance);
+ $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+ $entity->decimal52[LANGUAGE_NONE][0]['value'] = '1.235';
+ field_attach_insert('test_entity', $entity);
+
+ // Attempt to update the field in a way that would work without data.
+ $field['settings']['scale'] = 3;
+ try {
+ field_update_field($field);
+ $this->fail(t('Cannot update field schema with data.'));
+ }
+ catch (FieldException $e) {
+ $this->pass(t('Cannot update field schema with data.'));
+ }
+ }
+
+ /**
+ * Test that failure to create fields is handled gracefully.
+ */
+ function testFieldUpdateFailure() {
+ // Create a text field.
+ $field = array('field_name' => 'test_text', 'type' => 'text', 'settings' => array('max_length' => 255));
+ $field = field_create_field($field);
+
+ // Attempt to update the field in a way that would break the storage.
+ $prior_field = $field;
+ $field['settings']['max_length'] = -1;
+ try {
+ field_update_field($field);
+ $this->fail(t('Update succeeded.'));
+ }
+ catch (Exception $e) {
+ $this->pass(t('Update properly failed.'));
+ }
+
+ // 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), format_string('Table %table exists.', array('%table' => $table_name)));
+ }
+ }
+
+ /**
+ * Test adding and removing indexes while data is present.
+ */
+ function testFieldUpdateIndexesWithData() {
+
+ // Create a decimal field.
+ $field_name = 'testfield';
+ $field = array('field_name' => $field_name, 'type' => 'text');
+ $field = field_create_field($field);
+ $instance = array('field_name' => $field_name, 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
+ $instance = field_create_instance($instance);
+ $tables = array(_field_sql_storage_tablename($field), _field_sql_storage_revision_tablename($field));
+
+ // Verify the indexes we will create do not exist yet.
+ foreach ($tables as $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.
+ $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+ $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = 'field data';
+ field_attach_insert('test_entity', $entity);
+
+ // Add an index
+ $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"), 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"), 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', "Index changes performed without dropping the tables");
+ }
+
+ /**
+ * Test the storage details.
+ */
+ function testFieldStorageDetails() {
+ $current = _field_sql_storage_tablename($this->field);
+ $revision = _field_sql_storage_revision_tablename($this->field);
+
+ // Retrieve the field and instance with field_info so the storage details are attached.
+ $field = field_info_field($this->field['field_name']);
+ $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']), '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]), '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, 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)));
+ }
+ }
+
+ /**
+ * Test foreign key support.
+ */
+ function testFieldSqlStorageForeignKeys() {
+ // Create a 'shape' field, with a configurable foreign key (see
+ // field_test_field_schema()).
+ $field_name = 'testfield';
+ $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'][$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), TRUE);
+ $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
+ $foreign_key = reset($schema['foreign keys']);
+ $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');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info
new file mode 100644
index 0000000..2aef100
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info
@@ -0,0 +1,14 @@
+name = List
+description = Defines list field types. Use with Options to create selection lists.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+dependencies[] = options
+files[] = tests/list.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install
new file mode 100644
index 0000000..2386f04
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the list module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function list_field_schema($field) {
+ switch ($field['type']) {
+ case 'list_text':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ case 'list_float':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'float',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ case 'list_integer':
+ case 'list_boolean':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'int',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ }
+ return array(
+ 'columns' => $columns,
+ 'indexes' => array(
+ 'value' => array('value'),
+ ),
+ );
+}
+
+/**
+ * Rename the list field types and change 'allowed_values' format.
+ */
+function list_update_7001() {
+ $fields = _update_7000_field_read_fields(array('module' => 'list'));
+ foreach ($fields as $field) {
+ $update = array();
+
+ // Translate the old string format into the new array format.
+ $allowed_values = $field['settings']['allowed_values'];
+ if (is_string($allowed_values)) {
+ $position_keys = ($field['type'] == 'list');
+ $allowed_values = _list_update_7001_extract_allowed_values($allowed_values, $position_keys);
+
+ // Additionally, float keys need to be disambiguated ('.5' is '0.5').
+ if ($field['type'] == 'list_number' && !empty($allowed_values)) {
+ $keys = array_map(create_function('$a', 'return (string) (float) $a;'), array_keys($allowed_values));
+ $allowed_values = array_combine($keys, array_values($allowed_values));
+ }
+
+ // Place the new setting in the existing serialized 'data' column.
+ $data = db_query("SELECT data FROM {field_config} WHERE id = :id", array(':id' => $field['id']))->fetchField();
+ $data = unserialize($data);
+ $data['settings']['allowed_values'] = $allowed_values;
+ $update['data'] = serialize($data);
+ }
+
+ // Rename field types.
+ $types = array('list' => 'list_integer', 'list_number' => 'list_float');
+ if (isset($types[$field['type']])) {
+ $update['type'] = $types[$field['type']];
+ }
+
+ // Save the new data.
+ if ($update) {
+ $query = db_update('field_config')
+ ->condition('id', $field['id'])
+ ->fields($update)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Helper function for list_update_7001: extract allowed values from a string.
+ *
+ * This reproduces the parsing logic in use before D7 RC2.
+ */
+function _list_update_7001_extract_allowed_values($string, $position_keys) {
+ $values = array();
+
+ $list = explode("\n", $string);
+ $list = array_map('trim', $list);
+ $list = array_filter($list, 'strlen');
+
+ foreach ($list as $key => $value) {
+ // Check for a manually specified key.
+ if (strpos($value, '|') !== FALSE) {
+ list($key, $value) = explode('|', $value);
+ }
+ // Otherwise see if we need to use the value as the key. The "list" type
+ // will automatically convert non-keyed lines to integers.
+ elseif (!$position_keys) {
+ $key = $value;
+ }
+ $values[$key] = (isset($value) && $value !== '') ? $value : $key;
+ }
+
+ return $values;
+}
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Re-apply list_update_7001() for deleted fields.
+ */
+function list_update_7002() {
+ // See http://drupal.org/node/1022924: list_update_7001() intitally
+ // overlooked deleted fields, which then caused fatal errors when the fields
+ // were being purged.
+ // list_update_7001() has the required checks to ensure it is reentrant, so
+ // it can simply be executed once more..
+ list_update_7001();
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module
new file mode 100644
index 0000000..634c134
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module
@@ -0,0 +1,486 @@
+<?php
+
+/**
+ * @file
+ * Defines list field types that can be used with the Options module.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function list_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#list':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The List module defines various fields for storing a list of items, for use with the Field module. Usually these items are entered through a select list, checkboxes, or radio buttons. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ */
+function list_field_info() {
+ return array(
+ 'list_integer' => array(
+ 'label' => t('List (integer)'),
+ 'description' => t("This field stores integer values from a list of allowed 'value => label' pairs, i.e. 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_float' => array(
+ 'label' => t('List (float)'),
+ 'description' => t("This field stores float values from a list of allowed 'value => label' pairs, i.e. 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_text' => array(
+ 'label' => t('List (text)'),
+ 'description' => t("This field stores text values from a list of allowed 'value => label' pairs, i.e. 'US States': IL => Illinois, IA => Iowa, IN => Indiana."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_boolean' => array(
+ 'label' => t('Boolean'),
+ 'description' => t('This field stores simple on/off or yes/no options.'),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_buttons',
+ 'default_formatter' => 'list_default',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function list_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+
+ switch ($field['type']) {
+ case 'list_integer':
+ case 'list_float':
+ case 'list_text':
+ $form['allowed_values'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Allowed values list'),
+ '#default_value' => list_allowed_values_string($settings['allowed_values']),
+ '#rows' => 10,
+ '#element_validate' => array('list_allowed_values_setting_validate'),
+ '#field_has_data' => $has_data,
+ '#field' => $field,
+ '#field_type' => $field['type'],
+ '#access' => empty($settings['allowed_values_function']),
+ );
+
+ $description = '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label.');
+ if ($field['type'] == 'list_integer' || $field['type'] == 'list_float') {
+ $description .= '<br/>' . t('The key is the stored value, and must be numeric. The label will be used in displayed values and edit forms.');
+ $description .= '<br/>' . t('The label is optional: if a line contains a single number, it will be used as key and label.');
+ $description .= '<br/>' . t('Lists of labels are also accepted (one label per line), only if the field does not hold any values yet. Numeric keys will be automatically generated from the positions in the list.');
+ }
+ else {
+ $description .= '<br/>' . t('The key is the stored value. The label will be used in displayed values and edit forms.');
+ $description .= '<br/>' . t('The label is optional: if a line contains a single string, it will be used as key and label.');
+ }
+ $description .= '</p>';
+ $form['allowed_values']['#description'] = $description;
+
+ break;
+
+ case 'list_boolean':
+ $values = $settings['allowed_values'];
+ $off_value = array_shift($values);
+ $on_value = array_shift($values);
+
+ $form['allowed_values'] = array(
+ '#type' => 'value',
+ '#description' => '',
+ '#value_callback' => 'list_boolean_allowed_values_callback',
+ '#access' => empty($settings['allowed_values_function']),
+ );
+ $form['allowed_values']['on'] = array(
+ '#type' => 'textfield',
+ '#title' => t('On value'),
+ '#default_value' => $on_value,
+ '#required' => FALSE,
+ '#description' => t('If left empty, "1" will be used.'),
+ // Change #parents to make sure the element is not saved into field
+ // settings.
+ '#parents' => array('on'),
+ );
+ $form['allowed_values']['off'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Off value'),
+ '#default_value' => $off_value,
+ '#required' => FALSE,
+ '#description' => t('If left empty, "0" will be used.'),
+ // Change #parents to make sure the element is not saved into field
+ // settings.
+ '#parents' => array('off'),
+ );
+
+ // Link the allowed value to the on / off elements to prepare for the rare
+ // case of an alter changing #parents.
+ $form['allowed_values']['#on_parents'] = &$form['allowed_values']['on']['#parents'];
+ $form['allowed_values']['#off_parents'] = &$form['allowed_values']['off']['#parents'];
+
+ break;
+ }
+
+ // Alter the description for allowed values depending on the widget type.
+ if ($instance['widget']['type'] == 'options_onoff') {
+ $form['allowed_values']['#description'] .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>';
+ }
+ elseif ($instance['widget']['type'] == 'options_buttons') {
+ $form['allowed_values']['#description'] .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the <em>Number of values</em> option is greater than 1 for this field, otherwise radios will be displayed.") . '</p>';
+ }
+ $form['allowed_values']['#description'] .= '<p>' . t('Allowed HTML tags in labels: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())) . '</p>';
+
+ $form['allowed_values_function'] = array(
+ '#type' => 'value',
+ '#value' => $settings['allowed_values_function'],
+ );
+ $form['allowed_values_function_display'] = array(
+ '#type' => 'item',
+ '#title' => t('Allowed values list'),
+ '#markup' => t('The value of this field is being determined by the %function function and may not be changed.', array('%function' => $settings['allowed_values_function'])),
+ '#access' => !empty($settings['allowed_values_function']),
+ );
+
+ return $form;
+}
+
+/**
+ * Element validate callback; check that the entered values are valid.
+ */
+function list_allowed_values_setting_validate($element, &$form_state) {
+ $field = $element['#field'];
+ $has_data = $element['#field_has_data'];
+ $field_type = $field['type'];
+ $generate_keys = ($field_type == 'list_integer' || $field_type == 'list_float') && !$has_data;
+
+ $values = list_extract_allowed_values($element['#value'], $field['type'], $generate_keys);
+
+ if (!is_array($values)) {
+ form_error($element, t('Allowed values list: invalid input.'));
+ }
+ else {
+ // Check that keys are valid for the field type.
+ foreach ($values as $key => $value) {
+ if ($field_type == 'list_integer' && !preg_match('/^-?\d+$/', $key)) {
+ form_error($element, t('Allowed values list: keys must be integers.'));
+ break;
+ }
+ if ($field_type == 'list_float' && !is_numeric($key)) {
+ form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
+ break;
+ }
+ elseif ($field_type == 'list_text' && drupal_strlen($key) > 255) {
+ form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
+ break;
+ }
+ }
+
+ // Prevent removing values currently in use.
+ if ($has_data) {
+ $lost_keys = array_diff(array_keys($field['settings']['allowed_values']), array_keys($values));
+ if (_list_values_in_use($field, $lost_keys)) {
+ form_error($element, t('Allowed values list: some values are being removed while currently in use.'));
+ }
+ }
+
+ form_set_value($element, $values, $form_state);
+ }
+}
+
+/**
+* Form element #value_callback: assembles the allowed values for 'boolean' fields.
+*/
+function list_boolean_allowed_values_callback($element, $input, $form_state) {
+ $on = drupal_array_get_nested_value($form_state['input'], $element['#on_parents']);
+ $off = drupal_array_get_nested_value($form_state['input'], $element['#off_parents']);
+ return array($off, $on);
+}
+
+/**
+ * Implements hook_field_update_field().
+ */
+function list_field_update_field($field, $prior_field, $has_data) {
+ drupal_static_reset('list_allowed_values');
+}
+
+/**
+ * Returns the array of allowed values for a list field.
+ *
+ * The strings are not safe for output. Keys and values of the array should be
+ * sanitized through field_filter_xss() before being displayed.
+ *
+ * @param $field
+ * The field definition.
+ * @param $instance
+ * (optional) A field instance array. Defaults to NULL.
+ * @param $entity_type
+ * (optional) The type of entity; e.g. 'node' or 'user'. Defaults to NULL.
+ * @param $entity
+ * (optional) The entity object. Defaults to NULL.
+ *
+ * @return
+ * The array of allowed values. Keys of the array are the raw stored values
+ * (number or text), values of the array are the display labels.
+ */
+function list_allowed_values($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+ $allowed_values = &drupal_static(__FUNCTION__, array());
+
+ if (!isset($allowed_values[$field['id']])) {
+ $function = $field['settings']['allowed_values_function'];
+ // If $cacheable is FALSE, then the allowed values are not statically
+ // cached. See list_test_dynamic_values_callback() for an example of
+ // generating dynamic and uncached values.
+ $cacheable = TRUE;
+ if (!empty($function) && function_exists($function)) {
+ $values = $function($field, $instance, $entity_type, $entity, $cacheable);
+ }
+ else {
+ $values = $field['settings']['allowed_values'];
+ }
+
+ if ($cacheable) {
+ $allowed_values[$field['id']] = $values;
+ }
+ else {
+ return $values;
+ }
+ }
+
+ return $allowed_values[$field['id']];
+}
+
+/**
+ * Parses a string of 'allowed values' into an array.
+ *
+ * @param $string
+ * The list of allowed values in string format described in
+ * list_allowed_values_string().
+ * @param $field_type
+ * The field type. Either 'list_number' or 'list_text'.
+ * @param $generate_keys
+ * Boolean value indicating whether to generate keys based on the position of
+ * the value if a key is not manually specified, and if the value cannot be
+ * used as a key. This should only be TRUE for fields of type 'list_number'.
+ *
+ * @return
+ * The array of extracted key/value pairs, or NULL if the string is invalid.
+ *
+ * @see list_allowed_values_string()
+ */
+function list_extract_allowed_values($string, $field_type, $generate_keys) {
+ $values = array();
+
+ $list = explode("\n", $string);
+ $list = array_map('trim', $list);
+ $list = array_filter($list, 'strlen');
+
+ $generated_keys = $explicit_keys = FALSE;
+ foreach ($list as $position => $text) {
+ $value = $key = FALSE;
+
+ // Check for an explicit key.
+ $matches = array();
+ if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
+ $key = $matches[1];
+ $value = $matches[2];
+ $explicit_keys = TRUE;
+ }
+ // Otherwise see if we can use the value as the key. Detecting true integer
+ // strings takes a little trick.
+ elseif ($field_type == 'list_text'
+ || ($field_type == 'list_float' && is_numeric($text))
+ || ($field_type == 'list_integer' && is_numeric($text) && (float) $text == intval($text))) {
+ $key = $value = $text;
+ $explicit_keys = TRUE;
+ }
+ // Otherwise see if we can generate a key from the position.
+ elseif ($generate_keys) {
+ $key = (string) $position;
+ $value = $text;
+ $generated_keys = TRUE;
+ }
+ else {
+ return;
+ }
+
+ // Float keys are represented as strings and need to be disambiguated
+ // ('.5' is '0.5').
+ if ($field_type == 'list_float' && is_numeric($key)) {
+ $key = (string) (float) $key;
+ }
+
+ $values[$key] = $value;
+ }
+
+ // We generate keys only if the list contains no explicit key at all.
+ if ($explicit_keys && $generated_keys) {
+ return;
+ }
+
+ return $values;
+}
+
+/**
+ * Generates a string representation of an array of 'allowed values'.
+ *
+ * This string format is suitable for edition in a textarea.
+ *
+ * @param $values
+ * An array of values, where array keys are values and array values are
+ * labels.
+ *
+ * @return
+ * The string representation of the $values array:
+ * - Values are separated by a carriage return.
+ * - Each value is in the format "value|label" or "value".
+ */
+function list_allowed_values_string($values) {
+ $lines = array();
+ foreach ($values as $key => $value) {
+ $lines[] = "$key|$value";
+ }
+ return implode("\n", $lines);
+}
+
+/**
+ * Implements hook_field_update_forbid().
+ */
+function list_field_update_forbid($field, $prior_field, $has_data) {
+ if ($field['module'] == 'list' && $has_data) {
+ // Forbid any update that removes allowed values with actual data.
+ $lost_keys = array_diff(array_keys($prior_field['settings']['allowed_values']), array_keys($field['settings']['allowed_values']));
+ if (_list_values_in_use($field, $lost_keys)) {
+ throw new FieldUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field['field_name'])));
+ }
+ }
+}
+
+/**
+ * Checks if a list of values are being used in actual field values.
+ */
+function _list_values_in_use($field, $values) {
+ if ($values) {
+ $query = new EntityFieldQuery();
+ $found = $query
+ ->fieldCondition($field['field_name'], 'value', $values)
+ ->range(0, 1)
+ ->execute();
+ return !empty($found);
+ }
+
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'list_illegal_value': The value is not part of the list of allowed values.
+ */
+function list_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ if (!empty($item['value'])) {
+ if (!empty($allowed_values) && !isset($allowed_values[$item['value']])) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'list_illegal_value',
+ 'message' => t('%name: illegal value.', array('%name' => $instance['label'])),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function list_field_is_empty($item, $field) {
+ if (empty($item['value']) && (string) $item['value'] !== '0') {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_widget_info_alter().
+ *
+ * The List module does not implement widgets of its own, but reuses the
+ * widgets defined in options.module.
+ *
+ * @see list_options_list()
+ */
+function list_field_widget_info_alter(&$info) {
+ $widgets = array(
+ 'options_select' => array('list_integer', 'list_float', 'list_text'),
+ 'options_buttons' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ 'options_onoff' => array('list_boolean'),
+ );
+
+ foreach ($widgets as $widget => $field_types) {
+ $info[$widget]['field types'] = array_merge($info[$widget]['field types'], $field_types);
+ }
+}
+
+/**
+ * Implements hook_options_list().
+ */
+function list_options_list($field, $instance, $entity_type, $entity) {
+ return list_allowed_values($field, $instance, $entity_type, $entity);
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function list_field_formatter_info() {
+ return array(
+ 'list_default' => array(
+ 'label' => t('Default'),
+ 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ ),
+ 'list_key' => array(
+ 'label' => t('Key'),
+ 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function list_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+
+ switch ($display['type']) {
+ case 'list_default':
+ $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ if (isset($allowed_values[$item['value']])) {
+ $output = field_filter_xss($allowed_values[$item['value']]);
+ }
+ else {
+ // If no match was found in allowed values, fall back to the key.
+ $output = field_filter_xss($item['value']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'list_key':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => field_filter_xss($item['value']));
+ }
+ break;
+ }
+
+ return $element;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test
new file mode 100644
index 0000000..84de7e8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test
@@ -0,0 +1,457 @@
+<?php
+
+/**
+ * @file
+ * Tests for list.module.
+ */
+
+/**
+ * Tests for the 'List' field types.
+ */
+class ListFieldTestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field',
+ 'description' => 'Test the List field type.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+
+ $this->field_name = 'test_list';
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'list_integer',
+ 'cardinality' => 1,
+ 'settings' => array(
+ 'allowed_values' => array(1 => 'One', 2 => 'Two', 3 => 'Three'),
+ ),
+ );
+ $this->field = field_create_field($this->field);
+
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $this->instance = field_create_instance($this->instance);
+ }
+
+ /**
+ * Test that allowed values can be updated.
+ */
+ function testUpdateAllowedValues() {
+ $langcode = LANGUAGE_NONE;
+
+ // 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]), '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.
+ $entity = field_test_create_stub_entity();
+ $entity->{$this->field_name}[$langcode][0] = array('value' => 1);
+ field_test_entity_save($entity);
+ $this->field['settings']['allowed_values'] = array(2 => 'Two');
+ try {
+ field_update_field($this->field);
+ $this->fail(t('Cannot update a list field to not include keys with existing data.'));
+ }
+ catch (FieldException $e) {
+ $this->pass(t('Cannot update a list field to not include keys with existing data.'));
+ }
+ // Empty the value, so that we can actually remove the option.
+ $entity->{$this->field_name}[$langcode] = array();
+ field_test_entity_save($entity);
+
+ // Removed options do not appear.
+ $this->field['settings']['allowed_values'] = array(2 => 'Two');
+ 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]), '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]), '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);
+ unset($this->field['id']);
+ $this->field['settings']['allowed_values'] = array(1 => 'One', 2 => 'Two', 3 => 'Three');
+ $this->field = field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $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]), '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');
+ }
+}
+
+/**
+ * Sets up a List field for testing allowed values functions.
+ */
+class ListDynamicValuesTestCase extends FieldTestCase {
+ function setUp() {
+ parent::setUp(array('list', 'field_test', 'list_test'));
+
+ $this->field_name = 'test_list';
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'list_text',
+ 'cardinality' => 1,
+ 'settings' => array(
+ 'allowed_values_function' => 'list_test_dynamic_values_callback',
+ ),
+ );
+ $this->field = field_create_field($this->field);
+
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'required' => TRUE,
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $this->instance = field_create_instance($this->instance);
+ $this->test = array(
+ 'id' => mt_rand(1, 10),
+ // Make sure this does not equal the ID so that
+ // list_test_dynamic_values_callback() always returns 4 values.
+ 'vid' => mt_rand(20, 30),
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName(),
+ );
+ $this->entity = call_user_func_array('field_test_create_stub_entity', $this->test);
+ }
+}
+
+/**
+ * Tests the List field allowed values function.
+ */
+class ListDynamicValuesValidationTestCase extends ListDynamicValuesTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field dynamic values',
+ 'description' => 'Test the List field allowed values function.',
+ 'group' => 'Field types',
+ );
+ }
+
+ /**
+ * Test that allowed values function gets the entity.
+ */
+ function testDynamicAllowedValues() {
+ // Verify that the test passes against every value we had.
+ foreach ($this->test as $key => $value) {
+ $this->entity->test_list[LANGUAGE_NONE][0]['value'] = $value;
+ try {
+ field_attach_validate('test_entity', $this->entity);
+ $this->pass("$key should pass");
+ }
+ catch (FieldValidationException $e) {
+ // This will display as an exception, no need for a separate error.
+ throw($e);
+ }
+ }
+ // Now verify that the test does not pass against anything else.
+ foreach ($this->test as $key => $value) {
+ $this->entity->test_list[LANGUAGE_NONE][0]['value'] = is_numeric($value) ? (100 - $value) : ('X' . $value);
+ $pass = FALSE;
+ try {
+ field_attach_validate('test_entity', $this->entity);
+ }
+ catch (FieldValidationException $e) {
+ $pass = TRUE;
+ }
+ $this->assertTrue($pass, $key . ' should not pass');
+ }
+ }
+}
+
+/**
+ * List module UI tests.
+ */
+class ListFieldUITestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field UI',
+ 'description' => 'Test the List field UI functionality.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test', 'field_ui');
+
+ // Create test user.
+ $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Create content type, with underscores.
+ $type_name = 'test_' . strtolower($this->randomName());
+ $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+ $this->type = $type->type;
+ // Store a valid URL name, with hyphens instead of underscores.
+ $this->hyphen_type = str_replace('_', '-', $this->type);
+ }
+
+ /**
+ * List (integer) : test 'allowed values' input.
+ */
+ function testListAllowedValuesInteger() {
+ $this->field_name = 'field_list_integer';
+ $this->createListField('list_integer');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $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, '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, 'Values can be added and removed.');
+ // Non-integer keys.
+ $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', 'Mixed lists are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => 1))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that a flat list of values is 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, 'Values can be added.');
+ $string = "0|Zero\n1|One";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $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, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (float) : test 'allowed values' input.
+ */
+ function testListAllowedValuesFloat() {
+ $this->field_name = 'field_list_float';
+ $this->createListField('list_float');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $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, '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, 'Values can be added and removed.');
+ // Non-numeric keys.
+ $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', 'Mixed lists are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => .5))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that a flat list of values is 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, 'Values can be added.');
+ $string = "0|Zero\n.5|Point five";
+ $array = array('0' => 'Zero', '0.5' => 'Point five');
+ $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, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (text) : test 'allowed values' input.
+ */
+ function testListAllowedValuesText() {
+ $this->field_name = 'field_list_text';
+ $this->createListField('list_text');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $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, '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, '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, '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', 'Overly long keys are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => 'One'))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that flat lists of values are still accepted once the field has
+ // data.
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $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, 'Values can be added.');
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $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, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (boolen) : test 'On/Off' values input.
+ */
+ function testListAllowedValuesBoolean() {
+ $this->field_name = 'field_list_boolean';
+ $this->createListField('list_boolean');
+
+ // Check that the separate 'On' and 'Off' form fields work.
+ $on = $this->randomName();
+ $off = $this->randomName();
+ $allowed_values = array(1 => $on, 0 => $off);
+ $edit = array(
+ 'on' => $on,
+ 'off' => $off,
+ );
+ $this->drupalPost($this->admin_path, $edit, t('Save settings'));
+ $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, "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, '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');
+ }
+
+ /**
+ * Helper function to create list field of a given type.
+ *
+ * @param string $type
+ * 'list_integer', 'list_float', 'list_text' or 'list_boolean'
+ */
+ protected function createListField($type) {
+ // Create a test field and instance.
+ $field = array(
+ 'field_name' => $this->field_name,
+ 'type' => $type,
+ );
+ field_create_field($field);
+ $instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'node',
+ 'bundle' => $this->type,
+ );
+ field_create_instance($instance);
+
+ $this->admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $this->field_name;
+ }
+
+ /**
+ * Tests a string input for the 'allowed values' form element.
+ *
+ * @param $input_string
+ * The input string, in the pipe-linefeed format expected by the form
+ * element.
+ * @param $result
+ * Either an expected resulting array in
+ * $field['settings']['allowed_values'], or an expected error message.
+ * @param $message
+ * Message to display.
+ */
+ function assertAllowedValuesInput($input_string, $result, $message) {
+ $edit = array('field[settings][allowed_values]' => $input_string);
+ $this->drupalPost($this->admin_path, $edit, t('Save settings'));
+
+ if (is_string($result)) {
+ $this->assertText($result, $message);
+ }
+ else {
+ field_info_cache_clear();
+ $field = field_info_field($this->field_name);
+ $this->assertIdentical($field['settings']['allowed_values'], $result, $message);
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info
new file mode 100644
index 0000000..1861cdc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info
@@ -0,0 +1,12 @@
+name = "List test"
+description = "Support module for the List module tests."
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module
new file mode 100644
index 0000000..aa53337
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Helper module for the List module tests.
+ */
+
+/**
+ * Allowed values callback.
+ */
+function list_test_allowed_values_callback($field) {
+ $values = array(
+ 'Group 1' => array(
+ 0 => 'Zero',
+ ),
+ 1 => 'One',
+ 'Group 2' => array(
+ 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
+ ),
+ );
+
+ return $values;
+}
+
+/**
+ * An entity-bound allowed values callback.
+ */
+function list_test_dynamic_values_callback($field, $instance, $entity_type, $entity, &$cacheable) {
+ $cacheable = FALSE;
+ // We need the values of the entity as keys.
+ return drupal_map_assoc(array_merge(array($entity->ftlabel), entity_extract_ids($entity_type, $entity)));
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info
new file mode 100644
index 0000000..df198b8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info
@@ -0,0 +1,13 @@
+name = Number
+description = Defines numeric field types.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = number.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install
new file mode 100644
index 0000000..02c7a30
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the number module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function number_field_schema($field) {
+ switch ($field['type']) {
+ case 'number_integer' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'int',
+ 'not null' => FALSE
+ ),
+ );
+ break;
+
+ case 'number_float' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'float',
+ 'not null' => FALSE
+ ),
+ );
+ break;
+
+ case 'number_decimal' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'numeric',
+ 'precision' => $field['settings']['precision'],
+ 'scale' => $field['settings']['scale'],
+ 'not null' => FALSE
+ ),
+ );
+ break;
+ }
+ return array(
+ 'columns' => $columns,
+ );
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module
new file mode 100644
index 0000000..6046544
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module
@@ -0,0 +1,419 @@
+<?php
+
+/**
+ * @file
+ * Defines numeric field types.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function number_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#number':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Number module defines various numeric field types for the Field module. Numbers can be in integer, decimal, or floating-point form, and they can be formatted when displayed. Number fields can be limited to a specific set of input values or to a range of values. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ */
+function number_field_info() {
+ return array(
+ 'number_integer' => array(
+ 'label' => t('Integer'),
+ 'description' => t('This field stores a number in the database as an integer.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_integer',
+ ),
+ 'number_decimal' => array(
+ 'label' => t('Decimal'),
+ 'description' => t('This field stores a number in the database in a fixed decimal format.'),
+ 'settings' => array('precision' => 10, 'scale' => 2, 'decimal_separator' => '.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_decimal',
+ ),
+ 'number_float' => array(
+ 'label' => t('Float'),
+ 'description' => t('This field stores a number in the database in a floating point format.'),
+ 'settings' => array('decimal_separator' => '.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_decimal',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function number_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+ $form = array();
+
+ if ($field['type'] == 'number_decimal') {
+ $form['precision'] = array(
+ '#type' => 'select',
+ '#title' => t('Precision'),
+ '#options' => drupal_map_assoc(range(10, 32)),
+ '#default_value' => $settings['precision'],
+ '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
+ '#disabled' => $has_data,
+ );
+ $form['scale'] = array(
+ '#type' => 'select',
+ '#title' => t('Scale'),
+ '#options' => drupal_map_assoc(range(0, 10)),
+ '#default_value' => $settings['scale'],
+ '#description' => t('The number of digits to the right of the decimal.'),
+ '#disabled' => $has_data,
+ );
+ }
+ if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+ $form['decimal_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Decimal marker'),
+ '#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
+ '#default_value' => $settings['decimal_separator'],
+ '#description' => t('The character users will input to mark the decimal point in forms.'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function number_field_instance_settings_form($field, $instance) {
+ $settings = $instance['settings'];
+
+ $form['min'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Minimum'),
+ '#default_value' => $settings['min'],
+ '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
+ '#element_validate' => array('element_validate_number'),
+ );
+ $form['max'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum'),
+ '#default_value' => $settings['max'],
+ '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
+ '#element_validate' => array('element_validate_number'),
+ );
+ $form['prefix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Prefix'),
+ '#default_value' => $settings['prefix'],
+ '#size' => 60,
+ '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+ );
+ $form['suffix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Suffix'),
+ '#default_value' => $settings['suffix'],
+ '#size' => 60,
+ '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+ );
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'number_min': The value is less than the allowed minimum value.
+ * - 'number_max': The value is greater than the allowed maximum value.
+ */
+function number_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ foreach ($items as $delta => $item) {
+ if ($item['value'] != '') {
+ if (is_numeric($instance['settings']['min']) && $item['value'] < $instance['settings']['min']) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'number_min',
+ 'message' => t('%name: the value may be no less than %min.', array('%name' => $instance['label'], '%min' => $instance['settings']['min'])),
+ );
+ }
+ if (is_numeric($instance['settings']['max']) && $item['value'] > $instance['settings']['max']) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'number_max',
+ 'message' => t('%name: the value may be no greater than %max.', array('%name' => $instance['label'], '%max' => $instance['settings']['max'])),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_presave().
+ */
+function number_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ if ($field['type'] == 'number_decimal') {
+ // Let PHP round the value to ensure consistent behavior across storage
+ // backends.
+ foreach ($items as $delta => $item) {
+ if (isset($item['value'])) {
+ $items[$delta]['value'] = round($item['value'], $field['settings']['scale']);
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function number_field_is_empty($item, $field) {
+ if (empty($item['value']) && (string) $item['value'] !== '0') {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function number_field_formatter_info() {
+ return array(
+ // The 'Default' formatter is different for integer fields on the one hand,
+ // and for decimal and float fields on the other hand, in order to be able
+ // to use different default values for the settings.
+ 'number_integer' => array(
+ 'label' => t('Default'),
+ 'field types' => array('number_integer'),
+ 'settings' => array(
+ 'thousand_separator' => ' ',
+ // The 'decimal_separator' and 'scale' settings are not configurable
+ // through the UI, and will therefore keep their default values. They
+ // are only present so that the 'number_integer' and 'number_decimal'
+ // formatters can use the same code.
+ 'decimal_separator' => '.',
+ 'scale' => 0,
+ 'prefix_suffix' => TRUE,
+ ),
+ ),
+ 'number_decimal' => array(
+ 'label' => t('Default'),
+ 'field types' => array('number_decimal', 'number_float'),
+ 'settings' => array(
+ 'thousand_separator' => ' ',
+ 'decimal_separator' => '.',
+ 'scale' => 2,
+ 'prefix_suffix' => TRUE,
+ ),
+ ),
+ 'number_unformatted' => array(
+ 'label' => t('Unformatted'),
+ 'field types' => array('number_integer', 'number_decimal', 'number_float'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function number_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
+ $options = array(
+ '' => t('<none>'),
+ '.' => t('Decimal point'),
+ ',' => t('Comma'),
+ ' ' => t('Space'),
+ );
+ $element['thousand_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Thousand marker'),
+ '#options' => $options,
+ '#default_value' => $settings['thousand_separator'],
+ );
+
+ if ($display['type'] == 'number_decimal') {
+ $element['decimal_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Decimal marker'),
+ '#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
+ '#default_value' => $settings['decimal_separator'],
+ );
+ $element['scale'] = array(
+ '#type' => 'select',
+ '#title' => t('Scale'),
+ '#options' => drupal_map_assoc(range(0, 10)),
+ '#default_value' => $settings['scale'],
+ '#description' => t('The number of digits to the right of the decimal.'),
+ );
+ }
+
+ $element['prefix_suffix'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Display prefix and suffix.'),
+ '#default_value' => $settings['prefix_suffix'],
+ );
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function number_field_formatter_settings_summary($field, $instance, $view_mode) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $summary = array();
+ if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
+ $summary[] = number_format(1234.1234567890, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']);
+ if ($settings['prefix_suffix']) {
+ $summary[] = t('Display with prefix and suffix.');
+ }
+ }
+
+ return implode('<br />', $summary);
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function number_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+ $settings = $display['settings'];
+
+ switch ($display['type']) {
+ case 'number_integer':
+ case 'number_decimal':
+ foreach ($items as $delta => $item) {
+ $output = number_format($item['value'], $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']);
+ if ($settings['prefix_suffix']) {
+ $prefixes = isset($instance['settings']['prefix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['prefix'])) : array('');
+ $suffixes = isset($instance['settings']['suffix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['suffix'])) : array('');
+ $prefix = (count($prefixes) > 1) ? format_plural($item['value'], $prefixes[0], $prefixes[1]) : $prefixes[0];
+ $suffix = (count($suffixes) > 1) ? format_plural($item['value'], $suffixes[0], $suffixes[1]) : $suffixes[0];
+ $output = $prefix . $output . $suffix;
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'number_unformatted':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => $item['value']);
+ }
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function number_field_widget_info() {
+ return array(
+ 'number' => array(
+ 'label' => t('Text field'),
+ 'field types' => array('number_integer', 'number_decimal', 'number_float'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function number_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : '';
+ // Substitute the decimal separator.
+ if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+ $value = strtr($value, '.', $field['settings']['decimal_separator']);
+ }
+
+ $element += array(
+ '#type' => 'textfield',
+ '#default_value' => $value,
+ // Allow a slightly larger size that the field length to allow for some
+ // configurations where all characters won't fit in input field.
+ '#size' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 4 : 12,
+ // Allow two extra characters for signed values and decimal separator.
+ '#maxlength' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 2 : 10,
+ // Extract the number type from the field type name for easier validation.
+ '#number_type' => str_replace('number_', '', $field['type']),
+ );
+
+ // Add prefix and suffix.
+ if (!empty($instance['settings']['prefix'])) {
+ $prefixes = explode('|', $instance['settings']['prefix']);
+ $element['#field_prefix'] = field_filter_xss(array_pop($prefixes));
+ }
+ if (!empty($instance['settings']['suffix'])) {
+ $suffixes = explode('|', $instance['settings']['suffix']);
+ $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
+ }
+
+ $element['#element_validate'][] = 'number_field_widget_validate';
+
+ return array('value' => $element);
+}
+
+/**
+ * FAPI validation of an individual number element.
+ */
+function number_field_widget_validate($element, &$form_state) {
+ $field = field_widget_field($element, $form_state);
+ $instance = field_widget_instance($element, $form_state);
+
+ $type = $element['#number_type'];
+ $value = $element['#value'];
+
+ // Reject invalid characters.
+ if (!empty($value)) {
+ switch ($type) {
+ case 'float':
+ case 'decimal':
+ $regexp = '@([^-0-9\\' . $field['settings']['decimal_separator'] . '])|(.-)@';
+ $message = t('Only numbers and the decimal separator (@separator) allowed in %field.', array('%field' => $instance['label'], '@separator' => $field['settings']['decimal_separator']));
+ break;
+
+ case 'integer':
+ $regexp = '@([^-0-9])|(.-)@';
+ $message = t('Only numbers are allowed in %field.', array('%field' => $instance['label']));
+ break;
+ }
+ if ($value != preg_replace($regexp, '', $value)) {
+ form_error($element, $message);
+ }
+ else {
+ if ($type == 'decimal' || $type == 'float') {
+ // Verify that only one decimal separator exists in the field.
+ if (substr_count($value, $field['settings']['decimal_separator']) > 1) {
+ $message = t('%field: There should only be one decimal separator (@separator).',
+ array(
+ '%field' => t($instance['label']),
+ '@separator' => $field['settings']['decimal_separator'],
+ )
+ );
+ form_error($element, $message);
+ }
+ else {
+ // Substitute the decimal separator; things should be fine.
+ $value = strtr($value, $field['settings']['decimal_separator'], '.');
+ }
+ }
+ form_set_value($element, $value, $form_state);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function number_field_widget_error($element, $error, $form, &$form_state) {
+ form_error($element['value'], $error['message']);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test
new file mode 100644
index 0000000..88029cd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * @file
+ * Tests for number.module.
+ */
+
+/**
+ * Tests for number field types.
+ */
+class NumberFieldTestCase extends DrupalWebTestCase {
+ protected $field;
+ protected $instance;
+ protected $web_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Number field',
+ 'description' => 'Test the creation of number fields.',
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Test number_decimal field.
+ */
+ function testNumberDecimalField() {
+ // Create a field with settings to validate.
+ $this->field = array(
+ 'field_name' => drupal_strtolower($this->randomName()),
+ 'type' => 'number_decimal',
+ 'settings' => array(
+ 'precision' => 8, 'scale' => 4, 'decimal_separator' => '.',
+ )
+ );
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'number',
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'number_decimal',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+
+ // Display creation form.
+ $this->drupalGet('test-entity/add/test-bundle');
+ $langcode = LANGUAGE_NONE;
+ $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';
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $value,
+ );
+ $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)), '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(
+ '3.14.159',
+ '0..45469',
+ '..4589',
+ '6.459.52',
+ '6.3..25',
+ );
+
+ foreach ($wrong_entries as $wrong_entry) {
+ $this->drupalGet('test-entity/add/test-bundle');
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText(
+ t('There should only be one decimal separator (@separator)',
+ array('@separator' => $this->field['settings']['decimal_separator'])),
+ 'Correctly failed to save decimal value with more than one decimal point.'
+ );
+ }
+
+ // Try to create entries with minus sign not in the first position.
+ $wrong_entries = array(
+ '3-3',
+ '4-',
+ '1.3-',
+ '1.2-4',
+ '-10-10',
+ );
+
+ foreach ($wrong_entries as $wrong_entry) {
+ $this->drupalGet('test-entity/add/test-bundle');
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText(
+ t('Only numbers and the decimal separator (@separator) allowed in ',
+ array('@separator' => $this->field['settings']['decimal_separator'])),
+ 'Correctly failed to save decimal value with minus sign in the wrong position.'
+ );
+ }
+ }
+
+ /**
+ * Test number_integer field.
+ */
+ function testNumberIntegerField() {
+ // Display the "Add content type" form.
+ $this->drupalGet('admin/structure/types/add');
+
+ // Add a content type.
+ $name = $this->randomName();
+ $type = drupal_strtolower($name);
+ $edit = array('name' => $name, 'type' => $type);
+ $this->drupalPost(NULL, $edit, t('Save and add fields'));
+
+ // Add an integer field to the newly-created type.
+ $label = $this->randomName();
+ $field_name = drupal_strtolower($label);
+ $edit = array(
+ 'fields[_add_new_field][label]'=> $label,
+ 'fields[_add_new_field][field_name]' => $field_name,
+ 'fields[_add_new_field][type]' => 'number_integer',
+ 'fields[_add_new_field][widget_type]' => 'number',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Set the formatter to "number_integer" and to "unformatted", and just
+ // check that the settings summary does not generate warnings.
+ $this->drupalGet("admin/structure/types/manage/$type/display");
+ $edit = array(
+ "fields[field_$field_name][type]" => 'number_integer',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $edit = array(
+ "fields[field_$field_name][type]" => 'number_unformatted',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php
new file mode 100644
index 0000000..86f2b12
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Options module.
+ */
+
+/**
+ * Returns the list of options to be displayed for a field.
+ *
+ * Field types willing to enable one or several of the widgets defined in
+ * options.module (select, radios/checkboxes, on/off checkbox) need to
+ * implement this hook to specify the list of options to display in the
+ * widgets.
+ *
+ * @param $field
+ * The field definition.
+ * @param $instance
+ * (optional) The instance definition. The hook might be called without an
+ * $instance parameter in contexts where no specific instance can be targeted.
+ * It is recommended to only use instance level properties to filter out
+ * values from a list defined by field level properties.
+ * @param $entity_type
+ * The entity type the field is attached to.
+ * @param $entity
+ * The entity object the field is attached to, or NULL if no entity
+ * exists (e.g. in field settings page).
+ *
+ * @return
+ * The array of options for the field. Array keys are the values to be
+ * stored, and should be of the data type (string, number...) expected by
+ * the first 'column' for the field type. Array values are the labels to
+ * display within the widgets. The labels should NOT be sanitized,
+ * options.module takes care of sanitation according to the needs of each
+ * widget. The HTML tags defined in _field_filter_xss_allowed_tags() are
+ * allowed, other tags will be filtered.
+ */
+function hook_options_list($field, $instance, $entity_type, $entity) {
+ // Sample structure.
+ $options = array(
+ 0 => t('Zero'),
+ 1 => t('One'),
+ 2 => t('Two'),
+ 3 => t('Three'),
+ );
+
+ // Sample structure with groups. Only one level of nesting is allowed. This
+ // is only supported by the 'options_select' widget. Other widgets will
+ // flatten the array.
+ $options = array(
+ t('First group') => array(
+ 0 => t('Zero'),
+ ),
+ t('Second group') => array(
+ 1 => t('One'),
+ 2 => t('Two'),
+ ),
+ 3 => t('Three'),
+ );
+
+ // In actual implementations, the array of options will most probably depend
+ // on properties of the field. Example from taxonomy.module:
+ $options = array();
+ foreach ($field['settings']['allowed_values'] as $tree) {
+ $terms = taxonomy_get_tree($tree['vid'], $tree['parent']);
+ if ($terms) {
+ foreach ($terms as $term) {
+ $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
+ }
+ }
+ }
+
+ return $options;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info
new file mode 100644
index 0000000..571afbd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info
@@ -0,0 +1,13 @@
+name = Options
+description = Defines selection, check box and radio button widgets for text and numeric fields.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = options.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module
new file mode 100644
index 0000000..3862ba7
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module
@@ -0,0 +1,417 @@
+<?php
+
+/**
+ * @file
+ * Defines selection, check box and radio button widgets for text and numeric fields.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function options_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#options':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Options module defines checkbox, selection, and other input widgets for the Field module. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function options_theme() {
+ return array(
+ 'options_none' => array(
+ 'variables' => array('instance' => NULL, 'option' => NULL),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_info().
+ *
+ * Field type modules willing to use those widgets should:
+ * - Use hook_field_widget_info_alter() to append their field own types to the
+ * list of types supported by the widgets,
+ * - Implement hook_options_list() to provide the list of options.
+ * See list.module.
+ */
+function options_field_widget_info() {
+ return array(
+ 'options_select' => array(
+ 'label' => t('Select list'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ ),
+ 'options_buttons' => array(
+ 'label' => t('Check boxes/radio buttons'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ ),
+ 'options_onoff' => array(
+ 'label' => t('Single on/off checkbox'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ 'settings' => array('display_label' => 0),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function options_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ // Abstract over the actual field columns, to allow different field types to
+ // reuse those widgets.
+ $value_key = key($field['columns']);
+
+ $type = str_replace('options_', '', $instance['widget']['type']);
+ $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
+ $required = $element['#required'];
+ $has_value = isset($items[0][$value_key]);
+ $properties = _options_properties($type, $multiple, $required, $has_value);
+
+ $entity_type = $element['#entity_type'];
+ $entity = $element['#entity'];
+
+ // Prepare the list of options.
+ $options = _options_get_options($field, $instance, $properties, $entity_type, $entity);
+
+ // Put current field values in shape.
+ $default_value = _options_storage_to_form($items, $options, $value_key, $properties);
+
+ switch ($type) {
+ case 'select':
+ $element += array(
+ '#type' => 'select',
+ '#default_value' => $default_value,
+ // Do not display a 'multiple' select box if there is only one option.
+ '#multiple' => $multiple && count($options) > 1,
+ '#options' => $options,
+ );
+ break;
+
+ case 'buttons':
+ // If required and there is one single option, preselect it.
+ if ($required && count($options) == 1) {
+ reset($options);
+ $default_value = array(key($options));
+ }
+
+ // If this is a single-value field, take the first default value, or
+ // default to NULL so that the form element is properly recognized as
+ // not having a default value.
+ if (!$multiple) {
+ $default_value = $default_value ? reset($default_value) : NULL;
+ }
+
+ $element += array(
+ '#type' => $multiple ? 'checkboxes' : 'radios',
+ // Radio buttons need a scalar value.
+ '#default_value' => $default_value,
+ '#options' => $options,
+ );
+ break;
+
+ case 'onoff':
+ $keys = array_keys($options);
+ $off_value = array_shift($keys);
+ $on_value = array_shift($keys);
+ $element += array(
+ '#type' => 'checkbox',
+ '#default_value' => (isset($default_value[0]) && $default_value[0] == $on_value) ? 1 : 0,
+ '#on_value' => $on_value,
+ '#off_value' => $off_value,
+ );
+ // Override the title from the incoming $element.
+ $element['#title'] = isset($options[$on_value]) ? $options[$on_value] : '';
+
+ if ($instance['widget']['settings']['display_label']) {
+ $element['#title'] = $instance['label'];
+ }
+ break;
+ }
+
+ $element += array(
+ '#value_key' => $value_key,
+ '#element_validate' => array('options_field_widget_validate'),
+ '#properties' => $properties,
+ );
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function options_field_widget_settings_form($field, $instance) {
+ $form = array();
+ if ($instance['widget']['type'] == 'options_onoff') {
+ $form['display_label'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use field label instead of the "On value" as label'),
+ '#default_value' => $instance['widget']['settings']['display_label'],
+ '#weight' => -1,
+ );
+ }
+ return $form;
+}
+
+/**
+ * Form element validation handler for options element.
+ */
+function options_field_widget_validate($element, &$form_state) {
+ if ($element['#required'] && $element['#value'] == '_none') {
+ form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
+ }
+ // Transpose selections from field => delta to delta => field, turning
+ // multiple selected options into multiple parent elements.
+ $items = _options_form_to_storage($element);
+ form_set_value($element, $items, $form_state);
+}
+
+/**
+ * Describes the preparation steps required by each widget.
+ */
+function _options_properties($type, $multiple, $required, $has_value) {
+ $base = array(
+ 'filter_xss' => FALSE,
+ 'strip_tags' => FALSE,
+ 'empty_option' => FALSE,
+ 'optgroups' => FALSE,
+ );
+
+ $properties = array();
+
+ switch ($type) {
+ case 'select':
+ $properties = array(
+ // Select boxes do not support any HTML tag.
+ 'strip_tags' => TRUE,
+ 'optgroups' => TRUE,
+ );
+ if ($multiple) {
+ // Multiple select: add a 'none' option for non-required fields.
+ if (!$required) {
+ $properties['empty_option'] = 'option_none';
+ }
+ }
+ else {
+ // Single select: add a 'none' option for non-required fields,
+ // and a 'select a value' option for required fields that do not come
+ // with a value selected.
+ if (!$required) {
+ $properties['empty_option'] = 'option_none';
+ }
+ elseif (!$has_value) {
+ $properties['empty_option'] = 'option_select';
+ }
+ }
+ break;
+
+ case 'buttons':
+ $properties = array(
+ 'filter_xss' => TRUE,
+ );
+ // Add a 'none' option for non-required radio buttons.
+ if (!$required && !$multiple) {
+ $properties['empty_option'] = 'option_none';
+ }
+ break;
+
+ case 'onoff':
+ $properties = array(
+ 'filter_xss' => TRUE,
+ );
+ break;
+ }
+
+ return $properties + $base;
+}
+
+/**
+ * Collects the options for a field.
+ */
+function _options_get_options($field, $instance, $properties, $entity_type, $entity) {
+ // Get the list of options.
+ $options = (array) module_invoke($field['module'], 'options_list', $field, $instance, $entity_type, $entity);
+
+ // Sanitize the options.
+ _options_prepare_options($options, $properties);
+
+ if (!$properties['optgroups']) {
+ $options = options_array_flatten($options);
+ }
+
+ if ($properties['empty_option']) {
+ $label = theme('options_none', array('instance' => $instance, 'option' => $properties['empty_option']));
+ $options = array('_none' => $label) + $options;
+ }
+
+ return $options;
+}
+
+/**
+ * Sanitizes the options.
+ *
+ * The function is recursive to support optgroups.
+ */
+function _options_prepare_options(&$options, $properties) {
+ foreach ($options as $value => $label) {
+ // Recurse for optgroups.
+ if (is_array($label)) {
+ _options_prepare_options($options[$value], $properties);
+ }
+ else {
+ if ($properties['strip_tags']) {
+ $options[$value] = strip_tags($label);
+ }
+ if ($properties['filter_xss']) {
+ $options[$value] = field_filter_xss($label);
+ }
+ }
+ }
+}
+
+/**
+ * Transforms stored field values into the format the widgets need.
+ */
+function _options_storage_to_form($items, $options, $column, $properties) {
+ $items_transposed = options_array_transpose($items);
+ $values = (isset($items_transposed[$column]) && is_array($items_transposed[$column])) ? $items_transposed[$column] : array();
+
+ // Discard values that are not in the current list of options. Flatten the
+ // array if needed.
+ if ($properties['optgroups']) {
+ $options = options_array_flatten($options);
+ }
+ $values = array_values(array_intersect($values, array_keys($options)));
+ return $values;
+}
+
+/**
+ * Transforms submitted form values into field storage format.
+ */
+function _options_form_to_storage($element) {
+ $values = array_values((array) $element['#value']);
+ $properties = $element['#properties'];
+
+ // On/off checkbox: transform '0 / 1' into the 'on / off' values.
+ if ($element['#type'] == 'checkbox') {
+ $values = array($values[0] ? $element['#on_value'] : $element['#off_value']);
+ }
+
+ // Filter out the 'none' option. Use a strict comparison, because
+ // 0 == 'any string'.
+ if ($properties['empty_option']) {
+ $index = array_search('_none', $values, TRUE);
+ if ($index !== FALSE) {
+ unset($values[$index]);
+ }
+ }
+
+ // Make sure we populate at least an empty value.
+ if (empty($values)) {
+ $values = array(NULL);
+ }
+
+ $result = options_array_transpose(array($element['#value_key'] => $values));
+ return $result;
+}
+
+/**
+ * Manipulates a 2D array to reverse rows and columns.
+ *
+ * The default data storage for fields is delta first, column names second.
+ * This is sometimes inconvenient for field modules, so this function can be
+ * used to present the data in an alternate format.
+ *
+ * @param $array
+ * The array to be transposed. It must be at least two-dimensional, and
+ * the subarrays must all have the same keys or behavior is undefined.
+ * @return
+ * The transposed array.
+ */
+function options_array_transpose($array) {
+ $result = array();
+ if (is_array($array)) {
+ foreach ($array as $key1 => $value1) {
+ if (is_array($value1)) {
+ foreach ($value1 as $key2 => $value2) {
+ if (!isset($result[$key2])) {
+ $result[$key2] = array();
+ }
+ $result[$key2][$key1] = $value2;
+ }
+ }
+ }
+ }
+ return $result;
+}
+
+/**
+ * Flattens an array of allowed values.
+ *
+ * @param $array
+ * A single or multidimensional array.
+ * @return
+ * A flattened array.
+ */
+function options_array_flatten($array) {
+ $result = array();
+ if (is_array($array)) {
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $result += options_array_flatten($value);
+ }
+ else {
+ $result[$key] = $value;
+ }
+ }
+ }
+ return $result;
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function options_field_widget_error($element, $error, $form, &$form_state) {
+ form_error($element, $error['message']);
+}
+
+/**
+ * Returns HTML for the label for the empty value for options that are not required.
+ *
+ * The default theme will display N/A for a radio list and '- None -' for a select.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - instance: An array representing the widget requesting the options.
+ *
+ * @ingroup themeable
+ */
+function theme_options_none($variables) {
+ $instance = $variables['instance'];
+ $option = $variables['option'];
+
+ $output = '';
+ switch ($instance['widget']['type']) {
+ case 'options_buttons':
+ $output = t('N/A');
+ break;
+
+ case 'options_select':
+ $output = ($option == 'option_none' ? t('- None -') : t('- Select a value -'));
+ break;
+ }
+
+ return $output;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test
new file mode 100644
index 0000000..7183311
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test
@@ -0,0 +1,553 @@
+<?php
+
+/**
+ * @file
+ * Tests for options.module.
+ */
+
+class OptionsWidgetsTestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Options widgets',
+ 'description' => "Test the Options widgets.",
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test', 'list_test');
+
+ // Field with cardinality 1.
+ $this->card_1 = array(
+ 'field_name' => 'card_1',
+ 'type' => 'list_integer',
+ 'cardinality' => 1,
+ 'settings' => array(
+ // Make sure that 0 works as an option.
+ 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->card_1 = field_create_field($this->card_1);
+
+ // Field with cardinality 2.
+ $this->card_2 = array(
+ 'field_name' => 'card_2',
+ 'type' => 'list_integer',
+ 'cardinality' => 2,
+ 'settings' => array(
+ // Make sure that 0 works as an option.
+ 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->card_2 = field_create_field($this->card_2);
+
+ // Boolean field.
+ $this->bool = array(
+ 'field_name' => 'bool',
+ 'type' => 'list_boolean',
+ 'cardinality' => 1,
+ 'settings' => array(
+ // Make sure that 0 works as a 'on' value'.
+ 'allowed_values' => array(1 => 'Zero', 0 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->bool = field_create_field($this->bool);
+
+ // Create a web user.
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Tests the 'options_buttons' widget (single select).
+ */
+ function testRadioButtons() {
+ // Create an instance of the 'single value' field.
+ $instance = array(
+ 'field_name' => $this->card_1['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // With no field data, no buttons are checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $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>', 'Option text was properly filtered.');
+
+ // Select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // Check that the selected button is checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-1-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-2");
+
+ // Unselect option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+
+ // Check that required radios with one option is auto-selected.
+ $this->card_1['settings']['allowed_values'] = array(99 => 'Only allowed value');
+ field_update_field($this->card_1);
+ $instance['required'] = TRUE;
+ field_update_instance($instance);
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-1-$langcode-99");
+ }
+
+ /**
+ * Tests the 'options_buttons' widget (multiple select).
+ */
+ function testCheckBoxes() {
+ // Create an instance of the 'multiple values' field.
+ $instance = array(
+ 'field_name' => $this->card_2['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form: with no field data, nothing is checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $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>', 'Option text was properly filtered.');
+
+ // Submit form: select first and third options.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => TRUE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-1");
+ $this->assertFieldChecked("edit-card-2-$langcode-2");
+
+ // Submit form: select only first option.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => FALSE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-2");
+
+ // Submit form: select the three options while the field accepts only 2.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => TRUE,
+ "card_2[$langcode][2]" => TRUE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
+
+ // Submit form: uncheck all options.
+ $edit = array(
+ "card_2[$langcode][0]" => FALSE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => FALSE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ // Check that the value was saved.
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // Required checkbox with one option is auto-selected.
+ $this->card_2['settings']['allowed_values'] = array(99 => 'Only allowed value');
+ field_update_field($this->card_2);
+ $instance['required'] = TRUE;
+ field_update_instance($instance);
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-99");
+ }
+
+ /**
+ * Tests the 'options_select' widget (single select).
+ */
+ function testSelectListSingle() {
+ // Create an instance of the 'single value' field.
+ $instance = array(
+ 'field_name' => $this->card_1['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'required' => TRUE,
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // 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 -'))), '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', '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'])), 'Cannot save a required field when selecting "none" from the select list.');
+
+ // Submit form: select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // 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)), '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);
+
+ // Make the field non required.
+ $instance['required'] = FALSE;
+ field_update_instance($instance);
+
+ // 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 -'))), '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'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+
+ // Test optgroups.
+
+ $this->card_1['settings']['allowed_values'] = array();
+ $this->card_1['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
+ field_update_field($this->card_1);
+
+ // Display form: with no field data, nothing is selected
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $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', 'Option text was properly filtered.');
+ $this->assertRaw('Group 1', 'Option groups are displayed.');
+
+ // Submit form: select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-1-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
+
+ // Submit form: Unselect the option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+ }
+
+ /**
+ * Tests the 'options_select' widget (multiple select).
+ */
+ function testSelectListMultiple() {
+ // Create an instance of the 'multiple values' field.
+ $instance = array(
+ 'field_name' => $this->card_2['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form: with no field data, nothing is selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $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', 'Option text was properly filtered.');
+
+ // Submit form: select first and third options.
+ $edit = array("card_2[$langcode][]" => array(0 => 0, 2 => 2));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertOptionSelected("edit-card-2-$langcode", 2);
+
+ // Submit form: select only first option.
+ $edit = array("card_2[$langcode][]" => array(0 => 0));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+
+ // 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', 'Validation error was displayed.');
+
+ // Submit form: uncheck all options.
+ $edit = array("card_2[$langcode][]" => array());
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // Test the 'None' option.
+
+ // 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'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Check that selecting the 'none' option empties the field.
+ $edit = array("card_2[$langcode][]" => array('_none' => '_none'));
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // A required select list does not have an empty key.
+ $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)), '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.
+
+ // Test optgroups.
+
+ // Use a callback function defining optgroups.
+ $this->card_2['settings']['allowed_values'] = array();
+ $this->card_2['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
+ field_update_field($this->card_2);
+ $instance['required'] = FALSE;
+ field_update_instance($instance);
+
+ // Display form: with no field data, nothing is selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $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', '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));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+
+ // Submit form: Unselect the option.
+ $edit = array("card_2[$langcode][]" => array('_none' => '_none'));
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+ }
+
+ /**
+ * Tests the 'options_onoff' widget.
+ */
+ function testOnOffCheckbox() {
+ // Create an instance of the 'boolean' field.
+ $instance = array(
+ 'field_name' => $this->bool['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_onoff',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // 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>', 'Option text was properly filtered.');
+
+ // Submit form: check the option.
+ $edit = array("bool[$langcode]" => TRUE);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'bool', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-bool-$langcode");
+
+ // Submit form: uncheck the option.
+ $edit = array("bool[$langcode]" => FALSE);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'bool', $langcode, array(1));
+
+ // Display form: with 'off' value, option is unchecked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoFieldChecked("edit-bool-$langcode");
+
+ // Create admin user.
+ $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Create a test field instance.
+ $fieldUpdate = $this->bool;
+ $fieldUpdate['settings']['allowed_values'] = array(0 => 0, 1 => 'MyOnValue');
+ field_update_field($fieldUpdate);
+ $instance = array(
+ 'field_name' => $this->bool['field_name'],
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'widget' => array(
+ 'type' => 'options_onoff',
+ 'module' => 'options',
+ ),
+ );
+ field_create_instance($instance);
+
+ // Go to the edit page and check if the default settings works as expected
+ $fieldEditUrl = 'admin/structure/types/manage/page/fields/bool';
+ $this->drupalGet($fieldEditUrl);
+
+ $this->assertText(
+ 'Use field label instead of the "On value" as label ',
+ 'Display setting checkbox available.'
+ );
+
+ $this->assertFieldByXPath(
+ '*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="MyOnValue "]',
+ TRUE,
+ 'Default case shows "On value"'
+ );
+
+ // Enable setting
+ $edit = array('instance[widget][settings][display_label]' => 1);
+ // Save the new Settings
+ $this->drupalPost($fieldEditUrl, $edit, t('Save settings'));
+
+ // Go again to the edit page and check if the setting
+ // is stored and has the expected effect
+ $this->drupalGet($fieldEditUrl);
+ $this->assertText(
+ 'Use field label instead of the "On value" as label ',
+ 'Display setting checkbox is available'
+ );
+ $this->assertFieldChecked(
+ 'edit-instance-widget-settings-display-label',
+ 'Display settings checkbox checked'
+ );
+ $this->assertFieldByXPath(
+ '*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="' . $this->bool['field_name'] . ' "]',
+ TRUE,
+ 'Display label changes label of the checkbox'
+ );
+ }
+}
+
+/**
+ * Test an options select on a list field with a dynamic allowed values function.
+ */
+class OptionsSelectDynamicValuesTestCase extends ListDynamicValuesTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Options select dynamic values',
+ 'description' => 'Test an options select on a list field with a dynamic allowed values function.',
+ 'group' => 'Field types',
+ );
+ }
+
+ /**
+ * Tests the 'options_select' widget (single select).
+ */
+ function testSelectListDynamic() {
+ // Create an entity.
+ $this->entity->is_new = TRUE;
+ field_test_entity_save($this->entity);
+ // Create a web user.
+ $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($web_user);
+
+ // Display form.
+ $this->drupalGet('test-entity/manage/' . $this->entity->ftid . '/edit');
+ $options = $this->xpath('//select[@id="edit-test-list-und"]/option');
+ $this->assertEqual(count($options), count($this->test) + 1);
+ foreach ($options as $option) {
+ $value = (string) $option['value'];
+ if ($value != '_none') {
+ $this->assertTrue(array_search($value, $this->test));
+ }
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info
new file mode 100644
index 0000000..4dd9b00
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info
@@ -0,0 +1,14 @@
+name = Text
+description = Defines simple text field types.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = text.test
+required = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install
new file mode 100644
index 0000000..61be748
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the text module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function text_field_schema($field) {
+ switch ($field['type']) {
+ case 'text':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => $field['settings']['max_length'],
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+
+ case 'text_long':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+
+ case 'text_with_summary':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'summary' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ }
+ $columns += array(
+ 'format' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ );
+ return array(
+ 'columns' => $columns,
+ 'indexes' => array(
+ 'format' => array('format'),
+ ),
+ 'foreign keys' => array(
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ );
+}
+
+/**
+ * Change text field 'format' columns into varchar.
+ */
+function text_update_7000() {
+ $spec = array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ );
+ $fields = _update_7000_field_read_fields(array(
+ 'module' => 'text',
+ 'storage_type' => 'field_sql_storage',
+ ));
+ foreach ($fields as $field) {
+ if ($field['deleted']) {
+ $table = "field_deleted_data_{$field['id']}";
+ $revision_table = "field_deleted_revision_{$field['id']}";
+ }
+ else {
+ $table = "field_data_{$field['field_name']}";
+ $revision_table = "field_revision_{$field['field_name']}";
+ }
+ $column = $field['field_name'] . '_' . 'format';
+ db_change_field($table, $column, $column, $spec);
+ db_change_field($revision_table, $column, $column, $spec);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js
new file mode 100644
index 0000000..f3ae894
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js
@@ -0,0 +1,49 @@
+
+(function ($) {
+
+/**
+ * Auto-hide summary textarea if empty and show hide and unhide links.
+ */
+Drupal.behaviors.textSummary = {
+ attach: function (context, settings) {
+ $('.text-summary', context).once('text-summary', function () {
+ var $widget = $(this).closest('div.field-type-text-with-summary');
+ var $summaries = $widget.find('div.text-summary-wrapper');
+
+ $summaries.once('text-summary-wrapper').each(function(index) {
+ var $summary = $(this);
+ var $summaryLabel = $summary.find('label');
+ var $full = $widget.find('.text-full').eq(index).closest('.form-item');
+ var $fullLabel = $full.find('label');
+
+ // Create a placeholder label when the field cardinality is
+ // unlimited or greater than 1.
+ if ($fullLabel.length == 0) {
+ $fullLabel = $('<label></label>').prependTo($full);
+ }
+
+ // 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 () {
+ $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;
+ }
+ ).appendTo($summaryLabel);
+
+ // If no summary is set, hide the summary field.
+ if ($(this).find('.text-summary').val() == '') {
+ $link.click();
+ }
+ return;
+ });
+ });
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module
new file mode 100644
index 0000000..d73814f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module
@@ -0,0 +1,611 @@
+<?php
+
+/**
+ * @file
+ * Defines simple text field types.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function text_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#text':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t("The Text module defines various text field types for the Field module. A text field may contain plain text only, or optionally, may use Drupal's <a href='@filter-help'>text filters</a> to securely manage HTML output. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, the field can be validated, so that it is limited to a set of allowed values. See the <a href='@field-help'>Field module help page</a> for more information about fields.", array('@field-help' => url('admin/help/field'), '@filter-help' => url('admin/help/filter'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ *
+ * Field settings:
+ * - max_length: the maximum length for a varchar field.
+ * Instance settings:
+ * - text_processing: whether text input filters should be used.
+ * - display_summary: whether the summary field should be displayed.
+ * When empty and not displayed the summary will take its value from the
+ * trimmed value of the main text field.
+ */
+function text_field_info() {
+ return array(
+ 'text' => array(
+ 'label' => t('Text'),
+ 'description' => t('This field stores varchar text in the database.'),
+ 'settings' => array('max_length' => 255),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textfield',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_long' => array(
+ 'label' => t('Long text'),
+ 'description' => t('This field stores long text in the database.'),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textarea',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_with_summary' => array(
+ 'label' => t('Long text and summary'),
+ 'description' => t('This field stores long text in the database along with optional summary text.'),
+ 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
+ 'default_widget' => 'text_textarea_with_summary',
+ 'default_formatter' => 'text_default',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function text_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+
+ $form = array();
+
+ if ($field['type'] == 'text') {
+ $form['max_length'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum length'),
+ '#default_value' => $settings['max_length'],
+ '#required' => TRUE,
+ '#description' => t('The maximum length of the field in characters.'),
+ '#element_validate' => array('element_validate_integer_positive'),
+ // @todo: If $has_data, add a validate handler that only allows
+ // max_length to increase.
+ '#disabled' => $has_data,
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function text_field_instance_settings_form($field, $instance) {
+ $settings = $instance['settings'];
+
+ $form['text_processing'] = array(
+ '#type' => 'radios',
+ '#title' => t('Text processing'),
+ '#default_value' => $settings['text_processing'],
+ '#options' => array(
+ t('Plain text'),
+ t('Filtered text (user selects text format)'),
+ ),
+ );
+ if ($field['type'] == 'text_with_summary') {
+ $form['display_summary'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Summary input'),
+ '#default_value' => $settings['display_summary'],
+ '#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display type.'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'text_value_max_length': The value exceeds the maximum length.
+ * - 'text_summary_max_length': The summary exceeds the maximum length.
+ */
+function text_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ foreach ($items as $delta => $item) {
+ // @todo Length is counted separately for summary and value, so the maximum
+ // length can be exceeded very easily.
+ foreach (array('value', 'summary') as $column) {
+ if (!empty($item[$column])) {
+ if (!empty($field['settings']['max_length']) && drupal_strlen($item[$column]) > $field['settings']['max_length']) {
+ switch ($column) {
+ case 'value':
+ $message = t('%name: the text may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length']));
+ break;
+
+ case 'summary':
+ $message = t('%name: the summary may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length']));
+ break;
+ }
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => "text_{$column}_length",
+ 'message' => $message,
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_load().
+ *
+ * Where possible, generate the sanitized version of each field early so that
+ * it is cached in the field cache. This avoids looking up from the filter cache
+ * separately.
+ *
+ * @see text_field_formatter_view()
+ */
+function text_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ // Only process items with a cacheable format, the rest will be handled
+ // by formatters if needed.
+ if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) {
+ $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : '';
+ if ($field['type'] == 'text_with_summary') {
+ $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $item, 'summary') : '';
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function text_field_is_empty($item, $field) {
+ if (!isset($item['value']) || $item['value'] === '') {
+ return !isset($item['summary']) || $item['summary'] === '';
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function text_field_formatter_info() {
+ return array(
+ 'text_default' => array(
+ 'label' => t('Default'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+ 'text_plain' => array(
+ 'label' => t('Plain text'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+
+ // The text_trimmed formatter displays the trimmed version of the
+ // full element of the field. It is intended to be used with text
+ // and text_long fields. It also works with text_with_summary
+ // fields though the text_summary_or_trimmed formatter makes more
+ // sense for that field type.
+ 'text_trimmed' => array(
+ 'label' => t('Trimmed'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ 'settings' => array('trim_length' => 600),
+ ),
+
+ // The 'summary or trimmed' field formatter for text_with_summary
+ // fields displays returns the summary element of the field or, if
+ // the summary is empty, the trimmed version of the full element
+ // of the field.
+ 'text_summary_or_trimmed' => array(
+ 'label' => t('Summary or trimmed'),
+ 'field types' => array('text_with_summary'),
+ 'settings' => array('trim_length' => 600),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function text_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $element = array();
+
+ if (strpos($display['type'], '_trimmed') !== FALSE) {
+ $element['trim_length'] = array(
+ '#title' => t('Trim length'),
+ '#type' => 'textfield',
+ '#size' => 10,
+ '#default_value' => $settings['trim_length'],
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#required' => TRUE,
+ );
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function text_field_formatter_settings_summary($field, $instance, $view_mode) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $summary = '';
+
+ if (strpos($display['type'], '_trimmed') !== FALSE) {
+ $summary = t('Trim length') . ': ' . $settings['trim_length'];
+ }
+
+ return $summary;
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function text_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+
+ switch ($display['type']) {
+ case 'text_default':
+ case 'text_trimmed':
+ foreach ($items as $delta => $item) {
+ $output = _text_sanitize($instance, $langcode, $item, 'value');
+ if ($display['type'] == 'text_trimmed') {
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'text_summary_or_trimmed':
+ foreach ($items as $delta => $item) {
+ if (!empty($item['summary'])) {
+ $output = _text_sanitize($instance, $langcode, $item, 'summary');
+ }
+ else {
+ $output = _text_sanitize($instance, $langcode, $item, 'value');
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'text_plain':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => strip_tags($item['value']));
+ }
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * Sanitizes the 'value' or 'summary' data of a text value.
+ *
+ * Depending on whether the field instance uses text processing, data is run
+ * through check_plain() or check_markup().
+ *
+ * @param $instance
+ * The instance definition.
+ * @param $langcode
+ * The language associated to $item.
+ * @param $item
+ * The field value to sanitize.
+ * @param $column
+ * The column to sanitize (either 'value' or 'summary').
+ *
+ * @return
+ * The sanitized string.
+ */
+function _text_sanitize($instance, $langcode, $item, $column) {
+ // If the value uses a cacheable text format, text_field_load() precomputes
+ // the sanitized string.
+ if (isset($item["safe_$column"])) {
+ return $item["safe_$column"];
+ }
+ return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], $langcode) : check_plain($item[$column]);
+}
+
+/**
+ * Generate a trimmed, formatted version of a text field value.
+ *
+ * If the end of the summary is not indicated using the <!--break--> delimiter
+ * then we generate the summary automatically, trying to end it at a sensible
+ * place such as the end of a paragraph, a line break, or the end of a
+ * sentence (in that order of preference).
+ *
+ * @param $text
+ * The content for which a summary will be generated.
+ * @param $format
+ * The format of the content.
+ * If the PHP filter is present and $text contains PHP code, we do not
+ * split it up to prevent parse errors.
+ * If the line break filter is present then we treat newlines embedded in
+ * $text as line breaks.
+ * If the htmlcorrector filter is present, it will be run on the generated
+ * summary (if different from the incoming $text).
+ * @param $size
+ * The desired character length of the summary. If omitted, the default
+ * value will be used. Ignored if the special delimiter is present
+ * in $text.
+ * @return
+ * The generated summary.
+ */
+function text_summary($text, $format = NULL, $size = NULL) {
+
+ if (!isset($size)) {
+ // What used to be called 'teaser' is now called 'summary', but
+ // the variable 'teaser_length' is preserved for backwards compatibility.
+ $size = variable_get('teaser_length', 600);
+ }
+
+ // Find where the delimiter is in the body
+ $delimiter = strpos($text, '<!--break-->');
+
+ // If the size is zero, and there is no delimiter, the entire body is the summary.
+ if ($size == 0 && $delimiter === FALSE) {
+ return $text;
+ }
+
+ // If a valid delimiter has been specified, use it to chop off the summary.
+ if ($delimiter !== FALSE) {
+ return substr($text, 0, $delimiter);
+ }
+
+ // We check for the presence of the PHP evaluator filter in the current
+ // format. If the body contains PHP code, we do not split it up to prevent
+ // parse errors.
+ if (isset($format)) {
+ $filters = filter_list_format($format);
+ if (isset($filters['php_code']) && $filters['php_code']->status && strpos($text, '<?') !== FALSE) {
+ return $text;
+ }
+ }
+
+ // If we have a short body, the entire body is the summary.
+ if (drupal_strlen($text) <= $size) {
+ return $text;
+ }
+
+ // If the delimiter has not been specified, try to split at paragraph or
+ // sentence boundaries.
+
+ // The summary may not be longer than maximum length specified. Initial slice.
+ $summary = truncate_utf8($text, $size);
+
+ // Store the actual length of the UTF8 string -- which might not be the same
+ // as $size.
+ $max_rpos = strlen($summary);
+
+ // How much to cut off the end of the summary so that it doesn't end in the
+ // middle of a paragraph, sentence, or word.
+ // Initialize it to maximum in order to find the minimum.
+ $min_rpos = $max_rpos;
+
+ // Store the reverse of the summary. We use strpos on the reversed needle and
+ // haystack for speed and convenience.
+ $reversed = strrev($summary);
+
+ // Build an array of arrays of break points grouped by preference.
+ $break_points = array();
+
+ // A paragraph near the end of sliced summary is most preferable.
+ $break_points[] = array('</p>' => 0);
+
+ // If no complete paragraph then treat line breaks as paragraphs.
+ $line_breaks = array('<br />' => 6, '<br>' => 4);
+ // Newline only indicates a line break if line break converter
+ // filter is present.
+ if (isset($filters['filter_autop'])) {
+ $line_breaks["\n"] = 1;
+ }
+ $break_points[] = $line_breaks;
+
+ // If the first paragraph is too long, split at the end of a sentence.
+ $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
+
+ // Iterate over the groups of break points until a break point is found.
+ foreach ($break_points as $points) {
+ // Look for each break point, starting at the end of the summary.
+ foreach ($points as $point => $offset) {
+ // The summary is already reversed, but the break point isn't.
+ $rpos = strpos($reversed, strrev($point));
+ if ($rpos !== FALSE) {
+ $min_rpos = min($rpos + $offset, $min_rpos);
+ }
+ }
+
+ // If a break point was found in this group, slice and stop searching.
+ if ($min_rpos !== $max_rpos) {
+ // Don't slice with length 0. Length must be <0 to slice from RHS.
+ $summary = ($min_rpos === 0) ? $summary : substr($summary, 0, 0 - $min_rpos);
+ break;
+ }
+ }
+
+ // If the htmlcorrector filter is present, apply it to the generated summary.
+ if (isset($filters['filter_htmlcorrector'])) {
+ $summary = _filter_htmlcorrector($summary);
+ }
+
+ return $summary;
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function text_field_widget_info() {
+ return array(
+ 'text_textfield' => array(
+ 'label' => t('Text field'),
+ 'field types' => array('text'),
+ 'settings' => array('size' => 60),
+ ),
+ 'text_textarea' => array(
+ 'label' => t('Text area (multiple rows)'),
+ 'field types' => array('text_long'),
+ 'settings' => array('rows' => 5),
+ ),
+ 'text_textarea_with_summary' => array(
+ 'label' => t('Text area with a summary'),
+ 'field types' => array('text_with_summary'),
+ 'settings' => array('rows' => 20, 'summary_rows' => 5),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function text_field_widget_settings_form($field, $instance) {
+ $widget = $instance['widget'];
+ $settings = $widget['settings'];
+
+ if ($widget['type'] == 'text_textfield') {
+ $form['size'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Size of textfield'),
+ '#default_value' => $settings['size'],
+ '#required' => TRUE,
+ '#element_validate' => array('element_validate_integer_positive'),
+ );
+ }
+ else {
+ $form['rows'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Rows'),
+ '#default_value' => $settings['rows'],
+ '#required' => TRUE,
+ '#element_validate' => array('element_validate_integer_positive'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function text_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ $summary_widget = array();
+ $main_widget = array();
+
+ switch ($instance['widget']['type']) {
+ case 'text_textfield':
+ $main_widget = $element + array(
+ '#type' => 'textfield',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#size' => $instance['widget']['settings']['size'],
+ '#maxlength' => $field['settings']['max_length'],
+ '#attributes' => array('class' => array('text-full')),
+ );
+ break;
+
+ case 'text_textarea_with_summary':
+ $display = !empty($items[$delta]['summary']) || !empty($instance['settings']['display_summary']);
+ $summary_widget = array(
+ '#type' => $display ? 'textarea' : 'value',
+ '#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
+ '#title' => t('Summary'),
+ '#rows' => $instance['widget']['settings']['summary_rows'],
+ '#description' => t('Leave blank to use trimmed value of full text as the summary.'),
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'text') . '/text.js'),
+ ),
+ '#attributes' => array('class' => array('text-summary')),
+ '#prefix' => '<div class="text-summary-wrapper">',
+ '#suffix' => '</div>',
+ '#weight' => -10,
+ );
+ // Fall through to the next case.
+
+ case 'text_textarea':
+ $main_widget = $element + array(
+ '#type' => 'textarea',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#rows' => $instance['widget']['settings']['rows'],
+ '#attributes' => array('class' => array('text-full')),
+ );
+ break;
+ }
+
+ if ($main_widget) {
+ // Conditionally alter the form element's type if text processing is enabled.
+ if ($instance['settings']['text_processing']) {
+ $element = $main_widget;
+ $element['#type'] = 'text_format';
+ $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
+ $element['#base_type'] = $main_widget['#type'];
+ }
+ else {
+ $element['value'] = $main_widget;
+ }
+ }
+ if ($summary_widget) {
+ $element['summary'] = $summary_widget;
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function text_field_widget_error($element, $error, $form, &$form_state) {
+ switch ($error['error']) {
+ case 'text_summary_max_length':
+ $error_element = $element[$element['#columns'][1]];
+ break;
+
+ default:
+ $error_element = $element[$element['#columns'][0]];
+ break;
+ }
+
+ form_error($error_element, $error['message']);
+}
+
+/**
+ * Implements hook_field_prepare_translation().
+ */
+function text_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,
+ // we must not expose the source values.
+ $field_name = $field['field_name'];
+ if (!empty($source_entity->{$field_name}[$source_langcode])) {
+ $formats = filter_formats();
+ foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) {
+ $format_id = $item['format'];
+ if (!empty($format_id) && !filter_access($formats[$format_id])) {
+ unset($items[$delta]);
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_filter_format_update().
+ */
+function text_filter_format_update($format) {
+ field_cache_clear();
+}
+
+/**
+ * Implements hook_filter_format_disable().
+ */
+function text_filter_format_disable($format) {
+ field_cache_clear();
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test
new file mode 100644
index 0000000..2f14738
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test
@@ -0,0 +1,517 @@
+<?php
+
+/**
+ * @file
+ * Tests for text.module.
+ */
+
+class TextFieldTestCase extends DrupalWebTestCase {
+ protected $instance;
+ protected $admin_user;
+ protected $web_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text field',
+ 'description' => "Test the creation of text fields.",
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+
+ $this->admin_user = $this->drupalCreateUser(array('administer filters'));
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ // Test fields.
+
+ /**
+ * Test text field validation.
+ */
+ function testTextFieldValidation() {
+ // Create a field with settings to validate.
+ $max_length = 3;
+ $this->field = array(
+ 'field_name' => drupal_strtolower($this->randomName()),
+ 'type' => 'text',
+ 'settings' => array(
+ 'max_length' => $max_length,
+ )
+ );
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'text_textfield',
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ // Test valid and invalid values with field_attach_validate().
+ $entity = field_test_create_stub_entity();
+ $langcode = LANGUAGE_NONE;
+ for ($i = 0; $i <= $max_length + 2; $i++) {
+ $entity->{$this->field['field_name']}[$langcode][0]['value'] = str_repeat('x', $i);
+ try {
+ field_attach_validate('test_entity', $entity);
+ $this->assertTrue($i <= $max_length, "Length $i does not cause validation error when max_length is $max_length");
+ }
+ catch (FieldValidationException $e) {
+ $this->assertTrue($i > $max_length, "Length $i causes validation error when max_length is $max_length");
+ }
+ }
+ }
+
+ /**
+ * Test widgets.
+ */
+ function testTextfieldWidgets() {
+ $this->_testTextfieldWidgets('text', 'text_textfield');
+ $this->_testTextfieldWidgets('text_long', 'text_textarea');
+ }
+
+ /**
+ * Helper function for testTextfieldWidgets().
+ */
+ function _testTextfieldWidgets($field_type, $widget_type) {
+ // Setup a field and instance
+ $entity_type = 'test_entity';
+ $this->field_name = drupal_strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName() . '_label',
+ 'settings' => array(
+ 'text_processing' => TRUE,
+ ),
+ 'widget' => array(
+ 'type' => $widget_type,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Display creation form.
+ $this->drupalGet('test-entity/add/test-bundle');
+ $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();
+ $edit = array(
+ "{$this->field_name}[$langcode][0][value]" => $value,
+ );
+ $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)), '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->assertText($value, 'Filtered tags are not displayed');
+ }
+
+ /**
+ * Test widgets + 'formatted_text' setting.
+ */
+ function testTextfieldWidgetsFormatted() {
+ $this->_testTextfieldWidgetsFormatted('text', 'text_textfield');
+ $this->_testTextfieldWidgetsFormatted('text_long', 'text_textarea');
+ }
+
+ /**
+ * Helper function for testTextfieldWidgetsFormatted().
+ */
+ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
+ // Setup a field and instance
+ $entity_type = 'test_entity';
+ $this->field_name = drupal_strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName() . '_label',
+ 'settings' => array(
+ 'text_processing' => TRUE,
+ ),
+ 'widget' => array(
+ 'type' => $widget_type,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Disable all text formats besides the plain text fallback format.
+ $this->drupalLogin($this->admin_user);
+ foreach (filter_formats() as $format) {
+ if ($format->format != filter_fallback_format()) {
+ $this->drupalPost('admin/config/content/formats/' . $format->format . '/disable', array(), t('Disable'));
+ }
+ }
+ $this->drupalLogin($this->web_user);
+
+ // 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]", '', '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>';
+ $edit = array(
+ "{$this->field_name}[$langcode][0][value]" => $value,
+ );
+ $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)), '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, '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.
+ $this->drupalLogin($this->admin_user);
+ $edit = array(
+ 'format' => drupal_strtolower($this->randomName()),
+ 'name' => $this->randomName(),
+ );
+ $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
+ filter_formats_reset();
+ $this->checkPermissions(array(), TRUE);
+ $format = filter_format_load($edit['format']);
+ $format_id = $format->format;
+ $permission = filter_permission_name($format);
+ $rid = max(array_keys($this->web_user->roles));
+ user_role_grant_permissions($rid, array($permission));
+ $this->drupalLogin($this->web_user);
+
+ // 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, '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)), '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, 'Value is displayed unfiltered');
+ }
+}
+
+class TextSummaryTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text summary',
+ 'description' => 'Test text_summary() with different strings and lengths.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ $this->article_creator = $this->drupalCreateUser(array('create article content', 'edit own article content'));
+ }
+
+ /**
+ * Tests an edge case where the first sentence is a question and
+ * subsequent sentences are not. This edge case is documented at
+ * http://drupal.org/node/180425.
+ */
+ function testFirstSentenceQuestion() {
+ $text = 'A question? A sentence. Another sentence.';
+ $expected = 'A question? A sentence.';
+ $this->callTextSummary($text, $expected, NULL, 30);
+ }
+
+ /**
+ * Test summary with long example.
+ */
+ function testLongSentence() {
+ $text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . // 125
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' . // 108
+ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' . // 103
+ 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; // 110
+ $expected = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' .
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' .
+ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.';
+ // First three sentences add up to: 336, so add one for space and then 3 to get half-way into next word.
+ $this->callTextSummary($text, $expected, NULL, 340);
+ }
+
+ /**
+ * Test various summary length edge cases.
+ */
+ function testLength() {
+ // This string tests a number of edge cases.
+ $text = "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>";
+
+ // The summaries we expect text_summary() to return when $size is the index
+ // of each array item.
+ // Using no text format:
+ $expected = array(
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<",
+ "<p",
+ "<p>",
+ "<p>\n",
+ "<p>\nH",
+ "<p>\nHi",
+ "<p>\nHi\n",
+ "<p>\nHi\n<",
+ "<p>\nHi\n</",
+ "<p>\nHi\n</p",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ );
+
+ // And using a text format WITH the line-break and htmlcorrector filters.
+ $expected_lb = array(
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ );
+
+ // Test text_summary() for different sizes.
+ for ($i = 0; $i <= 37; $i++) {
+ $this->callTextSummary($text, $expected[$i], NULL, $i);
+ $this->callTextSummary($text, $expected_lb[$i], 'plain_text', $i);
+ $this->callTextSummary($text, $expected_lb[$i], 'filtered_html', $i);
+ }
+ }
+
+ /**
+ * Calls text_summary() and asserts that the expected teaser is returned.
+ */
+ function callTextSummary($text, $expected, $format = NULL, $size = NULL) {
+ $summary = text_summary($text, $format, $size);
+ $this->assertIdentical($summary, $expected, format_string('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
+ }
+
+ /**
+ * Test sending only summary.
+ */
+ function testOnlyTextSummary() {
+ // Login as article creator.
+ $this->drupalLogin($this->article_creator);
+ // Create article with summary but empty body.
+ $summary = $this->randomName();
+ $edit = array(
+ "title" => $this->randomName(),
+ "body[und][0][summary]" => $summary,
+ );
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($edit['title']);
+
+ $this->assertIdentical($node->body['und'][0]['summary'], $summary, 'Article with with summary and no body has been submitted.');
+ }
+}
+
+class TextTranslationTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text translation',
+ 'description' => 'Check if the text field is correctly prepared for translation.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale', 'translation');
+
+ $full_html_format = filter_format_load('full_html');
+ $this->format = $full_html_format->format;
+ $this->admin = $this->drupalCreateUser(array(
+ 'administer languages',
+ 'administer content types',
+ 'access administration pages',
+ 'bypass node access',
+ filter_permission_name($full_html_format),
+ ));
+ $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));
+
+ // Enable an additional language.
+ $this->drupalLogin($this->admin);
+ $edit = array('langcode' => 'fr');
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // 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')), 'Article content type has been updated.');
+ }
+
+ /**
+ * Test that a plaintext textfield widget is correctly populated.
+ */
+ function testTextField() {
+ // Disable text processing for body.
+ $edit = array('instance[settings][text_processing]' => 0);
+ $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings'));
+
+ // Login as translator.
+ $this->drupalLogin($this->translator);
+
+ // Create content.
+ $langcode = LANGUAGE_NONE;
+ $body = $this->randomName();
+ $edit = array(
+ "title" => $this->randomName(),
+ "language" => 'en',
+ "body[$langcode][0][value]" => $body,
+ );
+
+ // Translate the article in french.
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+ $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, 'The textfield widget is populated.');
+ }
+
+ /**
+ * Check that user that does not have access the field format cannot see the
+ * source value when creating a translation.
+ */
+ function testTextFieldFormatted() {
+ // Make node body multiple.
+ $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'), 'Body field cardinality set to multiple.');
+
+ $body = array(
+ $this->randomName(),
+ $this->randomName(),
+ );
+
+ // Create an article with the first body input format set to "Full HTML".
+ $title = $this->randomName();
+ $edit = array(
+ 'title' => $title,
+ 'language' => 'en',
+ );
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ // Populate the body field: the first item gets the "Full HTML" input
+ // format, the second one "Filtered HTML".
+ $formats = array('full_html', 'filtered_html');
+ $langcode = LANGUAGE_NONE;
+ foreach ($body as $delta => $value) {
+ $edit = array(
+ "body[$langcode][$delta][value]" => $value,
+ "body[$langcode][$delta][format]" => array_shift($formats),
+ );
+ $this->drupalPost('node/1/edit', $edit, t('Save'));
+ $this->assertText($body[$delta], format_string('The body field with delta @delta has been saved.', array('@delta' => $delta)));
+ }
+
+ // Login as translator.
+ $this->drupalLogin($this->translator);
+
+ // Translate the article in french.
+ $node = $this->drupalGetNodeByTitle($title);
+ $this->drupalGet("node/$node->nid/translate");
+ $this->clickLink(t('add translation'));
+ $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)));
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/tests/field.test b/kolab.org/www/drupal-7.26/modules/field/tests/field.test
new file mode 100644
index 0000000..1e59315
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/tests/field.test
@@ -0,0 +1,3709 @@
+<?php
+
+/**
+ * @file
+ * Tests for field.module.
+ */
+
+/**
+ * Parent class for Field API tests.
+ */
+class FieldTestCase extends DrupalWebTestCase {
+ var $default_storage = 'field_sql_storage';
+
+ /**
+ * Set the default field storage backend for fields created during tests.
+ */
+ function setUp() {
+ // Since this is a base class for many test cases, support the same
+ // flexibility that DrupalWebTestCase::setUp() has for the modules to be
+ // passed in as either an array or a variable number of string arguments.
+ $modules = func_get_args();
+ if (isset($modules[0]) && is_array($modules[0])) {
+ $modules = $modules[0];
+ }
+ parent::setUp($modules);
+ // Set default storage backend.
+ variable_set('field_storage_default', $this->default_storage);
+ }
+
+ /**
+ * Generate random values for a field_test field.
+ *
+ * @param $cardinality
+ * Number of values to generate.
+ * @return
+ * An array of random values, in the format expected for field values.
+ */
+ function _generateTestFieldValues($cardinality) {
+ $values = array();
+ for ($i = 0; $i < $cardinality; $i++) {
+ // field_test fields treat 0 as 'empty value'.
+ $values[$i]['value'] = mt_rand(1, 127);
+ }
+ return $values;
+ }
+
+ /**
+ * Assert that a field has the expected values in an entity.
+ *
+ * This function only checks a single column in the field values.
+ *
+ * @param $entity
+ * The entity to test.
+ * @param $field_name
+ * The name of the field to test
+ * @param $langcode
+ * The language code for the values.
+ * @param $expected_values
+ * The array of expected values.
+ * @param $column
+ * (Optional) the name of the column to check.
+ */
+ function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
+ $e = clone $entity;
+ field_attach_load('test_entity', array($e->ftid => $e));
+ $values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
+ $this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
+ foreach ($expected_values as $key => $value) {
+ $this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
+ }
+ }
+}
+
+class FieldAttachTestCase extends FieldTestCase {
+ function setUp() {
+ // Since this is a base class for many test cases, support the same
+ // flexibility that DrupalWebTestCase::setUp() has for the modules to be
+ // passed in as either an array or a variable number of string arguments.
+ $modules = func_get_args();
+ if (isset($modules[0]) && is_array($modules[0])) {
+ $modules = $modules[0];
+ }
+ if (!in_array('field_test', $modules)) {
+ $modules[] = 'field_test';
+ }
+ parent::setUp($modules);
+
+ $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',
+ 'description' => $this->randomName() . '_description',
+ 'weight' => mt_rand(0, 127),
+ 'settings' => array(
+ 'test_instance_setting' => $this->randomName(),
+ ),
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'label' => 'Test Field',
+ 'settings' => array(
+ 'test_widget_setting' => $this->randomName(),
+ )
+ )
+ );
+ field_create_instance($this->$instance);
+ }
+}
+
+/**
+ * Unit test class for storage-related field_attach_* functions.
+ *
+ * All field_attach_* test work with all field_storage plugins and
+ * all hook_field_attach_pre_{load,insert,update}() hooks.
+ */
+class FieldAttachStorageTestCase extends FieldAttachTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field attach tests (storage-related)',
+ 'description' => 'Test storage-related Field Attach API functions.',
+ 'group' => 'Field API',
+ );
+ }
+
+ /**
+ * Check field values insert, update and load.
+ *
+ * Works independently of the underlying field storage backend. Inserts or
+ * updates random field data and then loads and verifies the data.
+ */
+ function testFieldAttachSaveLoad() {
+ // Configure the instance so that we test hook_field_load() (see
+ // field_test_field_load() in field_test.module).
+ $this->instance['settings']['test_hook_field_load'] = TRUE;
+ field_update_instance($this->instance);
+ $langcode = LANGUAGE_NONE;
+
+ $entity_type = 'test_entity';
+ $values = array();
+
+ // TODO : test empty values filtering and "compression" (store consecutive deltas).
+
+ // Preparation: create three revisions and store them in $revision array.
+ for ($revision_id = 0; $revision_id < 3; $revision_id++) {
+ $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+ // Note: we try to insert one extra value.
+ $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+ $current_revision = $revision_id;
+ // If this is the first revision do an insert.
+ if (!$revision_id) {
+ $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
+ field_attach_insert($entity_type, $revision[$revision_id]);
+ }
+ else {
+ // Otherwise do an update.
+ $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
+ field_attach_update($entity_type, $revision[$revision_id]);
+ }
+ }
+
+ // Confirm current revision loads the correct data.
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ field_attach_load($entity_type, array(0 => $entity));
+ // Number of values per field loaded equals the field cardinality.
+ $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], 'Current revision: expected number of values');
+ for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+ // The field value loaded matches the one inserted or updated.
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'] , $values[$current_revision][$delta]['value'], format_string('Current revision: expected value %delta was found.', array('%delta' => $delta)));
+ // The value added in hook_field_load() is found.
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', format_string('Current revision: extra information for value %delta was found', array('%delta' => $delta)));
+ }
+
+ // Confirm each revision loads the correct data.
+ foreach (array_keys($revision) as $revision_id) {
+ $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array(0 => $entity));
+ // Number of values per field loaded equals the field cardinality.
+ $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
+ for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+ // The field value loaded matches the one inserted or updated.
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $values[$revision_id][$delta]['value'], format_string('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
+ // The value added in hook_field_load() is found.
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', format_string('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
+ }
+ }
+ }
+
+ /**
+ * Test the 'multiple' load feature.
+ */
+ function testFieldAttachLoadMultiple() {
+ $entity_type = 'test_entity';
+ $langcode = LANGUAGE_NONE;
+
+ // Define 2 bundles.
+ $bundles = array(
+ 1 => 'test_bundle_1',
+ 2 => 'test_bundle_2',
+ );
+ field_test_create_bundle($bundles[1]);
+ field_test_create_bundle($bundles[2]);
+ // Define 3 fields:
+ // - field_1 is in bundle_1 and bundle_2,
+ // - field_2 is in bundle_1,
+ // - field_3 is in bundle_2.
+ $field_bundles_map = array(
+ 1 => array(1, 2),
+ 2 => array(1),
+ 3 => array(2),
+ );
+ for ($i = 1; $i <= 3; $i++) {
+ $field_names[$i] = 'field_' . $i;
+ $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
+ $field = field_create_field($field);
+ $field_ids[$i] = $field['id'];
+ foreach ($field_bundles_map[$i] as $bundle) {
+ $instance = array(
+ 'field_name' => $field_names[$i],
+ 'entity_type' => 'test_entity',
+ 'bundle' => $bundles[$bundle],
+ 'settings' => array(
+ // Configure the instance so that we test hook_field_load()
+ // (see field_test_field_load() in field_test.module).
+ 'test_hook_field_load' => TRUE,
+ ),
+ );
+ field_create_instance($instance);
+ }
+ }
+
+ // Create one test entity per bundle, with random values.
+ foreach ($bundles as $index => $bundle) {
+ $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
+ $entity = clone($entities[$index]);
+ $instances = field_info_instances('test_entity', $bundle);
+ foreach ($instances as $field_name => $instance) {
+ $values[$index][$field_name] = mt_rand(1, 127);
+ $entity->$field_name = array($langcode => array(array('value' => $values[$index][$field_name])));
+ }
+ field_attach_insert($entity_type, $entity);
+ }
+
+ // Check that a single load correctly loads field values for both entities.
+ field_attach_load($entity_type, $entities);
+ foreach ($entities as $index => $entity) {
+ $instances = field_info_instances($entity_type, $bundles[$index]);
+ foreach ($instances as $field_name => $instance) {
+ // The field value loaded matches the one inserted.
+ $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], $values[$index][$field_name], format_string('Entity %index: expected value was found.', array('%index' => $index)));
+ // The value added in hook_field_load() is found.
+ $this->assertEqual($entity->{$field_name}[$langcode][0]['additional_key'], 'additional_value', format_string('Entity %index: extra information was found', array('%index' => $index)));
+ }
+ }
+
+ // Check that the single-field load option works.
+ $entity = field_test_create_stub_entity(1, 1, $bundles[1]);
+ field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_id' => $field_ids[1]));
+ $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['value'], $values[1][$field_names[1]], format_string('Entity %index: expected value was found.', array('%index' => 1)));
+ $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['additional_key'], 'additional_value', format_string('Entity %index: extra information was found', array('%index' => 1)));
+ $this->assert(!isset($entity->{$field_names[2]}), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
+ $this->assert(!isset($entity->{$field_names[3]}), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
+ }
+
+ /**
+ * Test saving and loading fields using different storage backends.
+ */
+ function testFieldAttachSaveLoadDifferentStorage() {
+ $entity_type = 'test_entity';
+ $langcode = LANGUAGE_NONE;
+
+ // Create two fields using different storage backends, and their instances.
+ $fields = array(
+ array(
+ 'field_name' => 'field_1',
+ 'type' => 'test_field',
+ 'cardinality' => 4,
+ 'storage' => array('type' => 'field_sql_storage')
+ ),
+ array(
+ 'field_name' => 'field_2',
+ 'type' => 'test_field',
+ 'cardinality' => 4,
+ 'storage' => array('type' => 'field_test_storage')
+ ),
+ );
+ foreach ($fields as $field) {
+ field_create_field($field);
+ $instance = array(
+ 'field_name' => $field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ );
+ field_create_instance($instance);
+ }
+
+ $entity_init = field_test_create_stub_entity();
+
+ // Create entity and insert random values.
+ $entity = clone($entity_init);
+ $values = array();
+ foreach ($fields as $field) {
+ $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity->{$field['field_name']}[$langcode] = $values[$field['field_name']];
+ }
+ field_attach_insert($entity_type, $entity);
+
+ // Check that values are loaded as expected.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ foreach ($fields as $field) {
+ $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}[$langcode], format_string('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
+ }
+ }
+
+ /**
+ * Test storage details alteration.
+ *
+ * @see field_test_storage_details_alter()
+ */
+ function testFieldStorageDetailsAlter() {
+ $field_name = 'field_test_change_my_details';
+ $field = array(
+ 'field_name' => $field_name,
+ 'type' => 'test_field',
+ 'cardinality' => 4,
+ 'storage' => array('type' => 'field_test_storage'),
+ );
+ $field = field_create_field($field);
+ $instance = array(
+ 'field_name' => $field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ );
+ field_create_instance($instance);
+
+ $field = field_info_field($instance['field_name']);
+ $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
+
+ // The storage details are indexed by a storage engine type.
+ $this->assertTrue(array_key_exists('drupal_variables', $field['storage']['details']), 'The storage type is Drupal variables.');
+
+ $details = $field['storage']['details']['drupal_variables'];
+
+ // The field_test storage details are indexed by variable name. The details
+ // are altered, so moon and mars are correct for this test.
+ $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), 'Moon is available in the instance array.');
+ $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), 'Mars is available in the instance array.');
+
+ // Test current and revision storage details together because the columns
+ // are the same.
+ foreach ((array) $field['columns'] as $column_name => $attributes) {
+ $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
+ $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
+ }
+ }
+
+ /**
+ * Tests insert and update with missing or NULL fields.
+ */
+ function testFieldAttachSaveMissingData() {
+ $entity_type = 'test_entity';
+ $entity_init = field_test_create_stub_entity();
+ $langcode = LANGUAGE_NONE;
+
+ // Insert: Field is missing.
+ $entity = clone($entity_init);
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), 'Insert: missing field results in no value saved');
+
+ // Insert: Field is NULL.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $entity->{$this->field_name} = NULL;
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), 'Insert: NULL field results in no value saved');
+
+ // Add some real data.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $values = $this->_generateTestFieldValues(1);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertEqual($entity->{$this->field_name}[$langcode], $values, 'Field data saved');
+
+ // Update: Field is missing. Data should survive.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ field_attach_update($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertEqual($entity->{$this->field_name}[$langcode], $values, 'Update: missing field leaves existing values in place');
+
+ // Update: Field is NULL. Data should be wiped.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $entity->{$this->field_name} = NULL;
+ field_attach_update($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), 'Update: NULL field removes existing values');
+
+ // Re-add some data.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $values = $this->_generateTestFieldValues(1);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_update($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertEqual($entity->{$this->field_name}[$langcode], $values, 'Field data saved');
+
+ // Update: Field is empty array. Data should be wiped.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $entity->{$this->field_name} = array();
+ field_attach_update($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), 'Update: empty array removes existing values');
+ }
+
+ /**
+ * Test insert with missing or NULL fields, with default value.
+ */
+ function testFieldAttachSaveMissingDataDefaultValue() {
+ // Add a default value function.
+ $this->instance['default_value_function'] = 'field_test_default_value';
+ field_update_instance($this->instance);
+
+ $entity_type = 'test_entity';
+ $entity_init = field_test_create_stub_entity();
+ $langcode = LANGUAGE_NONE;
+
+ // Insert: Field is NULL.
+ $entity = clone($entity_init);
+ $entity->{$this->field_name}[$langcode] = NULL;
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}[$langcode]), 'Insert: NULL field results in no value saved');
+
+ // Insert: Field is missing.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
+ $this->assertEqual($entity->{$this->field_name}[$langcode], $values, 'Insert: missing field results in default value saved');
+ }
+
+ /**
+ * Test field_attach_delete().
+ */
+ function testFieldAttachDelete() {
+ $entity_type = 'test_entity';
+ $langcode = LANGUAGE_NONE;
+ $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+
+ // Create revision 0
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+ $rev[0]->{$this->field_name}[$langcode] = $values;
+ field_attach_insert($entity_type, $rev[0]);
+
+ // Create revision 1
+ $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
+ $rev[1]->{$this->field_name}[$langcode] = $values;
+ field_attach_update($entity_type, $rev[1]);
+
+ // Create revision 2
+ $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+ $rev[2]->{$this->field_name}[$langcode] = $values;
+ field_attach_update($entity_type, $rev[2]);
+
+ // Confirm each revision loads
+ foreach (array_keys($rev) as $vid) {
+ $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array(0 => $read));
+ $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+ }
+
+ // Delete revision 1, confirm the other two still load.
+ field_attach_delete_revision($entity_type, $rev[1]);
+ foreach (array(0, 2) as $vid) {
+ $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array(0 => $read));
+ $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+ }
+
+ // Confirm the current revision still loads
+ $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+ field_attach_load($entity_type, array(0 => $read));
+ $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
+
+ // Delete all field data, confirm nothing loads
+ field_attach_delete($entity_type, $rev[2]);
+ foreach (array(0, 1, 2) as $vid) {
+ $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array(0 => $read));
+ $this->assertIdentical($read->{$this->field_name}, array(), "The test entity revision $vid is deleted.");
+ }
+ $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+ field_attach_load($entity_type, array(0 => $read));
+ $this->assertIdentical($read->{$this->field_name}, array(), 'The test entity current revision is deleted.');
+ }
+
+ /**
+ * Test field_attach_create_bundle() and field_attach_rename_bundle().
+ */
+ function testFieldAttachCreateRenameBundle() {
+ // Create a new bundle. This has to be initiated by the module so that its
+ // hook_entity_info() is consistent.
+ $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+ field_test_create_bundle($new_bundle);
+
+ // Add an instance to that bundle.
+ $this->instance['bundle'] = $new_bundle;
+ field_create_instance($this->instance);
+
+ // Save an entity with data in the field.
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity->{$this->field_name}[$langcode] = $values;
+ $entity_type = 'test_entity';
+ field_attach_insert($entity_type, $entity);
+
+ // Verify the field data is present on load.
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ field_attach_load($entity_type, array(0 => $entity));
+ $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
+
+ // Rename the bundle. This has to be initiated by the module so that its
+ // hook_entity_info() is consistent.
+ $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+ field_test_rename_bundle($this->instance['bundle'], $new_bundle);
+
+ // Check that the instance definition has been updated.
+ $this->instance = field_info_instance($entity_type, $this->field_name, $new_bundle);
+ $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
+
+ // Verify the field data is present on load.
+ $entity = field_test_create_stub_entity(0, 0, $new_bundle);
+ field_attach_load($entity_type, array(0 => $entity));
+ $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage");
+ }
+
+ /**
+ * Test field_attach_delete_bundle().
+ */
+ function testFieldAttachDeleteBundle() {
+ // Create a new bundle. This has to be initiated by the module so that its
+ // hook_entity_info() is consistent.
+ $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+ field_test_create_bundle($new_bundle);
+
+ // Add an instance to that bundle.
+ $this->instance['bundle'] = $new_bundle;
+ field_create_instance($this->instance);
+
+ // Create a second field for the test bundle
+ $field_name = drupal_strtolower($this->randomName() . '_field_name');
+ $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
+ field_create_field($field);
+ $instance = array(
+ 'field_name' => $field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => $this->instance['bundle'],
+ 'label' => $this->randomName() . '_label',
+ 'description' => $this->randomName() . '_description',
+ 'weight' => mt_rand(0, 127),
+ // test_field has no instance settings
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'settings' => array(
+ 'size' => mt_rand(0, 255))));
+ field_create_instance($instance);
+
+ // Save an entity with data for both fields
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity->{$this->field_name}[$langcode] = $values;
+ $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1);
+ field_attach_insert('test_entity', $entity);
+
+ // Verify the fields are present on load
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ field_attach_load('test_entity', array(0 => $entity));
+ $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
+ $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
+
+ // Delete the bundle. This has to be initiated by the module so that its
+ // hook_entity_info() is consistent.
+ field_test_delete_bundle($this->instance['bundle']);
+
+ // Verify no data gets loaded
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ field_attach_load('test_entity', array(0 => $entity));
+ $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field');
+ $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field');
+
+ // Verify that the instances are gone
+ $this->assertFalse(field_read_instance('test_entity', $this->field_name, $this->instance['bundle']), "First field is deleted");
+ $this->assertFalse(field_read_instance('test_entity', $field_name, $instance['bundle']), "Second field is deleted");
+ }
+}
+
+/**
+ * Unit test class for non-storage related field_attach_* functions.
+ */
+class FieldAttachOtherTestCase extends FieldAttachTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field attach tests (other)',
+ 'description' => 'Test other Field Attach API functions.',
+ 'group' => 'Field API',
+ );
+ }
+
+ /**
+ * 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);
+ $formatter_setting = $this->randomName();
+ $this->instance['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'field_test_default',
+ 'settings' => array(
+ 'test_formatter_setting' => $formatter_setting,
+ )
+ ),
+ );
+ 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'], "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);
+ $this->instance['display']['full']['label'] = 'hidden';
+ field_update_instance($this->instance);
+ 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->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
+
+ // Field hidden.
+ $entity = clone($entity_init);
+ $this->instance['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'hidden',
+ ),
+ );
+ field_update_instance($this->instance);
+ 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->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
+ foreach ($values as $delta => $value) {
+ $this->assertNoRaw("$formatter_setting|{$value['value']}", "Hidden field: value $delta is not displayed.");
+ }
+
+ // Multiple formatter.
+ $entity = clone($entity_init);
+ $formatter_setting = $this->randomName();
+ $this->instance['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'field_test_multiple',
+ 'settings' => array(
+ 'test_formatter_setting_multiple' => $formatter_setting,
+ )
+ ),
+ );
+ field_update_instance($this->instance);
+ 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);
+ $display = $formatter_setting;
+ foreach ($values as $delta => $value) {
+ $display .= "|$delta:{$value['value']}";
+ }
+ $this->content = $output;
+ $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
+
+ // Test a formatter that uses hook_field_formatter_prepare_view().
+ $entity = clone($entity_init);
+ $formatter_setting = $this->randomName();
+ $this->instance['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'field_test_with_prepare_view',
+ 'settings' => array(
+ 'test_formatter_setting_additional' => $formatter_setting,
+ )
+ ),
+ );
+ field_update_instance($this->instance);
+ 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;
+ foreach ($values as $delta => $value) {
+ $this->content = $output;
+ $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
+ $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
+ }
+
+ // TODO:
+ // - check display order with several fields
+
+ // Preprocess template.
+ $variables = array();
+ field_attach_preprocess($entity_type, $entity, $entity->content, $variables);
+ $result = TRUE;
+ foreach ($values as $delta => $item) {
+ if ($variables[$this->field_name][$delta]['value'] !== $item['value']) {
+ $result = FALSE;
+ break;
+ }
+ }
+ $this->assertTrue($result, format_string('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name)));
+ }
+
+ /**
+ * Tests the 'multiple entity' behavior of field_attach_prepare_view().
+ */
+ function testFieldAttachPrepareViewMultiple() {
+ $entity_type = 'test_entity';
+ $langcode = LANGUAGE_NONE;
+
+ // Set the instance to be hidden.
+ $this->instance['display']['full']['type'] = 'hidden';
+ field_update_instance($this->instance);
+
+ // Set up a second instance on another bundle, with a formatter that uses
+ // hook_field_formatter_prepare_view().
+ field_test_create_bundle('test_bundle_2');
+ $formatter_setting = $this->randomName();
+ $this->instance2 = $this->instance;
+ $this->instance2['bundle'] = 'test_bundle_2';
+ $this->instance2['display']['full'] = array(
+ 'type' => 'field_test_with_prepare_view',
+ 'settings' => array(
+ 'test_formatter_setting_additional' => $formatter_setting,
+ )
+ );
+ field_create_instance($this->instance2);
+
+ // Create one entity in each bundle.
+ $entity1_init = field_test_create_stub_entity(1, 1, 'test_bundle');
+ $values1 = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity1_init->{$this->field_name}[$langcode] = $values1;
+
+ $entity2_init = field_test_create_stub_entity(2, 2, 'test_bundle_2');
+ $values2 = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity2_init->{$this->field_name}[$langcode] = $values2;
+
+ // Run prepare_view, and check that the entities come out as expected.
+ $entity1 = clone($entity1_init);
+ $entity2 = clone($entity2_init);
+ field_attach_prepare_view($entity_type, array($entity1->ftid => $entity1, $entity2->ftid => $entity2), 'full');
+ $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
+ $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
+
+ // Same thing, reversed order.
+ $entity1 = clone($entity1_init);
+ $entity2 = clone($entity2_init);
+ field_attach_prepare_view($entity_type, array($entity2->ftid => $entity2, $entity1->ftid => $entity1), 'full');
+ $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
+ $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
+ }
+
+ /**
+ * Test field cache.
+ */
+ function testFieldAttachCache() {
+ // Initialize random values and a test entity.
+ $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+
+ // Non-cacheable entity type.
+ $entity_type = 'test_entity';
+ $cid = "field:$entity_type:{$entity_init->ftid}";
+
+ // Check that no initial cache entry is present.
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no initial cache entry');
+
+ // Save, and check that no cache entry is present.
+ $entity = clone($entity_init);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_insert($entity_type, $entity);
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no cache entry on insert');
+
+ // Load, and check that no cache entry is present.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no cache entry on load');
+
+
+ // Cacheable entity type.
+ $entity_type = 'test_cacheable_entity';
+ $cid = "field:$entity_type:{$entity_init->ftid}";
+ $instance = $this->instance;
+ $instance['entity_type'] = $entity_type;
+ field_create_instance($instance);
+
+ // Check that no initial cache entry is present.
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no initial cache entry');
+
+ // Save, and check that no cache entry is present.
+ $entity = clone($entity_init);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_insert($entity_type, $entity);
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on insert');
+
+ // Load a single field, and check that no cache entry is present.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
+ $cache = cache_get($cid, 'cache_field');
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on loading a single field');
+
+ // Load, and check that a cache entry is present with the expected values.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $cache = cache_get($cid, 'cache_field');
+ $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+
+ // Update with different values, and check that the cache entry is wiped.
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity = clone($entity_init);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_update($entity_type, $entity);
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on update');
+
+ // Load, and check that a cache entry is present with the expected values.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $cache = cache_get($cid, 'cache_field');
+ $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+
+ // Create a new revision, and check that the cache entry is wiped.
+ $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
+ $values = $this->_generateTestFieldValues($this->field['cardinality']);
+ $entity = clone($entity_init);
+ $entity->{$this->field_name}[$langcode] = $values;
+ field_attach_update($entity_type, $entity);
+ $cache = cache_get($cid, 'cache_field');
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on new revision creation');
+
+ // Load, and check that a cache entry is present with the expected values.
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $cache = cache_get($cid, 'cache_field');
+ $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+
+ // Delete, and check that the cache entry is wiped.
+ field_attach_delete($entity_type, $entity);
+ $this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry after delete');
+ }
+
+ /**
+ * Test field_attach_validate().
+ *
+ * Verify that field_attach_validate() invokes the correct
+ * 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 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;
+ }
+ // Arrange for item 1 not to generate an error
+ $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);
+ }
+ catch (FieldValidationException $e) {
+ $errors = $e->errors;
+ }
+
+ 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 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 first field's value $delta");
+ }
+ }
+ 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_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_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.');
+ }
+
+ /**
+ * Test field_attach_form().
+ *
+ * This could be much more thorough, but it does verify that the correct
+ * 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);
+
+ $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', "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() {
+ $this->createFieldWithInstance('_2');
+
+ $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_init, $form, $form_state);
+
+ // Simulate incoming values.
+ // First field.
+ $values = array();
+ $weights = array();
+ for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+ $values[$delta]['value'] = mt_rand(1, 127);
+ // Assign random weight.
+ do {
+ $weight = mt_rand(0, $this->field['cardinality']);
+ } while (in_array($weight, $weights));
+ $weights[$delta] = $weight;
+ $values[$delta]['_weight'] = $weight;
+ }
+ // Leave an empty value. 'field_test' fields are empty if empty().
+ $values[1]['value'] = 0;
+ // 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');
+ }
+}
+
+class FieldInfoTestCase extends FieldTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field info tests',
+ 'description' => 'Get information about existing fields, instances and bundles.',
+ 'group' => 'Field API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+ }
+
+ /**
+ * Test that field types and field definitions are correcly cached.
+ */
+ function testFieldInfo() {
+ // Test that field_test module's fields, widgets, and formatters show up.
+
+ $field_test_info = field_test_field_info();
+ // We need to account for the existence of user_field_info_alter().
+ foreach (array_keys($field_test_info) as $name) {
+ $field_test_info[$name]['instance_settings']['user_register_form'] = FALSE;
+ }
+ $info = field_info_field_types();
+ foreach ($field_test_info as $t_key => $field_type) {
+ foreach ($field_type as $key => $val) {
+ $this->assertEqual($info[$t_key][$key], $val, format_string('Field type %t_key key %key is %value', array('%t_key' => $t_key, '%key' => $key, '%value' => print_r($val, TRUE))));
+ }
+ $this->assertEqual($info[$t_key]['module'], 'field_test', "Field type field_test module appears");
+ }
+
+ $formatter_info = field_test_field_formatter_info();
+ $info = field_info_formatter_types();
+ foreach ($formatter_info as $f_key => $formatter) {
+ foreach ($formatter as $key => $val) {
+ $this->assertEqual($info[$f_key][$key], $val, format_string('Formatter type %f_key key %key is %value', array('%f_key' => $f_key, '%key' => $key, '%value' => print_r($val, TRUE))));
+ }
+ $this->assertEqual($info[$f_key]['module'], 'field_test', "Formatter type field_test module appears");
+ }
+
+ $widget_info = field_test_field_widget_info();
+ $info = field_info_widget_types();
+ foreach ($widget_info as $w_key => $widget) {
+ foreach ($widget as $key => $val) {
+ $this->assertEqual($info[$w_key][$key], $val, format_string('Widget type %w_key key %key is %value', array('%w_key' => $w_key, '%key' => $key, '%value' => print_r($val, TRUE))));
+ }
+ $this->assertEqual($info[$w_key]['module'], 'field_test', "Widget type field_test module appears");
+ }
+
+ $storage_info = field_test_field_storage_info();
+ $info = field_info_storage_types();
+ foreach ($storage_info as $s_key => $storage) {
+ foreach ($storage as $key => $val) {
+ $this->assertEqual($info[$s_key][$key], $val, format_string('Storage type %s_key key %key is %value', array('%s_key' => $s_key, '%key' => $key, '%value' => print_r($val, TRUE))));
+ }
+ $this->assertEqual($info[$s_key]['module'], 'field_test', "Storage type field_test module appears");
+ }
+
+ // Verify that no unexpected instances exist.
+ $instances = field_info_instances('test_entity');
+ $expected = array('test_bundle' => array());
+ $this->assertIdentical($instances, $expected, format_string("field_info_instances('test_entity') returns %expected.", array('%expected' => var_export($expected, TRUE))));
+ $instances = field_info_instances('test_entity', 'test_bundle');
+ $this->assertIdentical($instances, array(), "field_info_instances('test_entity', 'test_bundle') returns an empty array.");
+
+ // Create a field, verify it shows up.
+ $core_fields = field_info_fields();
+ $field = array(
+ 'field_name' => drupal_strtolower($this->randomName()),
+ 'type' => 'test_field',
+ );
+ field_create_field($field);
+ $fields = field_info_fields();
+ $this->assertEqual(count($fields), count($core_fields) + 1, 'One new field exists');
+ $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], 'info fields contains field name');
+ $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], 'info fields contains field type');
+ $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', 'info fields contains field module');
+ $settings = array('test_field_setting' => 'dummy test string');
+ foreach ($settings as $key => $val) {
+ $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
+ }
+ $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, 'info fields contains cardinality 1');
+ $this->assertEqual($fields[$field['field_name']]['active'], 1, 'info fields contains active 1');
+
+ // Create an instance, verify that it shows up
+ $instance = array(
+ 'field_name' => $field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName(),
+ 'description' => $this->randomName(),
+ 'weight' => mt_rand(0, 127),
+ // test_field has no instance settings
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'settings' => array(
+ 'test_setting' => 999)));
+ field_create_instance($instance);
+
+ $info = entity_get_info('test_entity');
+ $instances = field_info_instances('test_entity', $instance['bundle']);
+ $this->assertEqual(count($instances), 1, format_string('One instance shows up in info when attached to a bundle on a @label.', array(
+ '@label' => $info['label']
+ )));
+ $this->assertTrue($instance < $instances[$instance['field_name']], 'Instance appears in info correctly');
+
+ // Test a valid entity type but an invalid bundle.
+ $instances = field_info_instances('test_entity', 'invalid_bundle');
+ $this->assertIdentical($instances, array(), "field_info_instances('test_entity', 'invalid_bundle') returns an empty array.");
+
+ // Test invalid entity type and bundle.
+ $instances = field_info_instances('invalid_entity', $instance['bundle']);
+ $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity', 'test_bundle') returns an empty array.");
+
+ // Test invalid entity type, no bundle provided.
+ $instances = field_info_instances('invalid_entity');
+ $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity') returns an empty array.");
+
+ // Test with an entity type that has no bundles.
+ $instances = field_info_instances('user');
+ $expected = array('user' => array());
+ $this->assertIdentical($instances, $expected, format_string("field_info_instances('user') returns %expected.", array('%expected' => 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');
+ }
+
+ /**
+ * Test that cached field definitions are ready for current runtime context.
+ */
+ function testFieldPrepare() {
+ $field_definition = array(
+ 'field_name' => 'field',
+ 'type' => 'test_field',
+ );
+ field_create_field($field_definition);
+
+ // Simulate a stored field definition missing a field setting (e.g. a
+ // third-party module adding a new field setting has been enabled, and
+ // existing fields do not know the setting yet).
+ $data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
+ $data = unserialize($data);
+ $data['settings'] = array();
+ db_update('field_config')
+ ->fields(array('data' => serialize($data)))
+ ->condition('field_name', $field_definition['field_name'])
+ ->execute();
+
+ field_cache_clear();
+
+ // Read the field back.
+ $field = field_info_field($field_definition['field_name']);
+
+ // Check that all expected settings are in place.
+ $field_type = field_info_field_types($field_definition['type']);
+ $this->assertIdentical($field['settings'], $field_type['settings'], 'All expected default field settings are present.');
+ }
+
+ /**
+ * Test that cached instance definitions are ready for current runtime context.
+ */
+ function testInstancePrepare() {
+ $field_definition = array(
+ 'field_name' => 'field',
+ 'type' => 'test_field',
+ );
+ field_create_field($field_definition);
+ $instance_definition = array(
+ 'field_name' => $field_definition['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ );
+ field_create_instance($instance_definition);
+
+ // Simulate a stored instance definition missing various settings (e.g. a
+ // third-party module adding instance, widget or display settings has been
+ // enabled, but existing instances do not know the new settings).
+ $data = db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle']))->fetchField();
+ $data = unserialize($data);
+ $data['settings'] = array();
+ $data['widget']['settings'] = 'unavailable_widget';
+ $data['widget']['settings'] = array();
+ $data['display']['default']['type'] = 'unavailable_formatter';
+ $data['display']['default']['settings'] = array();
+ db_update('field_config_instance')
+ ->fields(array('data' => serialize($data)))
+ ->condition('field_name', $instance_definition['field_name'])
+ ->condition('bundle', $instance_definition['bundle'])
+ ->execute();
+
+ field_cache_clear();
+
+ // Read the instance back.
+ $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);
+
+ // Check that all expected instance settings are in place.
+ $field_type = field_info_field_types($field_definition['type']);
+ $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , 'All expected instance settings are present.');
+
+ // Check that the default widget is used and expected settings are in place.
+ $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], 'Unavailable widget replaced with default widget.');
+ $widget_type = field_info_widget_types($instance['widget']['type']);
+ $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , 'All expected widget settings are present.');
+
+ // Check that display settings are set for the 'default' mode.
+ $display = $instance['display']['default'];
+ $this->assertIdentical($display['type'], $field_type['default_formatter'], "Formatter is set for the 'default' view mode");