[ 'label' => t('Multiupload'), 'field types' => ['file'], 'behaviors' => [ 'multiple values' => FIELD_BEHAVIOR_CUSTOM, 'default value' => FIELD_BEHAVIOR_NONE, ], 'settings' => [ 'progress_indicator' => 'throbber', ], ], ]; } /** * Implements hook_file_entity_inline_widgets(). */ function multiupload_filefield_widget_file_entity_inline_widgets() { $widgets = []; $widgets['file_mfw'] = [ 'element_type' => 'mfw_managed_file', ]; return $widgets; } /** * Implements hook_field_widget_form(). * * Mostly copied from drupal core module /modules/file/file.field.inc. */ function multiupload_filefield_widget_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { // This is essentially copied from file.field.inc. $defaults = [ 'fid' => 0, 'display' => !empty($field['settings']['display_default']), 'description' => '', ]; // Load the items for form rebuilds from the field state as they might not be // in $form_state['values'] because of validation limitations. Also, they are // only passed in as $items when editing existing entities. $field_state = field_form_get_state($element['#field_parents'], $field['field_name'], $langcode, $form_state); if (isset($field_state['items'])) { $items = $field_state['items']; } // Using the mfw_managed_file type with enhancements. $element_info = element_info('mfw_managed_file'); $element += [ '#type' => 'mfw_managed_file', '#upload_location' => file_field_widget_uri($field, $instance), '#upload_validators' => file_field_widget_upload_validators($field, $instance), '#value_callback' => 'mfw_field_widget_value', '#process' => array_merge($element_info['#process'], ['mfw_field_widget_process']), '#progress_indicator' => $instance['widget']['settings']['progress_indicator'], // Allows this field to return an array instead of a single value. '#extended' => TRUE, ]; if ($field['cardinality'] == 1) { // Set the default value. $element['#default_value'] = !empty($items) ? $items[0] : $defaults; // If there's only one field, return it as delta 0. if (empty($element['#default_value']['fid'])) { $element['#description'] = theme('file_upload_help', [ 'description' => $element['#description'], 'upload_validators' => $element['#upload_validators'], ]); } $elements = [$element]; } else { // If there are multiple values, add an element for each existing one. foreach ($items as $item) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $item; $elements[$delta]['#weight'] = $delta; $delta++; } // And then add one more empty row for new uploads except when this is a // programmed form as it is not necessary. if (($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) && empty($form_state['programmed'])) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $defaults; $elements[$delta]['#weight'] = $delta; $elements[$delta]['#required'] = ($element['#required'] && $delta == 0); } // The group of elements all-together need some extra functionality // after building up the full list (like draggable table rows). $elements['#file_upload_delta'] = $delta; $elements['#theme'] = 'file_widget_multiple'; $elements['#theme_wrappers'] = ['fieldset']; $elements['#process'] = ['mfw_field_widget_process_multiple']; $elements['#title'] = $element['#title']; $elements['#description'] = $element['#description']; $elements['#field_name'] = $element['#field_name']; $elements['#language'] = $element['#language']; $elements['#display_field'] = $field['settings']['display_field']; // Add some properties that will eventually be added to the file upload // field. These are added here so that they may be referenced easily through // a hook_form_alter(). $elements['#file_upload_title'] = t('Add a new file'); $elements['#file_upload_description'] = theme('file_upload_help', [ 'description' => '', 'upload_validators' => $elements[0]['#upload_validators'], ]); } return $elements; } /** * Implements hook_field_widget_settings_form(). */ function multiupload_filefield_widget_field_widget_settings_form($field, $instance) { $form = file_field_widget_settings_form($field, $instance); $form['#attached']['js'] = [drupal_get_path('module', 'multiupload_filefield_widget') . '/mfw.js']; return $form; } /** * Get the upload validators for a file field. * * @param array $field * A field array. * @param array $instance * An instance array. * * @return array * An array suitable for passing to file_save_upload() or the file field * element's '#upload_validators' property. */ function multiupload_filefield_widget_field_widget_upload_validators(array $field, array $instance) { return file_field_widget_upload_validators($field, $instance); } /** * The #value_callback for the file_mfw field element. */ function mfw_field_widget_value($element, $input, &$form_state) { if ($input) { // Checkboxes lose their value when empty. // If the display field is present make sure its unchecked value is saved. $field = field_widget_field($element, $form_state); if (empty($input['display'])) { $input['display'] = $field['settings']['display_field'] ? 0 : 1; } } // We depend on the mfw managed file element to handle uploads. $return = multiupload_filefield_widget_mfw_managed_file_value($element, $input, $form_state); // Ensure that all the required properties are returned even if empty. $return += [ 'fid' => 0, 'display' => 1, 'description' => '', ]; $last_parent = $element['#parents'][count($element['#parents']) - 1]; $form_state['values'][$element['#field_name']][LANGUAGE_NONE][$last_parent] = $return; return $return; } /** * An element #process callback for the mfw_file field type. * * Expands the mfw_file type to include the description and display fields. */ function mfw_field_widget_process($element, &$form_state, &$form) { return file_field_widget_process($element, $form_state, $form); } /** * An element #process callback for a group of mfw_file fields. * * Mostly copied from drupal core module /module/file/file.field.inc. * * 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. */ function mfw_field_widget_process_multiple($element, &$form_state, $form) { $upload_name = implode('_', $element['#parents']) . '_' . $element['#file_upload_delta']; if (isset($_FILES['files']['name']) && array_key_exists($upload_name, $_FILES['files']['name'])) { $count = count($_FILES['files']['name'][$upload_name]); // Supposing #file_upload_delta is always the last delta this will work. for ($i = 1; $i < $count; $i++) { $element[] = $element[$element['#file_upload_delta']]; } } $element_children = element_children($element); $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'] = [ '#type' => 'weight', '#title' => $description ? t('Weight for @title', ['@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'] = [ '#type' => 'hidden', '#default_value' => $delta, ]; } } // Add a new wrapper around all the elements for AJAX replacement. $element['#prefix'] = '
'; $element['#suffix'] = '
'; return $element; } /** * Submit handler for upload and remove buttons of file_mfw fields. * * 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 multiupload_filefield_widget_field_widget_submit($form, &$form_state) { return file_field_widget_submit($form, $form_state); }