diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 37c913ae9b8..43d15a65bea 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -1544,7 +1544,7 @@ class FormBuilder implements FormBuilderInterface {
// Set the element's #value property.
if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
- $value_callback = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
+ $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
if ($process_input) {
// Get the input for the current element. NULL values in the input need
// to be explicitly distinguished from missing input. (see below)
@@ -1568,8 +1568,8 @@ class FormBuilder implements FormBuilderInterface {
// If we have input for the current element, assign it to the #value
// property, optionally filtered through $value_callback.
if ($input_exists) {
- if (function_exists($value_callback)) {
- $element['#value'] = $value_callback($element, $input, $form_state);
+ if (is_callable($value_callable)) {
+ $element['#value'] = call_user_func_array($value_callable, array(&$element, $input, &$form_state));
}
if (!isset($element['#value']) && isset($input)) {
$element['#value'] = $input;
@@ -1584,8 +1584,8 @@ class FormBuilder implements FormBuilderInterface {
if (!isset($element['#value'])) {
// Call #type_value without a second argument to request default_value
// handling.
- if (function_exists($value_callback)) {
- $element['#value'] = $value_callback($element, FALSE, $form_state);
+ if (is_callable($value_callable)) {
+ $element['#value'] = call_user_func_array($value_callable, array(&$element, FALSE, &$form_state));
}
// Final catch. If we haven't set a value yet, use the explicit default
// value. Avoid image buttons (which come with garbage value), so we
diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php
index a7205d1c912..ad6febf8da3 100644
--- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php
+++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php
@@ -525,7 +525,7 @@ interface FormBuilderInterface extends FormErrorInterface {
* rendering each element). Each of these three pipelines provides ample
* opportunity for modules to customize what happens. For example, during this
* function's life cycle, the following functions get called for each element:
- * - $element['#value_callback']: A function that implements how user input is
+ * - $element['#value_callback']: A callable that implements how user input is
* mapped to an element's #value property. This defaults to a function named
* 'form_type_TYPE_value' where TYPE is $element['#type'].
* - $element['#process']: An array of functions called after user input has
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index dee5b4ec1fe..93ce9d2b709 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -5,7 +5,6 @@
* Field module functionality for the File module.
*/
-use Drupal\Component\Utility\NestedArray;
use Drupal\field\FieldInterface;
/**
@@ -18,278 +17,6 @@ function file_field_info_alter(&$info) {
$info['file']['settings']['uri_scheme'] = file_default_scheme();
}
-/**
- * Render API callback: Retrieves the value for the file_generic field element.
- *
- * This function is assigned as a #value callback in file_field_widget_form().
- */
-function file_field_widget_value($element, $input = FALSE, $form_state) {
- if ($input) {
- // Checkboxes lose their value when empty.
- // If the display field is present make sure its unchecked value is saved.
- if (empty($input['display'])) {
- $input['display'] = $element['#display_field'] ? 0 : 1;
- }
- }
-
- // We depend on the managed file element to handle uploads.
- $return = file_managed_file_value($element, $input, $form_state);
-
- // Ensure that all the required properties are returned even if empty.
- $return += array(
- 'fids' => array(),
- 'display' => 1,
- 'description' => '',
- );
-
- return $return;
-}
-
-/**
- * Validation callback for upload element on file widget. Checks if user has
- * uploaded more files than allowed.
- *
- * This validator is used only when cardinality not set to 1 or unlimited.
- */
-function file_field_widget_multiple_count_validate($element, &$form_state, $form) {
- $parents = $element['#parents'];
- $entity_type = $element['#entity_type'];
- $field_name = $element['#field_name'];
- $values = NestedArray::getValue($form_state['values'], $parents);
-
- array_pop($parents);
- $current = count(element_children(NestedArray::getValue($form, $parents))) - 1;
-
- $field = field_info_field($entity_type, $field_name);
- $cardinality = $field->getCardinality();
- $uploaded = count($values['fids']);
- $count = $uploaded + $current;
- if ($count > $cardinality) {
- $keep = $uploaded - $count + $cardinality;
- $removed_files = array_slice($values['fids'], $keep);
- $removed_names = array();
- foreach ($removed_files as $fid) {
- $file = file_load($fid);
- $removed_names[] = $file->getFilename();
- }
- drupal_set_message(
- t(
- 'Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.',
- array(
- '%field' => $field_name,
- '@max' => $cardinality,
- '@count' => $keep,
- '%list' => implode(', ', $removed_names),
- )
- ),
- 'warning'
- );
- $values['fids'] = array_slice($values['fids'], 0, $keep);
- NestedArray::setValue($form_state['values'], $element['#parents'], $values);
- }
-}
-
-/**
- * Render API callback: Processes a file_generic field element.
- *
- * Expands the file_generic type to include the description and display fields.
- *
- * This function is assigned as a #process callback in file_field_widget_form().
- */
-function file_field_widget_process($element, &$form_state, $form) {
- $item = $element['#value'];
- $item['fids'] = $element['fids']['#value'];
-
- $element['#theme'] = 'file_widget';
-
- // Add the display field if enabled.
- if ($element['#display_field'] && $item['fids']) {
- $element['display'] = array(
- '#type' => empty($item['fids']) ? 'hidden' : 'checkbox',
- '#title' => t('Include file in display'),
- '#value' => isset($item['display']) ? $item['display'] : $element['#display_default'],
- '#attributes' => array('class' => array('file-display')),
- );
- }
- else {
- $element['display'] = array(
- '#type' => 'hidden',
- '#value' => '1',
- );
- }
-
- // Add the description field if enabled.
- if ($element['#description_field'] && $item['fids']) {
- $config = \Drupal::config('file.settings');
- $element['description'] = array(
- '#type' => $config->get('description.type'),
- '#title' => t('Description'),
- '#value' => isset($item['description']) ? $item['description'] : '',
- '#maxlength' => $config->get('description.length'),
- '#description' => t('The description may be used as the label of the link to the file.'),
- );
- }
-
- // Adjust the Ajax settings so that on upload and remove of any individual
- // file, the entire group of file fields is updated together.
- if ($element['#cardinality'] != 1) {
- $parents = array_slice($element['#array_parents'], 0, -1);
- $new_path = 'file/ajax';
- $new_options = array(
- 'query' => array(
- 'element_parents' => implode('/', $parents),
- 'form_build_id' => $form['form_build_id']['#value'],
- ),
- );
- $field_element = NestedArray::getValue($form, $parents);
- $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
- foreach (element_children($element) as $key) {
- if (isset($element[$key]['#ajax'])) {
- $element[$key]['#ajax']['path'] = $new_path;
- $element[$key]['#ajax']['options'] = $new_options;
- $element[$key]['#ajax']['wrapper'] = $new_wrapper;
- }
- }
- unset($element['#prefix'], $element['#suffix']);
- }
-
- // Add another submit handler to the upload and remove buttons, to implement
- // functionality needed by the field widget. This submit handler, along with
- // the rebuild logic in file_field_widget_form() requires the entire field,
- // not just the individual item, to be valid.
- foreach (array('upload_button', 'remove_button') as $key) {
- $element[$key]['#submit'][] = 'file_field_widget_submit';
- $element[$key]['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1));
- }
-
- return $element;
-}
-
-/**
- * Render API callback: Processes a group of file_generic field elements.
- *
- * Adds the weight field to each row so it can be ordered and adds a new Ajax
- * wrapper around the entire group so it can be replaced all at once.
- *
- * This function is assigned as a #process callback in file_field_widget_form().
- */
-function file_field_widget_process_multiple($element, &$form_state, $form) {
- $element_children = element_children($element, TRUE);
- $count = count($element_children);
-
- foreach ($element_children as $delta => $key) {
- if ($key != $element['#file_upload_delta']) {
- $description = _file_field_get_description_from_element($element[$key]);
- $element[$key]['_weight'] = array(
- '#type' => 'weight',
- '#title' => $description ? t('Weight for @title', array('@title' => $description)) : t('Weight for new file'),
- '#title_display' => 'invisible',
- '#delta' => $count,
- '#default_value' => $delta,
- );
- }
- else {
- // The title needs to be assigned to the upload field so that validation
- // errors include the correct widget label.
- $element[$key]['#title'] = $element['#title'];
- $element[$key]['_weight'] = array(
- '#type' => 'hidden',
- '#default_value' => $delta,
- );
- }
- }
-
- // Add a new wrapper around all the elements for Ajax replacement.
- $element['#prefix'] = '
';
- $element['#suffix'] = '
';
-
- return $element;
-}
-
-/**
- * Retrieves the file description from a field field element.
- *
- * This helper function is used by file_field_widget_process_multiple().
- *
- * @param $element
- * The element being processed.
- *
- * @return
- * A description of the file suitable for use in the administrative interface.
- */
-function _file_field_get_description_from_element($element) {
- // Use the actual file description, if it's available.
- if (!empty($element['#default_value']['description'])) {
- return $element['#default_value']['description'];
- }
- // Otherwise, fall back to the filename.
- if (!empty($element['#default_value']['filename'])) {
- return $element['#default_value']['filename'];
- }
- // This is probably a newly uploaded file; no description is available.
- return FALSE;
-}
-
-/**
- * Form submission handler for upload/remove button of file_field_widget_form().
- *
- * This runs in addition to and after file_managed_file_submit().
- *
- * @see file_managed_file_submit()
- * @see file_field_widget_form()
- * @see file_field_widget_process()
- */
-function file_field_widget_submit($form, &$form_state) {
- // During the form rebuild, file_field_widget_form() will create field item
- // widget elements using re-indexed deltas, so clear out $form_state['input']
- // to avoid a mismatch between old and new deltas. The rebuilt elements will
- // have #default_value set appropriately for the current state of the field,
- // so nothing is lost in doing this.
- $parents = array_slice($form_state['triggering_element']['#parents'], 0, -2);
- NestedArray::setValue($form_state['input'], $parents, NULL);
-
- $button = $form_state['triggering_element'];
-
- // Go one level up in the form, to the widgets container.
- $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
- $field_name = $element['#field_name'];
- $parents = $element['#field_parents'];
-
- $submitted_values = NestedArray::getValue($form_state['values'], array_slice($button['#parents'], 0, -2));
- foreach ($submitted_values as $delta => $submitted_value) {
- if (empty($submitted_value['fids'])) {
- unset($submitted_values[$delta]);
- }
- }
-
- // If there are more files uploaded via the same widget, we have to separate
- // them, as we display each file in it's own widget.
- $new_values = array();
- foreach ($submitted_values as $delta => $submitted_value) {
- if (is_array($submitted_value['fids'])) {
- foreach ($submitted_value['fids'] as $fid) {
- $new_value = $submitted_value;
- $new_value['fids'] = array($fid);
- $new_values[] = $new_value;
- }
- }
- else {
- $new_value = $submitted_value;
- }
- }
-
- // Re-index deltas after removing empty items.
- $submitted_values = array_values($new_values);
-
- // Update form_state values.
- NestedArray::setValue($form_state['values'], array_slice($button['#parents'], 0, -2), $submitted_values);
-
- // Update items.
- $field_state = field_form_get_state($parents, $field_name, $form_state);
- $field_state['items'] = $submitted_values;
- field_form_set_state($parents, $field_name, $form_state, $field_state);
-}
-
/**
* Returns HTML for an individual file upload widget.
*
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldWidget/FileWidget.php
index a7c8d3028f8..5c976ed5f8f 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldWidget/FileWidget.php
@@ -10,6 +10,8 @@ namespace Drupal\file\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\field\Field;
/**
* Plugin implementation of the 'file_generic' widget.
@@ -145,7 +147,7 @@ class FileWidget extends WidgetBase {
$elements['#type'] = 'details';
$elements['#theme'] = 'file_widget_multiple';
$elements['#theme_wrappers'] = array('details');
- $elements['#process'] = array('file_field_widget_process_multiple');
+ $elements['#process'] = array(array(get_class($this), 'processMultiple'));
$elements['#title'] = $title;
$elements['#description'] = $description;
@@ -202,13 +204,12 @@ class FileWidget extends WidgetBase {
'#type' => 'managed_file',
'#upload_location' => $items[$delta]->getUploadLocation(),
'#upload_validators' => $items[$delta]->getUploadValidators(),
- '#value_callback' => 'file_field_widget_value',
- '#process' => array_merge($element_info['#process'], array('file_field_widget_process')),
+ '#value_callback' => array(get_class($this), 'value'),
+ '#process' => array_merge($element_info['#process'], array(array(get_class($this), 'process'))),
'#progress_indicator' => $this->getSetting('progress_indicator'),
// Allows this field to return an array instead of a single value.
'#extended' => TRUE,
- // Add properties needed by file_field_widget_value() and
- // file_field_widget_process().
+ // Add properties needed by value() and process() methods.
'#display_field' => (bool) $field_settings['display_field'],
'#display_default' => $field_settings['display_default'],
'#description_field' => $field_settings['description_field'],
@@ -235,7 +236,7 @@ class FileWidget extends WidgetBase {
$element['#description'] = drupal_render($file_upload_help);
$element['#multiple'] = $cardinality != 1 ? TRUE : FALSE;
if ($cardinality != 1 && $cardinality != -1) {
- $element['#element_validate'] = array('file_field_widget_multiple_count_validate');
+ $element['#element_validate'] = array(array(get_class($this), 'validateMultipleCount'));
}
}
@@ -262,4 +263,265 @@ class FileWidget extends WidgetBase {
return $new_values;
}
+ /**
+ * Form API callback. Retrieves the value for the file_generic field element.
+ *
+ * This method is assigned as a #value_callback in formElement() method.
+ */
+ public static function value($element, $input = FALSE, $form_state) {
+ if ($input) {
+ // Checkboxes lose their value when empty.
+ // If the display field is present make sure its unchecked value is saved.
+ if (empty($input['display'])) {
+ $input['display'] = $element['#display_field'] ? 0 : 1;
+ }
+ }
+
+ // We depend on the managed file element to handle uploads.
+ $return = file_managed_file_value($element, $input, $form_state);
+
+ // Ensure that all the required properties are returned even if empty.
+ $return += array(
+ 'fids' => array(),
+ 'display' => 1,
+ 'description' => '',
+ );
+
+ return $return;
+ }
+
+ /**
+ * Form element validation callback for upload element on file widget. Checks
+ * if user has uploaded more files than allowed.
+ *
+ * This validator is used only when cardinality not set to 1 or unlimited.
+ */
+ public static function validateMultipleCount($element, &$form_state, $form) {
+ $parents = $element['#parents'];
+ $values = NestedArray::getValue($form_state['values'], $parents);
+
+ array_pop($parents);
+ $current = count(element_children(NestedArray::getValue($form, $parents))) - 1;
+
+ $field = Field::fieldInfo()->getField($element['#entity_type'], $element['#field_name']);
+ $uploaded = count($values['fids']);
+ $count = $uploaded + $current;
+ if ($count > $field->cardinality) {
+ $keep = $uploaded - $count + $field->cardinality;
+ $removed_files = array_slice($values['fids'], $keep);
+ $removed_names = array();
+ foreach ($removed_files as $fid) {
+ $file = file_load($fid);
+ $removed_names[] = $file->getFilename();
+ }
+ $args = array('%field' => $field->getFieldName(), '@max' => $field->cardinality, '@count' => $keep, '%list' => implode(', ', $removed_names));
+ $message = t('Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.', $args);
+ drupal_set_message($message, 'warning');
+ $values['fids'] = array_slice($values['fids'], 0, $keep);
+ NestedArray::setValue($form_state['values'], $element['#parents'], $values);
+ }
+ }
+
+ /**
+ * Form API callback: Processes a file_generic field element.
+ *
+ * Expands the file_generic type to include the description and display
+ * fields.
+ *
+ * This method is assigned as a #process callback in formElement() method.
+ */
+ public static function process($element, &$form_state, $form) {
+ $item = $element['#value'];
+ $item['fids'] = $element['fids']['#value'];
+
+ $element['#theme'] = 'file_widget';
+
+ // Add the display field if enabled.
+ if ($element['#display_field'] && $item['fids']) {
+ $element['display'] = array(
+ '#type' => empty($item['fids']) ? 'hidden' : 'checkbox',
+ '#title' => t('Include file in display'),
+ '#value' => isset($item['display']) ? $item['display'] : $element['#display_default'],
+ '#attributes' => array('class' => array('file-display')),
+ );
+ }
+ else {
+ $element['display'] = array(
+ '#type' => 'hidden',
+ '#value' => '1',
+ );
+ }
+
+ // Add the description field if enabled.
+ if ($element['#description_field'] && $item['fids']) {
+ $config = \Drupal::config('file.settings');
+ $element['description'] = array(
+ '#type' => $config->get('description.type'),
+ '#title' => t('Description'),
+ '#value' => isset($item['description']) ? $item['description'] : '',
+ '#maxlength' => $config->get('description.length'),
+ '#description' => t('The description may be used as the label of the link to the file.'),
+ );
+ }
+
+ // Adjust the Ajax settings so that on upload and remove of any individual
+ // file, the entire group of file fields is updated together.
+ if ($element['#cardinality'] != 1) {
+ $parents = array_slice($element['#array_parents'], 0, -1);
+ $new_path = 'file/ajax';
+ $new_options = array(
+ 'query' => array(
+ 'element_parents' => implode('/', $parents),
+ 'form_build_id' => $form['form_build_id']['#value'],
+ ),
+ );
+ $field_element = NestedArray::getValue($form, $parents);
+ $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
+ foreach (element_children($element) as $key) {
+ if (isset($element[$key]['#ajax'])) {
+ $element[$key]['#ajax']['path'] = $new_path;
+ $element[$key]['#ajax']['options'] = $new_options;
+ $element[$key]['#ajax']['wrapper'] = $new_wrapper;
+ }
+ }
+ unset($element['#prefix'], $element['#suffix']);
+ }
+
+ // Add another submit handler to the upload and remove buttons, to implement
+ // functionality needed by the field widget. This submit handler, along with
+ // the rebuild logic in file_field_widget_form() requires the entire field,
+ // not just the individual item, to be valid.
+ foreach (array('upload_button', 'remove_button') as $key) {
+ $element[$key]['#submit'][] = array(get_called_class(), 'submit');
+ $element[$key]['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1));
+ }
+
+ return $element;
+ }
+
+ /**
+ * Form API callback: Processes a group of file_generic field elements.
+ *
+ * Adds the weight field to each row so it can be ordered and adds a new Ajax
+ * wrapper around the entire group so it can be replaced all at once.
+ *
+ * This method on is assigned as a #process callback in formMultipleElements()
+ * method.
+ */
+ public static function processMultiple($element, &$form_state, $form) {
+ $element_children = element_children($element, TRUE);
+ $count = count($element_children);
+
+ foreach ($element_children as $delta => $key) {
+ if ($key != $element['#file_upload_delta']) {
+ $description = static::getDescriptionFromElement($element[$key]);
+ $element[$key]['_weight'] = array(
+ '#type' => 'weight',
+ '#title' => $description ? t('Weight for @title', array('@title' => $description)) : t('Weight for new file'),
+ '#title_display' => 'invisible',
+ '#delta' => $count,
+ '#default_value' => $delta,
+ );
+ }
+ else {
+ // The title needs to be assigned to the upload field so that validation
+ // errors include the correct widget label.
+ $element[$key]['#title'] = $element['#title'];
+ $element[$key]['_weight'] = array(
+ '#type' => 'hidden',
+ '#default_value' => $delta,
+ );
+ }
+ }
+
+ // Add a new wrapper around all the elements for Ajax replacement.
+ $element['#prefix'] = '';
+ $element['#suffix'] = '
';
+
+ return $element;
+ }
+
+ /**
+ * Retrieves the file description from a field field element.
+ *
+ * This helper static method is used by processMultiple() method.
+ *
+ * @param array $element
+ * An associative array with the element being processed.
+ *
+ * @return array|false
+ * A description of the file suitable for use in the administrative
+ * interface.
+ */
+ protected static function getDescriptionFromElement($element) {
+ // Use the actual file description, if it's available.
+ if (!empty($element['#default_value']['description'])) {
+ return $element['#default_value']['description'];
+ }
+ // Otherwise, fall back to the filename.
+ if (!empty($element['#default_value']['filename'])) {
+ return $element['#default_value']['filename'];
+ }
+ // This is probably a newly uploaded file; no description is available.
+ return FALSE;
+ }
+
+ /**
+ * Form submission handler for upload/remove button of formElement().
+ *
+ * This runs in addition to and after file_managed_file_submit().
+ *
+ * @see file_managed_file_submit()
+ */
+ public static function submit($form, &$form_state) {
+ // During the form rebuild, formElement() will create field item widget
+ // elements using re-indexed deltas, so clear out $form_state['input'] to
+ // avoid a mismatch between old and new deltas. The rebuilt elements will
+ // have #default_value set appropriately for the current state of the field,
+ // so nothing is lost in doing this.
+ $parents = array_slice($form_state['triggering_element']['#parents'], 0, -2);
+ NestedArray::setValue($form_state['input'], $parents, NULL);
+
+ $button = $form_state['triggering_element'];
+
+ // Go one level up in the form, to the widgets container.
+ $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
+ $field_name = $element['#field_name'];
+ $parents = $element['#field_parents'];
+
+ $submitted_values = NestedArray::getValue($form_state['values'], array_slice($button['#parents'], 0, -2));
+ foreach ($submitted_values as $delta => $submitted_value) {
+ if (empty($submitted_value['fids'])) {
+ unset($submitted_values[$delta]);
+ }
+ }
+
+ // If there are more files uploaded via the same widget, we have to separate
+ // them, as we display each file in it's own widget.
+ $new_values = array();
+ foreach ($submitted_values as $delta => $submitted_value) {
+ if (is_array($submitted_value['fids'])) {
+ foreach ($submitted_value['fids'] as $fid) {
+ $new_value = $submitted_value;
+ $new_value['fids'] = array($fid);
+ $new_values[] = $new_value;
+ }
+ }
+ else {
+ $new_value = $submitted_value;
+ }
+ }
+
+ // Re-index deltas after removing empty items.
+ $submitted_values = array_values($new_values);
+
+ // Update form_state values.
+ NestedArray::setValue($form_state['values'], array_slice($button['#parents'], 0, -2), $submitted_values);
+
+ // Update items.
+ $field_state = field_form_get_state($parents, $field_name, $form_state);
+ $field_state['items'] = $submitted_values;
+ field_form_set_state($parents, $field_name, $form_state, $field_state);
+ }
+
}
diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc
index 244f3894fbe..7cd7e2aff5d 100644
--- a/core/modules/image/image.field.inc
+++ b/core/modules/image/image.field.inc
@@ -14,115 +14,6 @@ function image_field_info_alter(&$info) {
$info['image']['settings']['uri_scheme'] = file_default_scheme();
}
-/**
- * An element #process callback for the image_image field type.
- *
- * Expands the image_image type to include the alt and title fields.
- */
-function image_field_widget_process($element, &$form_state, $form) {
- $item = $element['#value'];
- $item['fids'] = $element['fids']['#value'];
-
- $element['#theme'] = 'image_widget';
- $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/css/image.theme.css';
-
- // Add the image preview.
- if (!empty($element['#files']) && $element['#preview_image_style']) {
- $file = reset($element['#files']);
- $variables = array(
- 'style_name' => $element['#preview_image_style'],
- 'uri' => $file->getFileUri(),
- );
-
- // Determine image dimensions.
- if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
- $variables['width'] = $element['#value']['width'];
- $variables['height'] = $element['#value']['height'];
- }
- else {
- $image = \Drupal::service('image.factory')->get($file->getFileUri());
- if ($image->isSupported()) {
- $variables['width'] = $image->getWidth();
- $variables['height'] = $image->getHeight();
- }
- else {
- $variables['width'] = $variables['height'] = NULL;
- }
- }
-
- $element['preview'] = array(
- '#theme' => 'image_style',
- '#width' => $variables['width'],
- '#height' => $variables['height'],
- '#style_name' => $variables['style_name'],
- '#uri' => $variables['uri'],
- );
-
- // Store the dimensions in the form so the file doesn't have to be accessed
- // again. This is important for remote files.
- $element['width'] = array(
- '#type' => 'hidden',
- '#value' => $variables['width'],
- );
- $element['height'] = array(
- '#type' => 'hidden',
- '#value' => $variables['height'],
- );
- }
-
- // Add the additional alt and title fields.
- $element['alt'] = array(
- '#title' => t('Alternate text'),
- '#type' => 'textfield',
- '#default_value' => isset($item['alt']) ? $item['alt'] : '',
- '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
- // @see https://drupal.org/node/465106#alt-text
- '#maxlength' => 512,
- '#weight' => -2,
- '#access' => (bool) $item['fids'] && $element['#alt_field'],
- '#element_validate' => $element['#alt_field_required'] == 1 ? array('_image_field_required_fields_validate') : array(),
- );
- $element['title'] = array(
- '#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => isset($item['title']) ? $item['title'] : '',
- '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'),
- '#maxlength' => 1024,
- '#weight' => -1,
- '#access' => (bool) $item['fids'] && $element['#title_field'],
- '#element_validate' => $element['#alt_field_required'] == 1 ? array('_image_field_required_fields_validate') : array(),
- );
-
- return $element;
-}
-
-/**
- * Validate callback for alt and title field, if the user wants them required.
- *
- * This is separated in a validate function instead of a #required flag to avoid
- * being validated on the process callback.
- */
-function _image_field_required_fields_validate($element, &$form_state) {
- // Only do validation if the function is triggered from other places than
- // the image process form.
- if (!in_array('file_managed_file_submit', $form_state['triggering_element']['#submit'])) {
- // If the image is not there, we do not check for empty values.
- $parents = $element['#parents'];
- $field = array_pop($parents);
- $image_field = NestedArray::getValue($form_state['input'], $parents);
- // We check for the array key, so that it can be NULL (like if the user
- // submits the form without using the "upload" button).
- if (!array_key_exists($field, $image_field)) {
- return;
- }
- // Check if field is left emtpy.
- elseif (empty($image_field[$field])) {
- form_error($element, $form_state, t('The field !title is required', array('!title' => $element['#title'])));
- return;
- }
- }
-}
-
/**
* Returns HTML for an image field widget.
*
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
index 0ecca824c3b..94528ae86b9 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
@@ -7,8 +7,9 @@
namespace Drupal\image\Plugin\Field\FieldWidget;
-use Drupal\file\Plugin\Field\FieldWidget\FileWidget;
use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\file\Plugin\Field\FieldWidget\FileWidget;
/**
* Plugin implementation of the 'image_image' widget.
@@ -119,14 +120,127 @@ class ImageWidget extends FileWidget {
$element['#upload_validators']['file_validate_extensions'][0] = implode(' ', $extensions);
// Add all extra functionality provided by the image widget.
- $element['#process'][] = 'image_field_widget_process';
- // Add properties needed by image_field_widget_process().
+ $element['#process'][] = array(get_class($this), 'process');
+ // Add properties needed by process() method.
$element['#preview_image_style'] = $this->getSetting('preview_image_style');
$element['#title_field'] = $field_settings['title_field'];
+ $element['#title_field_required'] = $field_settings['title_field_required'];
$element['#alt_field'] = $field_settings['alt_field'];
$element['#alt_field_required'] = $field_settings['alt_field_required'];
return $element;
}
+ /**
+ * Form API callback: Processes a image_image field element.
+ *
+ * Expands the image_image type to include the alt and title fields.
+ *
+ * This method is assigned as a #process callback in formElement() method.
+ */
+ public static function process($element, &$form_state, $form) {
+ $item = $element['#value'];
+ $item['fids'] = $element['fids']['#value'];
+
+ $element['#theme'] = 'image_widget';
+ $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/css/image.theme.css';
+
+ // Add the image preview.
+ if (!empty($element['#files']) && $element['#preview_image_style']) {
+ $file = reset($element['#files']);
+ $variables = array(
+ 'style_name' => $element['#preview_image_style'],
+ 'uri' => $file->getFileUri(),
+ );
+
+ // Determine image dimensions.
+ if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
+ $variables['width'] = $element['#value']['width'];
+ $variables['height'] = $element['#value']['height'];
+ }
+ else {
+ $image = \Drupal::service('image.factory')->get($file->getFileUri());
+ if ($image->getExtension()) {
+ $variables['width'] = $image->getWidth();
+ $variables['height'] = $image->getHeight();
+ }
+ else {
+ $variables['width'] = $variables['height'] = NULL;
+ }
+ }
+
+ $element['preview'] = array(
+ '#theme' => 'image_style',
+ '#width' => $variables['width'],
+ '#height' => $variables['height'],
+ '#style_name' => $variables['style_name'],
+ '#uri' => $variables['uri'],
+ );
+
+ // Store the dimensions in the form so the file doesn't have to be
+ // accessed again. This is important for remote files.
+ $element['width'] = array(
+ '#type' => 'hidden',
+ '#value' => $variables['width'],
+ );
+ $element['height'] = array(
+ '#type' => 'hidden',
+ '#value' => $variables['height'],
+ );
+ }
+
+ // Add the additional alt and title fields.
+ $element['alt'] = array(
+ '#title' => t('Alternate text'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['alt']) ? $item['alt'] : '',
+ '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
+ // @see http://www.gawds.org/show.php?contentid=28
+ '#maxlength' => 512,
+ '#weight' => -2,
+ '#access' => (bool) $item['fids'] && $element['#alt_field'],
+ '#element_validate' => $element['#alt_field_required'] == 1 ? array(array(get_called_class(), 'validateRequiredFields')) : array(),
+ );
+ $element['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Title'),
+ '#default_value' => isset($item['title']) ? $item['title'] : '',
+ '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'),
+ '#maxlength' => 1024,
+ '#weight' => -1,
+ '#access' => (bool) $item['fids'] && $element['#title_field'],
+ '#element_validate' => $element['#title_field_required'] == 1 ? array(array(get_called_class(), 'validateRequiredFields')) : array(),
+ );
+
+ return $element;
+ }
+
+ /**
+ * Validate callback for alt and title field, if the user wants them required.
+ *
+ * This is separated in a validate function instead of a #required flag to
+ * avoid being validated on the process callback.
+ */
+ public static function validateRequiredFields($element, &$form_state) {
+ // Only do validation if the function is triggered from other places than
+ // the image process form.
+ if (!in_array('file_managed_file_submit', $form_state['triggering_element']['#submit'])) {
+ // If the image is not there, we do not check for empty values.
+ $parents = $element['#parents'];
+ $field = array_pop($parents);
+ $image_field = NestedArray::getValue($form_state['input'], $parents);
+ // We check for the array key, so that it can be NULL (like if the user
+ // submits the form without using the "upload" button).
+ if (!array_key_exists($field, $image_field)) {
+ return;
+ }
+ // Check if field is left empty.
+ elseif (empty($image_field[$field])) {
+ \Drupal::formBuilder()->setError($element, $form_state, t('The field !title is required', array('!title' => $element['#title'])));
+ return;
+ }
+ }
+ }
+
+
}