diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 73c4a83..aa4479c 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -689,13 +689,19 @@ function hook_field_is_empty($item, $field) {
* 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().
+ * which widget to use.
+ *
+ * Widgets are Plugins managed by the
+ * Drupal\field\Plugin\Type\Widget\WidgetPluginManager class. A widget is
+ * implemented by providing a class that implements
+ * Drupal\field\Plugin\Type\Widget\WidgetInterface (in most cases, by
+ * subclassing Drupal\field\Plugin\Type\Widget\WidgetBase), and provides the
+ * proper annotation block.
*
* 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().
+ * elements with additional processing capabilities. The methods of the
+ * WidgetInterface object 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
@@ -703,84 +709,11 @@ function hook_field_is_empty($item, $field) {
*/
/**
- * 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' => 9, 'summary_rows' => 3),
- '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.
+ * Array of informations on existing widget types, as collected by the
+ * annotation discovery mechanism.
*/
function hook_field_widget_info_alter(&$info) {
// Add a setting to a widget type.
@@ -789,93 +722,7 @@ function hook_field_widget_info_alter(&$info) {
);
// 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 $element;
+ $info['options_select']['field_types'][] = 'my_field_type';
}
/**
@@ -956,53 +803,27 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $cont
* of the hook involves reading from the database, it is highly recommended to
* statically cache the information.
*
- * @param $widget
+ * @param array $widget_properties
* The instance's widget properties.
- * @param $context
+ * @param array $context
* An associative array containing:
* - entity_type: The entity type; e.g., 'node' or 'user'.
- * - entity: The entity object.
+ * - bundle: The bundle: e.g., 'page' or 'article'.
* - field: The field that the widget belongs to.
* - instance: The instance of the field.
- * - default: A boolean indicating whether the form is being shown as a dummy
- * form to set default values.
*
* @see hook_field_widget_properties_ENTITY_TYPE_alter()
*/
-function hook_field_widget_properties_alter(&$widget, $context) {
+function hook_field_widget_properties_alter(array &$widget_properties, array $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';
+ $widget_properties['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".
*/
@@ -2381,25 +2202,23 @@ function hook_field_extra_fields_display_alter(&$displays, $context) {
* of the hook involves reading from the database, it is highly recommended to
* statically cache the information.
*
- * @param $widget
+ * @param array $widget_properties
* The instance's widget properties.
- * @param $context
+ * @param array $context
* An associative array containing:
* - entity_type: The entity type; e.g., 'node' or 'user'.
- * - entity: The entity object.
+ * - bundle: The bundle: e.g., 'page' or 'article'.
* - field: The field that the widget belongs to.
* - instance: The instance of the field.
- * - default: A boolean indicating whether the form is being shown as a dummy
- * form to set default values.
*
* @see hook_field_widget_properties_alter()
*/
-function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
+function hook_field_widget_properties_ENTITY_TYPE_alter(array &$widget_properties, array $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';
+ $widget_properties['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
}
}
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 7c2acdb..87bd5c2 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -108,6 +108,95 @@ const FIELD_STORAGE_INSERT = 'insert';
*/
/**
+ * Invoke a method on all the fields of a given entity.
+ *
+ * @todo Remove _field_invoke() and friends when field types and formatters are
+ * turned into plugins.
+ *
+ * @param string $method
+ * The name of the method to invoke.
+ * @param Closure $target
+ * A closure that receives an $instance object and returns the object on
+ * which the method should be invoked.
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The fully formed $entity_type entity.
+ * @param mixed $a
+ * A parameter for the invoked method. Defaults to NULL.
+ * @param mixed $b
+ * A parameter for the invoked method. Defaults to NULL.
+ * @param array $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.
+ * - 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.
+ * - langcode: 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_method($method, \Closure $target_closure, EntityInterface $entity, &$a = NULL, &$b = NULL, array $options = array()) {
+ // Merge default options.
+ $default_options = array(
+ 'deleted' => FALSE,
+ 'langcode' => NULL,
+ );
+ $options += $default_options;
+
+ $entity_type = $entity->entityType();
+ // Determine the list of instances to iterate on.
+ $instances = _field_invoke_get_instances($entity_type, $entity->bundle(), $options);
+
+ // Iterate through the instances and collect results.
+ $return = array();
+ foreach ($instances as $instance) {
+
+ // Let the closure determine the target object on which the method should be
+ // called.
+ $target = $target_closure($instance);
+
+ if (method_exists($target, $method)) {
+ $field = field_info_field_by_id($instance['field_id']);
+ $field_name = $field['field_name'];
+
+ // Determine the list of languages to iterate on.
+ $available_langcodes = field_available_languages($entity_type, $field);
+ $langcodes = _field_language_suggestion($available_langcodes, $options['langcode'], $field_name);
+
+ foreach ($langcodes as $langcode) {
+ $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
+
+ $result = $target->$method($entity, $langcode, $items, $a, $b);
+
+ if (isset($result)) {
+ // For methods with array results, we merge results together.
+ // For methods 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.
*
* @param $op
@@ -432,6 +521,20 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
}
/**
+ * Defines a 'target closure' for field_invoke_method().
+ *
+ * Used to invoke methods on an instance's widget.
+ *
+ * @return Closure
+ * A 'target closure' for field_invoke_method().
+ */
+function _field_invoke_widget_target() {
+ return function ($instance) {
+ return $instance->getWidget();
+ };
+}
+
+/**
* 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
@@ -543,7 +646,7 @@ function field_attach_form($entity_type, EntityInterface $entity, &$form, &$form
// If no language is provided use the default site language.
$options = array('langcode' => field_valid_language($langcode));
- $form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
+ $form += (array) field_invoke_method('form', _field_invoke_widget_target(), $entity, $form, $form_state, $options);
// Add custom weight handling.
$form['#pre_render'][] = '_field_extra_fields_pre_render';
@@ -795,9 +898,6 @@ function field_attach_validate($entity_type, $entity) {
* An associative array containing the current state of the form.
*/
function field_attach_form_validate($entity_type, EntityInterface $entity, $form, &$form_state) {
- // 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);
@@ -812,7 +912,7 @@ function field_attach_form_validate($entity_type, EntityInterface $entity, $form
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
}
}
- _field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state);
+ field_invoke_method('flagErrors', _field_invoke_widget_target(), $entity, $form, $form_state);
}
}
@@ -836,9 +936,7 @@ function field_attach_form_validate($entity_type, EntityInterface $entity, $form
*/
function field_attach_submit($entity_type, EntityInterface $entity, $form, &$form_state) {
// Extract field values from submitted values.
- _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
-
- _field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
+ field_invoke_method('submit', _field_invoke_widget_target(), $entity, $form, $form_state);
// Let other modules act on submitting the entity.
// Avoid module_invoke_all() to let $form_state be taken by reference.
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index f1e5481..6ac93fa 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -559,6 +559,14 @@ function _field_write_instance($instance, $update = FALSE) {
$field = field_read_field($instance['field_name']);
$field_type = field_info_field_types($field['type']);
+ // Temporary workaround to allow incoming $instance as arrays or classed
+ // objects.
+ // @todo remove once the external APIs have been converted to use
+ // FieldInstance objects.
+ if (is_object($instance) && get_class($instance) == 'Drupal\field\FieldInstance') {
+ $instance = $instance->getArray();
+ }
+
// Set defaults.
$instance += array(
'settings' => array(),
diff --git a/core/modules/field/field.default.inc b/core/modules/field/field.default.inc
index b4a6f50..7d1a309 100644
--- a/core/modules/field/field.default.inc
+++ b/core/modules/field/field.default.inc
@@ -11,39 +11,6 @@
*/
/**
- * 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:
@@ -86,13 +53,6 @@ function field_default_validate($entity_type, $entity, $field, $instance, $langc
}
}
-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.
*
diff --git a/core/modules/field/field.form.inc b/core/modules/field/field.form.inc
index 8e7dbaa..dae9ec9 100644
--- a/core/modules/field/field.form.inc
+++ b/core/modules/field/field.form.inc
@@ -6,290 +6,6 @@
*/
/**
- * 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) {
- $id = $entity->id();
- }
-
- $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,
- 'default' => !$entity,
- );
- 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_type' => $instance['entity_type'],
- '#entity' => $entity,
- '#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,
- 'default' => !$entity,
- );
- 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,
- 'default' => FALSE,
- );
- 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' => '
',
- '#suffix' => '
',
- '#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.
@@ -387,43 +103,6 @@ function field_form_element_after_build($element, &$form_state) {
}
/**
- * 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
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index 65c6bcc..41dd9a6 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -5,6 +5,8 @@
* Field Info API, providing information about available fields and field types.
*/
+use Drupal\field\FieldInstance;
+
/**
* @defgroup field_info Field Info API
* @{
@@ -88,7 +90,6 @@ function _field_info_collate_types() {
else {
$info = array(
'field types' => array(),
- 'widget types' => array(),
'formatter types' => array(),
'storage types' => array(),
);
@@ -108,21 +109,6 @@ function _field_info_collate_types() {
}
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');
@@ -227,7 +213,7 @@ function _field_info_collate_fields() {
foreach ($definitions['instances'] as $instance) {
$field = $info['fields'][$instance['field_id']];
$instance = _field_info_prepare_instance($instance, $field);
- $info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
+ $info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = new FieldInstance($instance);
// Enrich field definitions with the list of bundles where they have
// instances. NOTE: Deleted fields in $info['field_ids'] are not
// enriched because all of their instances are deleted, too, and
@@ -310,8 +296,6 @@ function _field_info_prepare_instance($instance, $field) {
$instance['default_value'] = NULL;
}
- $instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
-
foreach ($instance['display'] as $view_mode => $display) {
$instance['display'][$view_mode] = _field_info_prepare_instance_display($field, $display);
}
@@ -372,37 +356,6 @@ function _field_info_prepare_instance_display($field, $display) {
}
/**
- * Prepares widget specifications for the current run-time context.
- *
- * @param $field
- * The field structure for the instance.
- * @param $widget
- * Widget specifications as found in $instance['widget'].
- */
-function _field_info_prepare_instance_widget($field, $widget) {
- $field_type = field_info_field_types($field['type']);
-
- // Fill in default values.
- $widget += array(
- 'type' => $field_type['default_widget'],
- 'settings' => array(),
- 'weight' => 0,
- );
-
- $widget_type = field_info_widget_types($widget['type']);
- // Fallback to default formatter if formatter type is not available.
- if (!$widget_type) {
- $widget['type'] = $field_type['default_widget'];
- $widget_type = field_info_widget_types($widget['type']);
- }
- $widget['module'] = $widget_type['module'];
- // Fill in default settings for the widget.
- $widget['settings'] += field_info_widget_settings($widget['type']);
-
- return $widget;
-}
-
-/**
* Prepares 'extra fields' for the current run-time context.
*
* @param $extra_fields
@@ -496,27 +449,22 @@ function field_info_field_types($field_type = NULL) {
}
/**
- * Returns information about field widgets from hook_field_widget_info().
+ * Returns information about field widgets from AnnotatedClassDiscovery.
*
* @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.
+ * Either a single widget type description, as provided by class annotations
+ * 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];
- }
+ return field_get_plugin_manager('widget')->getDefinition($widget_type);
}
else {
- return $widget_types;
+ return field_get_plugin_manager('widget')->getDefinitions();
}
}
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 72b6d91..ce652cf 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -490,6 +490,35 @@ function field_associate_fields($module) {
}
/**
+ * Returns the PluginManager object for a given field plugin type.
+ *
+ * @param string $plugin_type
+ * The plugin type. One of:
+ * - field_type
+ * - widget
+ * - formatter
+ * - storage
+ *
+ * @return Drupal\Component\Plugin\PluginManagerInterface
+ * The PluginManager object.
+ */
+function field_get_plugin_manager($plugin_type) {
+ $plugin_types = &drupal_static(__FUNCTION__, array());
+
+ $classes = array(
+ 'widget' => 'Drupal\field\Plugin\Type\Widget\WidgetPluginManager',
+ );
+
+ if (isset($classes[$plugin_type])) {
+ if (!isset($plugin_types[$plugin_type])) {
+ $plugin_types[$plugin_type] = new $classes[$plugin_type]();
+ }
+
+ return $plugin_types[$plugin_type];
+ }
+}
+
+/**
* Helper function to get the default value for a field on an entity.
*
* @param $entity_type
diff --git a/core/modules/field/lib/Drupal/field/FieldInstance.php b/core/modules/field/lib/Drupal/field/FieldInstance.php
new file mode 100644
index 0000000..04eed11
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/FieldInstance.php
@@ -0,0 +1,127 @@
+definition = $definition;
+ }
+
+ /**
+ * Returns the Widget plugin for the instance.
+ *
+ * @return Drupal\field\Plugin\Type\Widget\WidgetInterface
+ * The Widget plugin to be used for the instance.
+ */
+ public function getWidget() {
+ if (empty($this->widget)) {
+ $widget_properties = $this->definition['widget'];
+
+ // Let modules alter the widget properties.
+ $context = array(
+ 'entity_type' => $this->definition['entity_type'],
+ 'bundle' => $this->definition['bundle'],
+ 'field' => field_info_field($this->definition['field_name']),
+ 'instance' => $this,
+ );
+ drupal_alter(array('field_widget_properties', 'field_widget_properties_' . $this->definition['entity_type']), $widget_properties, $context);
+
+ $options = array(
+ 'instance' => $this,
+ 'type' => $widget_properties['type'],
+ 'settings' => $widget_properties['settings'],
+ 'weight' => $widget_properties['weight'],
+ );
+ $this->widget = field_get_plugin_manager('widget')->getInstance($options);
+ }
+
+ return $this->widget;
+ }
+
+ /**
+ * Implements ArrayAccess::offsetExists().
+ */
+ public function offsetExists($offset) {
+ return isset($this->definition[$offset]) || array_key_exists($offset, $this->definition);
+ }
+
+ /**
+ * Implements ArrayAccess::offsetGet().
+ */
+ public function &offsetGet($offset) {
+ return $this->definition[$offset];
+ }
+
+ /**
+ * Implements ArrayAccess::offsetSet().
+ */
+ public function offsetSet($offset, $value) {
+ if (!isset($offset)) {
+ // Do nothing; $array[] syntax is not supported by this temporary wrapper.
+ return;
+ }
+ $this->definition[$offset] = $value;
+
+ // If the widget properties changed, the widget plugin needs to be
+ // re-instanciated.
+ if ($offset == 'widget') {
+ unset($this->widget);
+ }
+ }
+
+ /**
+ * Implements ArrayAccess::offsetUnset().
+ */
+ public function offsetUnset($offset) {
+ unset($this->definition[$offset]);
+
+ // If the widget properties changed, the widget plugin needs to be
+ // re-instanciated.
+ if ($offset == 'widget') {
+ unset($this->widget);
+ }
+ }
+
+ /**
+ * Returns the instance definition as a regular array.
+ *
+ * This is used as a temporary BC layer.
+ * @todo Remove once the external APIs have been converted to use
+ * FieldInstance objects.
+ *
+ * @return array
+ * The instance definition as a regular array.
+ */
+ public function getArray() {
+ return $this->definition;
+ }
+
+ /**
+ * Handles serialization of Drupal\field\FieldInstance objects.
+ */
+ public function __sleep() {
+ return array('definition');
+ }
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php
new file mode 100644
index 0000000..be22874
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php
@@ -0,0 +1,89 @@
+defaultSettingsMerged) {
+ $this->mergeDefaults();
+ }
+ return $this->settings;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\PluginSettingsInterface::getSetting().
+ */
+ public function getSetting($key) {
+ // Merge defaults if we have no value for the key.
+ if (!$this->defaultSettingsMerged && !array_key_exists($key, $this->settings)) {
+ $this->mergeDefaults();
+ }
+ return isset($this->settings[$key]) ? $this->settings[$key] : NULL;
+ }
+
+ /**
+ * Merges default settings values into $settings.
+ */
+ protected function mergeDefaults() {
+ $this->settings += $this->getDefaultSettings();
+ $this->defaultSettingsMerged = TRUE;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\PluginSettingsInterface::getDefaultSettings().
+ */
+ public function getDefaultSettings() {
+ $definition = $this->getDefinition();
+ return $definition['settings'];
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\PluginSettingsInterface::setSettings().
+ */
+ public function setSettings(array $settings) {
+ $this->settings = $settings;
+ $this->defaultSettingsMerged = FALSE;
+ return $this;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\PluginSettingsInterface::setSetting().
+ */
+ public function setSetting($key, $value) {
+ $this->settings[$key] = $value;
+ return $this;
+ }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php
new file mode 100644
index 0000000..955e88d
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php
@@ -0,0 +1,69 @@
+decorated = $decorated;
+ }
+
+ /**
+ * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
+ */
+ public function getDefinition($plugin_id) {
+ $definitions = $this->getDefinitions();
+ return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
+ }
+
+ /**
+ * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
+ */
+ public function getDefinitions() {
+ $definitions = $this->decorated->getDefinitions();
+
+ $legacy_discovery = new HookDiscovery('field_widget_info');
+ if ($legacy_definitions = $legacy_discovery->getDefinitions()) {
+ foreach ($legacy_definitions as $plugin_id => &$definition) {
+ $definition['class'] = '\Drupal\field\Plugin\field\widget\LegacyWidget';
+
+ // Transform properties for which the format has changed.
+ if (isset($definition['field types'])) {
+ $definition['field_types'] = $definition['field types'];
+ unset($definition['field types']);
+ }
+ if (isset($definition['behaviors']['multiple values'])) {
+ $definition['multiple_values'] = $definition['behaviors']['multiple values'];
+ unset($definition['behaviors']['multiple values']);
+ }
+
+ $definitions[$plugin_id] = $definition;
+ }
+ }
+ return $definitions;
+ }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
new file mode 100644
index 0000000..6b653ff
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
@@ -0,0 +1,447 @@
+instance = $instance;
+ $this->field = field_info_field($instance['field_name']);
+ $this->settings = $settings;
+ $this->weight = $weight;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::form().
+ */
+ public function form(EntityInterface $entity, $langcode, array $items, array &$form, array &$form_state, $get_delta = NULL) {
+ $entity_type = $entity->entityType();
+ $field = $this->field;
+ $instance = $this->instance;
+ $field_name = $field['field_name'];
+
+ $parents = $form['#parents'];
+
+ $addition = array(
+ $field_name => array(),
+ );
+
+ // Populate widgets with default values when creating a new entity.
+ if (empty($items) && ($entity->isNew())) {
+ $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
+ }
+
+ // 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);
+ }
+
+ // Collect widget elements.
+ $elements = array();
+
+ // 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.
+ $definition = $this->getDefinition();
+ if (isset($get_delta) || $definition['multiple_values']) {
+ $delta = isset($get_delta) ? $get_delta : 0;
+ $element = array(
+ '#title' => check_plain($instance['label']),
+ '#description' => field_filter_xss($instance['description']),
+ );
+ $element = $this->formSingleElement($entity, $items, $delta, $langcode, $element, $form, $form_state);
+
+ if ($element) {
+ if (isset($get_delta)) {
+ // If we are processing a specific delta value for a field where the
+ // field module handles multiples, set the delta in the result.
+ $elements[$delta] = $element;
+ }
+ else {
+ // For fields that handle their own processing, we cannot make
+ // assumptions about how the field is structured, just merge in the
+ // returned element.
+ $elements = $element;
+ }
+ }
+ }
+ // If the widget does not handle multiple values itself, (and we are not
+ // displaying an individual element), process the multiple value form.
+ else {
+ $elements = $this->formMultipleElements($entity, $items, $langcode, $form, $form_state);
+ }
+
+ // 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($this->getPluginId()),
+ ),
+ ),
+ '#weight' => $this->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
+ * - table display and drag-n-drop value reordering
+ */
+ protected function formMultipleElements(EntityInterface $entity, array $items, $langcode, array &$form, array &$form_state) {
+ $field = $this->field;
+ $instance = $this->instance;
+ $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'];
+ $is_multiple = TRUE;
+ break;
+
+ default:
+ $max = $field['cardinality'] - 1;
+ $is_multiple = ($field['cardinality'] > 1);
+ break;
+ }
+
+ $id_prefix = implode('-', array_merge($parents, array($field_name)));
+ $wrapper_id = drupal_html_id($id_prefix . '-add-more-wrapper');
+
+ $title = check_plain($instance['label']);
+ $description = field_filter_xss($instance['description']);
+
+ $elements = array();
+
+ for ($delta = 0; $delta <= $max; $delta++) {
+ // For multiple fields, title and description are handled by the wrapping
+ // table.
+ $element = array(
+ '#title' => $is_multiple ? '' : $title,
+ '#description' => $is_multiple ? '' : $description,
+ );
+ $element = $this->formSingleElement($entity, $items, $delta, $langcode, $element, $form, $form_state);
+
+ if ($element) {
+ // Input field for the delta (drag-n-drop reordering).
+ if ($is_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 #type 'weight' element's property.
+ '#delta' => $max,
+ '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
+ '#weight' => 100,
+ );
+ }
+
+ $elements[$delta] = $element;
+ }
+ }
+
+ if ($elements) {
+ $elements += array(
+ '#theme' => 'field_multiple_value_form',
+ '#field_name' => $field['field_name'],
+ '#cardinality' => $field['cardinality'],
+ '#required' => $instance['required'],
+ '#title' => $title,
+ '#description' => $description,
+ '#prefix' => '',
+ '#suffix' => '
',
+ '#max_delta' => $max,
+ );
+
+ // Add 'add more' button, if not working with a programmed form.
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
+ $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 $elements;
+ }
+
+ /**
+ * Generates the form element for a single copy of the widget.
+ */
+ protected function formSingleElement(EntityInterface $entity, array $items, $delta, $langcode, array $element, array &$form, array &$form_state) {
+ $instance = $this->instance;
+ $field = $this->field;
+
+ $element += array(
+ '#entity_type' => $entity->entityType(),
+ '#bundle' => $entity->bundle(),
+ '#entity' => $entity,
+ '#field_name' => $field['field_name'],
+ '#language' => $langcode,
+ '#field_parents' => $form['#parents'],
+ '#columns' => array_keys($field['columns']),
+ // Only the first widget should be required.
+ '#required' => $delta == 0 && $instance['required'],
+ '#delta' => $delta,
+ '#weight' => $delta,
+ );
+
+ $element = $this->formElement($items, $delta, $element, $langcode, $form, $form_state);
+
+ if ($element) {
+ // Allow modules to alter the field widget form element.
+ $context = array(
+ 'form' => $form,
+ 'field' => $field,
+ 'instance' => $instance,
+ 'langcode' => $langcode,
+ 'items' => $items,
+ 'delta' => $delta,
+ 'default' => !empty($entity->field_ui_default_value),
+ );
+ drupal_alter(array('field_widget_form', 'field_widget_' . $this->getPluginId() . '_form'), $element, $form_state, $context);
+ }
+
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::submit().
+ */
+ public function submit(EntityInterface $entity, $langcode, array &$items, array $form, array &$form_state) {
+ $field_name = $this->field['field_name'];
+
+ // Extract the values from $form_state['values'].
+ $path = array_merge($form['#parents'], array($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']);
+
+ // Let the widget turn the submitted values into actual field values.
+ // Make sure the '_weight' entries are persisted in the process.
+ $weights = array();
+ if (isset($values[0]['_weight'])) {
+ foreach ($values as $delta => $value) {
+ $weights[$delta] = $value['_weight'];
+ }
+ }
+ $items = $this->massageFormValues($values, $form, $form_state);
+
+ foreach ($items as $delta => &$item) {
+ // Put back the weight.
+ if (isset($weights[$delta])) {
+ $item['_weight'] = $weights[$delta];
+ }
+ // The tasks below are going to reshuffle deltas. Keep track of the
+ // original deltas for correct reporting of errors in flagErrors().
+ $item['_original_delta'] = $delta;
+ }
+
+ // Account for drag-n-drop reordering.
+ $this->sortItems($items);
+
+ // Remove empty values.
+ $items = _field_filter_items($this->field, $items);
+
+ // Put delta mapping in $form_state, so that flagErrors() can use it.
+ $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+ foreach ($items as $delta => &$item) {
+ $field_state['original_deltas'][$delta] = $item['_original_delta'];
+ unset($item['_original_delta']);
+ }
+ field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
+ }
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::flagErrors().
+ */
+ public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state) {
+ $field_name = $this->field['field_name'];
+
+ $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+
+ if (!empty($field_state['errors'])) {
+ // Locate the correct element in the 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']) {
+ $definition = $this->getDefinition();
+ $is_multiple = $definition['multiple_values'];
+
+ foreach ($field_state['errors'] as $delta => $delta_errors) {
+ // For a multiple-value widget, pass all errors to the main widget.
+ // For single-value widgets, pass errors by delta.
+ if ($is_multiple) {
+ $delta_element = $element;
+ }
+ else {
+ $original_delta = $field_state['original_deltas'][$delta];
+ $delta_element = $element[$original_delta];
+ }
+ foreach ($delta_errors as $error) {
+ $error_element = $this->errorElement($delta_element, $error, $form, $form_state);
+ 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_name, $langcode, $form_state, $field_state);
+ }
+ }
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+ */
+ public function settingsForm(array $form, array &$form_state) {
+ return array();
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+ */
+ public function errorElement(array $element, array $error, array $form, array &$form_state) {
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::massageFormValues()
+ */
+ public function massageFormValues(array $values, array $form, array &$form_state) {
+ return $values;
+ }
+
+ /**
+ * Sorts submitted field values according to drag-n-drop reordering.
+ *
+ * @param array $items
+ * The field values.
+ */
+ protected function sortItems(array &$items) {
+ $is_multiple = ($this->field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) || ($this->field['cardinality'] > 1);
+ if ($is_multiple && isset($items[0]['_weight'])) {
+ usort($items, function ($a, $b) {
+ $a_weight = (is_array($a) ? $a['_weight'] : 0);
+ $b_weight = (is_array($b) ? $b['_weight'] : 0);
+ return $a_weight - $b_weight;
+ });
+ // Remove the '_weight' entries.
+ foreach ($items as $delta => &$item) {
+ if (is_array($item)) {
+ unset($item['_weight']);
+ }
+ }
+ }
+ }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php
new file mode 100644
index 0000000..8fdd785
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php
@@ -0,0 +1,85 @@
+isNew() is
+ * TRUE), the 'default value', if any, is pre-populated. Also allows other
+ * modules to alter the form element by implementing their own hooks.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The entity for which the widget is being built.
+ * @param string $langcode
+ * The language associated with the field.
+ * @param array $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 array $form
+ * An array representing the form that the editing element will be attached
+ * to.
+ * @param array $form_state
+ * An array containing the current state of the form.
+ * @param int $get_delta
+ * Used to get only a specific delta value of a multiple value field.
+ *
+ * @return array
+ * The form element array created for this field.
+ */
+ public function form(EntityInterface $entity, $langcode, array $items, array &$form, array &$form_state, $get_delta = NULL);
+
+ /**
+ * Extracts field values from submitted form values.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The entity for which the widget is being submitted.
+ * @param string $langcode
+ * The language associated to $items.
+ * @param array $items
+ * The field values. This parameter is altered by reference to receive the
+ * incoming form values.
+ * @param array $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 array $form_state
+ * The form state.
+ */
+ public function submit(EntityInterface $entity, $langcode, array &$items, array $form, array &$form_state);
+
+ /**
+ * Reports field-level validation errors against actual form elements.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The entity for which the widget is being submitted.
+ * @param string $langcode
+ * The language associated to $items.
+ * @param array $items
+ * The field values.
+ * @param array $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 array $form_state
+ * The form state.
+ */
+ public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state);
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php
new file mode 100644
index 0000000..851cbea
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php
@@ -0,0 +1,24 @@
+getPluginClass($plugin_id);
+ return new $plugin_class($plugin_id, $this->discovery, $configuration['instance'], $configuration['settings'], $configuration['weight']);
+ }
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php
new file mode 100644
index 0000000..c0ffc95
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php
@@ -0,0 +1,164 @@
+ array(),
+ 'multiple_values' => FALSE,
+ );
+
+ protected $cache_bin = 'field';
+ protected $cache_key = 'field_widget_types';
+ protected $hook = 'field_widget_info';
+
+ /**
+ * Constructs a WidgetPluginManager object.
+ */
+ public function __construct() {
+ $this->baseDiscovery = new LegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'widget'));
+ $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_key, $this->cache_bin);
+
+ $this->factory = new WidgetFactory($this);
+ }
+
+ /**
+ * Clear cached definitions.
+ *
+ * @todo Remove when http://drupal.org/node/1764232 is fixed.
+ */
+ public function clearDefinitions() {
+ // Clear 'static' data by creating a new object.
+ $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_key, $this->cache_bin);
+ cache($this->cache_bin)->delete($this->cache_key);
+ }
+
+ /**
+ * Overrides Drupal\Component\Plugin\PluginManagerBase::getDefinition().
+ *
+ * @todo Remove when http://drupal.org/node/1778942 is fixed.
+ */
+ public function getDefinition($plugin_id) {
+ $definition = $this->discovery->getDefinition($plugin_id);
+ if (!empty($definition)) {
+ $this->processDefinition($definition, $plugin_id);
+ return $definition;;
+ }
+ }
+
+ /**
+ * Overrides Drupal\Component\Plugin\PluginManagerBase::getInstance().
+ */
+ public function getInstance(array $options) {
+ $instance = $options['instance'];
+ $type = $options['type'];
+
+ $definition = $this->getDefinition($type);
+ $field = field_info_field($instance['field_name']);
+
+ // Switch back to default widget if either:
+ // - $type_info doesn't exist (the widget type is unknown),
+ // - the field type is not allowed for the widget.
+ if (!isset($definition['class']) || !in_array($field['type'], $definition['field_types'])) {
+ // Grab the default widget for the field type.
+ $field_type_definition = field_info_field_types($field['type']);
+ $type = $field_type_definition['default_widget'];
+ }
+
+ $configuration = array(
+ 'instance' => $instance,
+ 'settings' => $options['settings'],
+ 'weight' => $options['weight'],
+ );
+ return $this->createInstance($type, $configuration);
+ }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php b/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
new file mode 100644
index 0000000..166bd87
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
@@ -0,0 +1,106 @@
+getDefinition();
+ $function = $definition['module'] . '_field_widget_settings_form';
+ if (function_exists($function)) {
+ return $function($this->field, $this->instance);
+ }
+ return array();
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+ */
+ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+ $definition = $this->getDefinition();
+ $function = $definition['module'] . '_field_widget_form';
+
+ if (function_exists($function)) {
+ // hook_field_widget_form() implementations read widget properties directly
+ // from $instance. Put the actual properties we use here, which might have
+ // been altered by hook_field_widget_property().
+ $instance = clone $this->instance;
+ $instance['widget']['type'] = $this->getPluginId();
+ $instance['widget']['settings'] = $this->getSettings();
+
+ return $function($form, $form_state, $this->field, $instance, $langcode, $items, $delta, $element);
+ }
+ return array();
+ }
+
+ /**
+ * Overrides Drupal\field\Plugin\Type\Widget\WidgetBase::flagErrors().
+ *
+ * In D7, hook_field_widget_error() was supposed to call form_error() itself,
+ * whereas the new errorElement() method simply returns the element to flag.
+ * So we override the flagError() method to be more similar to the previous
+ * code in field_default_form_errors().
+ */
+ public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state) {
+ $field_name = $this->field['field_name'];
+
+ $field_state = field_form_get_state($form['#parents'], $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']) {
+ $definition = $this->getDefinition();
+ $is_multiple = $definition['multiple_values'];
+ $function = $definition['module'] . '_field_widget_error';
+ $function_exists = function_exists($function);
+
+ 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 = $is_multiple ? $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_name, $langcode, $form_state, $field_state);
+ }
+ }
+ }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 7155af9..28112d2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -52,15 +52,6 @@ class FieldInfoTest extends FieldTestBase {
$this->assertEqual($info[$f_key]['module'], 'field_test', t("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, t("Widget type $w_key key $key is $val"));
- }
- $this->assertEqual($info[$w_key]['module'], 'field_test', t("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) {
@@ -208,9 +199,10 @@ class FieldInfoTest extends FieldTestBase {
$this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('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'], t('Unavailable widget replaced with default widget.'));
- $widget_type = field_info_widget_types($instance['widget']['type']);
- $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
+ $widget = $instance->getWidget();
+ $this->assertIdentical($widget->getPluginId(), $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
+ $widget_type = $widget->getDefinition();
+ $this->assertIdentical($widget->getSettings(), $widget_type['settings'] , t('All expected widget settings are present.'));
// Check that display settings are set for the 'default' mode.
$display = $instance['display']['default'];
@@ -256,9 +248,9 @@ class FieldInfoTest extends FieldTestBase {
$this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
}
- $info = field_test_field_widget_info();
- foreach ($info as $type => $data) {
- $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
+ foreach (array('test_field_widget', 'test_field_widget_multiple') as $type) {
+ $info = field_info_widget_types($type);
+ $this->assertIdentical(field_info_widget_settings($type), $info['settings'], "field_info_widget_settings returns {$type}'s widget settings");
}
$info = field_test_field_formatter_info();
diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
index dea9dc5..2e6121e 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
@@ -67,9 +67,8 @@ class FormTest extends FieldTestBase {
$this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
// TODO : check that the widget is populated with default value ?
- // Check that neither hook_field_widget_properties_alter nor
- // hook_field_widget_form_alter() believe this is the default value form.
- $this->assertNoText('From hook_field_widget_properties_alter(): Default form is true.', 'Not default value form in hook_field_widget_properties_alter().');
+ // Check that hook_field_widget_form_alter() does not believe this is the
+ // default value form.
$this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
// Submit with invalid value (field-level validation).
diff --git a/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php b/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php
new file mode 100644
index 0000000..ecaf204
--- /dev/null
+++ b/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php
@@ -0,0 +1,83 @@
+field;
+ $instance = $this->instance;
+
+ $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL;
+
+ $element += array(
+ '#type' => 'number',
+ '#default_value' => $value,
+ );
+
+ // Set the step for floating point and decimal numbers.
+ switch ($field['type']) {
+ case 'number_decimal':
+ $element['#step'] = pow(0.1, $field['settings']['scale']);
+ break;
+
+ case 'number_float':
+ $element['#step'] = 'any';
+ break;
+ }
+
+ // Set minimum and maximum.
+ if (is_numeric($instance['settings']['min'])) {
+ $element['#min'] = $instance['settings']['min'];
+ }
+ if (is_numeric($instance['settings']['max'])) {
+ $element['#max'] = $instance['settings']['max'];
+ }
+
+ // 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));
+ }
+
+ return array('value' => $element);
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+ */
+ public function errorElement(array $element, array $error, array $form, array &$form_state) {
+ return $element['value'];
+ }
+
+}
diff --git a/core/modules/field/modules/number/number.module b/core/modules/field/modules/number/number.module
index 9985984..1a7024b 100644
--- a/core/modules/field/modules/number/number.module
+++ b/core/modules/field/modules/number/number.module
@@ -304,65 +304,3 @@ function number_field_formatter_view($entity_type, $entity, $field, $instance, $
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'] : '';
-
- $element += array(
- '#type' => 'number',
- '#default_value' => $value,
- );
-
- // Set the step for floating point and decimal numbers.
- switch ($field['type']) {
- case 'number_decimal':
- $element['#step'] = pow(0.1, $field['settings']['scale']);
- break;
-
- case 'number_float':
- $element['#step'] = 'any';
- break;
- }
-
- // Set minimum and maximum.
- if (is_numeric($instance['settings']['min'])) {
- $element['#min'] = $instance['settings']['min'];
- }
- if (is_numeric($instance['settings']['max'])) {
- $element['#max'] = $instance['settings']['max'];
- }
-
- // 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));
- }
-
- return array('value' => $element);
-}
-
-/**
- * Implements hook_field_widget_error().
- */
-function number_field_widget_error($element, $error, $form, &$form_state) {
- form_error($element['value'], $error['message']);
-}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php
new file mode 100644
index 0000000..17f80a0
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php
@@ -0,0 +1,69 @@
+ 'number',
+ '#title' => t('Rows'),
+ '#default_value' => $this->getSetting('rows'),
+ '#required' => TRUE,
+ '#min' => 1,
+ );
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+ */
+ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+ $main_widget = $element + array(
+ '#type' => 'textarea',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#rows' => $this->getSetting('rows'),
+ '#attributes' => array('class' => array('text-full')),
+ );
+
+ if ($this->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;
+ }
+
+ return $element;
+ }
+
+}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php
new file mode 100644
index 0000000..373f1ed
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php
@@ -0,0 +1,73 @@
+instance['settings']['display_summary'];
+ $element['summary'] = array(
+ '#type' => $display_summary ? 'textarea' : 'value',
+ '#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
+ '#title' => t('Summary'),
+ '#rows' => $this->getSetting('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' => '',
+ '#suffix' => '
',
+ '#weight' => -10,
+ );
+
+ return $element;
+ }
+
+ /**
+ * Overrides TextareaWidget::errorElement().
+ */
+ public function errorElement(array $element, array $error, array $form, array &$form_state) {
+ switch ($error['error']) {
+ case 'text_summary_max_length':
+ $error_element = $element['summary'];
+ break;
+
+ default:
+ $error_element = $element;
+ break;
+ }
+
+ return $error_element;
+ }
+
+}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php
new file mode 100644
index 0000000..e6b87b1
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php
@@ -0,0 +1,70 @@
+ 'number',
+ '#title' => t('Size of textfield'),
+ '#default_value' => $this->getSetting('size'),
+ '#required' => TRUE,
+ '#min' => 1,
+ );
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+ */
+ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+ $main_widget = $element + array(
+ '#type' => 'textfield',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#size' => $this->getSetting('size'),
+ '#maxlength' => $this->field['settings']['max_length'],
+ '#attributes' => array('class' => array('text-full')),
+ );
+
+ if ($this->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;
+ }
+
+ return $element;
+ }
+
+}
diff --git a/core/modules/field/modules/text/text.module b/core/modules/field/modules/text/text.module
index dcf4d1e..bbd209e 100644
--- a/core/modules/field/modules/text/text.module
+++ b/core/modules/field/modules/text/text.module
@@ -444,140 +444,6 @@ function text_summary($text, $format = NULL, $size = NULL) {
}
/**
- * 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' => 9, 'summary_rows' => 3),
- ),
- );
-}
-
-/**
- * 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' => 'number',
- '#title' => t('Size of textfield'),
- '#default_value' => $settings['size'],
- '#required' => TRUE,
- '#min' => 1,
- );
- }
- else {
- $form['rows'] = array(
- '#type' => 'number',
- '#title' => t('Rows'),
- '#default_value' => $settings['rows'],
- '#required' => TRUE,
- '#min' => 1,
- );
- }
-
- 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' => '',
- '#suffix' => '
',
- '#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) {
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index 4819b3f..7acb4bb 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -337,9 +337,11 @@ function field_test_entity_nested_form($form, &$form_state, $entity_1, $entity_2
*/
function field_test_entity_nested_form_validate($form, &$form_state) {
$entity_1 = entity_create('test_entity', $form_state['values']);
+ field_attach_submit('test_entity', $entity_1, $form, $form_state);
field_attach_form_validate('test_entity', $entity_1, $form, $form_state);
$entity_2 = entity_create('test_entity', $form_state['values']['entity_2']);
+ field_attach_submit('test_entity', $entity_2, $form['entity_2'], $form_state);
field_attach_form_validate('test_entity', $entity_2, $form['entity_2'], $form_state);
}
diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc
index 8933d8b..b2ed35c 100644
--- a/core/modules/field/tests/modules/field_test/field_test.field.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.field.inc
@@ -163,53 +163,6 @@ function field_test_field_instance_settings_form($field, $instance) {
}
/**
- * Implements hook_field_widget_info().
- */
-function field_test_field_widget_info() {
- return array(
- 'test_field_widget' => array(
- 'label' => t('Test field'),
- 'field types' => array('test_field', 'hidden_test_field'),
- 'settings' => array('test_widget_setting' => 'dummy test string'),
- ),
- 'test_field_widget_multiple' => array(
- 'label' => t('Test field 1'),
- 'field types' => array('test_field'),
- 'settings' => array('test_widget_setting_multiple' => 'dummy test string'),
- 'behaviors' => array(
- 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
- ),
- ),
- );
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function field_test_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
- switch ($instance['widget']['type']) {
- case 'test_field_widget':
- $element += array(
- '#type' => 'textfield',
- '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
- );
- return array('value' => $element);
-
- case 'test_field_widget_multiple':
- $values = array();
- foreach ($items as $delta => $value) {
- $values[] = $value['value'];
- }
- $element += array(
- '#type' => 'textfield',
- '#default_value' => implode(', ', $values),
- '#element_validate' => array('field_test_widget_multiple_validate'),
- );
- return $element;
- }
-}
-
-/**
* Form element validation handler for 'test_field_widget_multiple' widget.
*/
function field_test_widget_multiple_validate($element, &$form_state) {
@@ -222,42 +175,6 @@ function field_test_widget_multiple_validate($element, &$form_state) {
}
/**
- * Implements hook_field_widget_error().
- */
-function field_test_field_widget_error($element, $error, $form, &$form_state) {
- // @todo No easy way to differenciate widget types, we should receive it as a
- // parameter.
- if (isset($element['value'])) {
- // Widget is test_field_widget.
- $error_element = $element['value'];
- }
- else {
- // Widget is test_field_widget_multiple.
- $error_element = $element;
- }
-
- form_error($error_element, $error['message']);
-}
-
-/**
- * Implements hook_field_widget_settings_form().
- */
-function field_test_field_widget_settings_form($field, $instance) {
- $widget = $instance['widget'];
- $settings = $widget['settings'];
-
- $form['test_widget_setting'] = array(
- '#type' => 'textfield',
- '#title' => t('Field test field widget setting'),
- '#default_value' => $settings['test_widget_setting'],
- '#required' => FALSE,
- '#description' => t('A dummy form element to simulate field widget setting.'),
- );
-
- return $form;
-}
-
-/**
* Implements hook_field_formatter_info().
*/
function field_test_field_formatter_info() {
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 6dfbb15..f4bb287 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -238,11 +238,6 @@ function field_test_field_widget_properties_alter(&$widget, $context) {
if (in_array($context['entity_type'], array('node', 'comment')) && ($context['field']['field_name'] == 'alter_test_text')) {
$widget['settings']['size'] = 42;
}
- // Set a message if this is for the form displayed to set default value for
- // the field instance.
- if ($context['default']) {
- drupal_set_message('From hook_field_widget_properties_alter(): Default form is true.');
- }
}
/**
@@ -261,11 +256,11 @@ function field_test_field_widget_properties_user_alter(&$widget, $context) {
function field_test_field_widget_form_alter(&$element, &$form_state, $context) {
switch ($context['field']['field_name']) {
case 'alter_test_text':
- drupal_set_message('Field size: ' . $context['instance']['widget']['settings']['size']);
+ drupal_set_message('Field size: ' . $context['instance']->getWidget()->getSetting('size'));
break;
case 'alter_test_options':
- drupal_set_message('Widget type: ' . $context['instance']['widget']['type']);
+ drupal_set_message('Widget type: ' . $context['instance']->getWidget()->getPluginId());
break;
}
// Set a message if this is for the form displayed to set default value for
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php
new file mode 100644
index 0000000..55471ba
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php
@@ -0,0 +1,64 @@
+ 'textfield',
+ '#title' => t('Field test field widget setting'),
+ '#description' => t('A dummy form element to simulate field widget setting.'),
+ '#default_value' => $this->getSetting('test_widget_setting'),
+ '#required' => FALSE,
+ );
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+ */
+ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+ $element += array(
+ '#type' => 'textfield',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
+ );
+ return array('value' => $element);
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+ */
+ public function errorElement(array $element, array $error, array $form, array &$form_state) {
+ return $element['value'];
+ }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
new file mode 100644
index 0000000..ead51bd
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
@@ -0,0 +1,69 @@
+ 'textfield',
+ '#title' => t('Field test field widget setting'),
+ '#description' => t('A dummy form element to simulate field widget setting.'),
+ '#default_value' => $this->getSetting('test_widget_setting'),
+ '#required' => FALSE,
+ );
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+ */
+ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+ $values = array();
+ foreach ($items as $delta => $value) {
+ $values[] = $value['value'];
+ }
+ $element += array(
+ '#type' => 'textfield',
+ '#default_value' => implode(', ', $values),
+ '#element_validate' => array('field_test_widget_multiple_validate'),
+ );
+ return $element;
+ }
+
+ /**
+ * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+ */
+ public function errorElement(array $element, array $error, array $form, array &$form_state) {
+ return $element;
+ }
+
+}
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index bb99234..54dc192 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -5,6 +5,8 @@
* Administrative interface for custom field type creation.
*/
+use Drupal\field\FieldInstance;
+
/**
* Page callback: Lists all defined fields for quick reference.
*
@@ -307,7 +309,6 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
// Gather bundle information.
$instances = field_info_instances($entity_type, $bundle);
$field_types = field_info_field_types();
- $widget_types = field_info_widget_types();
$extra_fields = field_info_extra_fields($entity_type, $bundle, 'form');
@@ -345,6 +346,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
foreach ($instances as $name => $instance) {
$field = field_info_field($instance['field_name']);
$admin_field_path = $admin_path . '/fields/' . $instance['field_name'];
+ $widget_definition = $instance->getWidget()->getDefinition();
+
$table[$name] = array(
'#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
'#row_type' => 'field',
@@ -387,7 +390,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
),
'widget_type' => array(
'#type' => 'link',
- '#title' => t($widget_types[$instance['widget']['type']]['label']),
+ '#title' => $widget_definition['label'],
'#href' => $admin_field_path . '/widget-type',
'#options' => array('attributes' => array('title' => t('Change widget type.'))),
),
@@ -1475,8 +1478,10 @@ function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) {
if (!isset($options)) {
$options = array();
$field_types = field_info_field_types();
- foreach (field_info_widget_types() as $name => $widget_type) {
- foreach ($widget_type['field types'] as $widget_field_type) {
+ $widget_types = field_info_widget_types();
+ uasort($widget_types, 'drupal_sort_weight');
+ foreach ($widget_types as $name => $widget_type) {
+ foreach ($widget_type['field_types'] as $widget_field_type) {
// Check that the field type exists.
if (isset($field_types[$widget_field_type])) {
$options[$widget_field_type][$name] = $widget_type['label'];
@@ -1657,7 +1662,7 @@ function field_ui_field_settings_form_submit($form, &$form_state) {
* @see field_ui_widget_type_form_submit()
* @ingroup forms
*/
-function field_ui_widget_type_form($form, &$form_state, $instance) {
+function field_ui_widget_type_form($form, &$form_state, FieldInstance $instance) {
drupal_set_title($instance['label']);
$bundle = $instance['bundle'];
@@ -1666,7 +1671,7 @@ function field_ui_widget_type_form($form, &$form_state, $instance) {
$field = field_info_field($field_name);
$field_type = field_info_field_types($field['type']);
- $widget_type = field_info_widget_types($instance['widget']['type']);
+ $widget_definition = $instance->getWidget()->getDefinition();
$bundles = field_info_bundles();
$bundle_label = $bundles[$entity_type][$bundle]['label'];
@@ -1685,7 +1690,7 @@ function field_ui_widget_type_form($form, &$form_state, $instance) {
'#title' => t('Widget type'),
'#required' => TRUE,
'#options' => field_ui_widget_type_options($field['type']),
- '#default_value' => $instance['widget']['type'],
+ '#default_value' => $instance->getWidget()->getPluginId(),
'#description' => t('The type of form element you would like to present to the user when creating this field in the %type type.', array('%type' => $bundle_label)),
);
@@ -1817,6 +1822,10 @@ function field_ui_field_edit_form($form, &$form_state, $instance) {
$form['#field'] = $field;
$form['#instance'] = $instance;
+ // Create an arbitrary entity object (used by the 'default value' widget).
+ $ids = (object) array('entity_type' => $instance['entity_type'], 'bundle' => $instance['bundle'], 'entity_id' => NULL);
+ $form['#entity'] = _field_create_entity_from_ids($ids);
+ $form['#entity']->field_ui_default_value = TRUE;
if (!empty($field['locked'])) {
$form['locked'] = array(
@@ -1906,12 +1915,9 @@ function field_ui_field_edit_form($form, &$form_state, $instance) {
$form['instance']['settings'] = $additions;
}
- // Add additional widget settings from the widget module.
- $additions = module_invoke($widget_type['module'], 'field_widget_settings_form', $field, $instance);
- if (is_array($additions)) {
- $form['instance']['widget']['settings'] = $additions;
- $form['instance']['widget']['active']['#value'] = 1;
- }
+ // Add widget settings for the widget type.
+ $additions = $instance->getWidget()->settingsForm($form, $form_state);
+ $form['instance']['widget']['settings'] = $additions ? $additions : array('#type' => 'value', '#value' => array());
// Add handling for default value if not provided by any other module.
if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && empty($instance['default_value_function'])) {
@@ -2018,6 +2024,7 @@ function field_ui_field_edit_instance_pre_render($element) {
*/
function field_ui_default_value_widget($field, $instance, &$form, &$form_state) {
$field_name = $field['field_name'];
+ $entity = $form['#entity'];
$element = array(
'#type' => 'fieldset',
@@ -2031,13 +2038,15 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
'#parents' => array(),
);
- // Insert the widget.
- $items = $instance['default_value'];
+ // Adjust the instance definition used for the form element. We want a
+ // non-required input and no description.
$instance['required'] = FALSE;
$instance['description'] = '';
- // @todo Allow multiple values (requires more work on 'add more' JS handler).
- $element += field_default_form($instance['entity_type'], NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state, 0);
+ // Insert the widget. Since we do not use the "official" instance definition,
+ // the whole flow cannot use field_invoke_method().
+ $items = (array) $instance['default_value'];
+ $element += $instance->getWidget()->form($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
return $element;
}
@@ -2050,31 +2059,36 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
function field_ui_field_edit_form_validate($form, &$form_state) {
// Take the incoming values as the $instance definition, so that the 'default
// value' gets validated using the instance settings being submitted.
- $instance = $form_state['values']['instance'];
+ $instance = $form['#instance'];
$field_name = $instance['field_name'];
+ $entity = $form['#entity'];
if (isset($form['instance']['default_value_widget'])) {
$element = $form['instance']['default_value_widget'];
- $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state);
- $field = $field_state['field'];
-
// Extract the 'default value'.
$items = array();
- field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+ $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
- // Validate the value and report errors.
+ // Grab the field definition from $form_state.
+ $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state);
+ $field = $field_state['field'];
+
+ // Validate the value.
$errors = array();
$function = $field['module'] . '_field_validate';
if (function_exists($function)) {
$function(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $errors);
}
+
+ // Report errors.
if (isset($errors[$field_name][LANGUAGE_NOT_SPECIFIED])) {
// Store reported errors in $form_state.
$field_state['errors'] = $errors[$field_name][LANGUAGE_NOT_SPECIFIED];
field_form_set_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state, $field_state);
+
// Assign reported errors to the correct form element.
- field_default_form_errors(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+ $instance->getWidget()->flagErrors($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
}
}
}
@@ -2085,12 +2099,12 @@ function field_ui_field_edit_form_validate($form, &$form_state) {
* @see field_ui_field_edit_form_validate().
*/
function field_ui_field_edit_form_submit($form, &$form_state) {
- $instance = $form_state['values']['instance'];
- $field = $form_state['values']['field'];
+ $instance = $form['#instance'];
+ $field = $form['#field'];
+ $entity = $form['#entity'];
- // Update any field settings that have changed.
- $field_source = field_info_field($instance['field_name']);
- $field = array_merge($field_source, $field);
+ // Merge incoming values into the field.
+ $field = array_merge($field, $form_state['values']['field']);
try {
field_update_field($field);
}
@@ -2105,15 +2119,15 @@ function field_ui_field_edit_form_submit($form, &$form_state) {
// Extract field values.
$items = array();
- field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
- field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+ $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
$instance['default_value'] = $items ? $items : NULL;
}
- // Retrieve the stored instance settings to merge with the incoming values.
- $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
- $instance = array_merge($instance_source, $instance);
+ // Merge incoming values into the instance.
+ foreach ($form_state['values']['instance'] as $key => $value) {
+ $instance[$key] = $value;
+ }
field_update_instance($instance);
drupal_set_message(t('Saved %label configuration.', array('%label' => $instance['label'])));
diff --git a/core/modules/field_ui/field_ui.api.php b/core/modules/field_ui/field_ui.api.php
index 80cbe78..e33fbdd 100644
--- a/core/modules/field_ui/field_ui.api.php
+++ b/core/modules/field_ui/field_ui.api.php
@@ -91,47 +91,6 @@ function hook_field_instance_settings_form($field, $instance) {
}
/**
- * Add settings to a widget settings form.
- *
- * Invoked from field_ui_field_edit_form() to allow the module defining the
- * widget to add settings for a widget instance.
- *
- * @param $field
- * The field structure being configured.
- * @param $instance
- * The instance structure being configured.
- *
- * @return
- * The form definition for the widget settings.
- */
-function hook_field_widget_settings_form($field, $instance) {
- $widget = $instance['widget'];
- $settings = $widget['settings'];
-
- if ($widget['type'] == 'text_textfield') {
- $form['size'] = array(
- '#type' => 'number',
- '#title' => t('Size of textfield'),
- '#default_value' => $settings['size'],
- '#min' => 1,
- '#required' => TRUE,
- );
- }
- else {
- $form['rows'] = array(
- '#type' => 'number',
- '#title' => t('Rows'),
- '#default_value' => $settings['rows'],
- '#min' => 1,
- '#required' => TRUE,
- );
- }
-
- return $form;
-}
-
-
-/**
* Specify the form elements for a formatter's settings.
*
* @param $field
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
index 687aa2e..7ee9501 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
@@ -69,10 +69,8 @@ class AlterTest extends WebTestBase {
// size when the form is displayed.
$this->drupalGet('admin/structure/types/manage/article/fields/alter_test_text');
$this->assertText('Field size: 42', 'Altered field size is found in hook_field_widget_form_alter().');
- // Test that hook_field_widget_properties_alter() and
- // hook_field_widget_form_alter() registers this is the default value form
- // and sets a message.
- $this->assertText('From hook_field_widget_properties_alter(): Default form is true.', 'Default value form detected in hook_field_widget_properties_alter().');
+ // Test that hook_field_widget_form_alter() registers this is the default
+ // value form and sets a message.
$this->assertText('From hook_field_widget_form_alter(): Default form is true.', 'Default value form detected in hook_field_widget_form_alter().');
// Create the alter_test_options field.
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 61b018a..8e5a18c 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -255,9 +255,8 @@ function forum_uri($forum) {
*/
function _forum_node_check_node_type(Node $node) {
// Fetch information about the forum field.
- $field = field_info_instance('node', 'taxonomy_forums', $node->type);
-
- return is_array($field);
+ $instance = field_info_instance('node', 'taxonomy_forums', $node->type);
+ return !empty($instance);
}
/**
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
index a72e052..f7670fe 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
@@ -56,12 +56,17 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
$instance = field_info_instance('node', $field_name, 'article');
// Add another instance with another default image to the page content type.
- $instance2 = array_merge($instance, array(
+ $instance2 = array(
+ 'field_name' => $field['field_name'],
+ 'entity_type' => 'node',
'bundle' => 'page',
+ 'label' => $instance['label'],
+ 'required' => $instance['required'],
'settings' => array(
'default_image' => $default_images['instance2']->fid,
),
- ));
+ 'widget' => $instance['widget'],
+ );
field_create_instance($instance2);
$instance2 = field_info_instance('node', $field_name, 'page');