Issue #2072995 by claudiu.cristea, yched: Change notice: Move FAPI callbacks for file/image widgets in classes.
parent
97072d8670
commit
edbe2839a4
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
|
||||
$element['#suffix'] = '</div>';
|
||||
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -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'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
|
||||
$element['#suffix'] = '</div>';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue