diff --git a/core/includes/file.inc b/core/includes/file.inc index c66a2e0..6cfff2f 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1031,6 +1031,8 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { * * @param $source * A string specifying the filepath or URI of the uploaded file to save. + * @param $delta + * The delta of the file being uploaded. Used in conjunction with #multiple. * @param $validators * An optional, associative array of callback functions used to validate the * file. See file_validate() for a full discussion of the array format. @@ -1061,52 +1063,52 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { * - source: Path to the file before it is moved. * - destination: Path to the file after it is moved (same as 'uri'). */ -function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) { +function file_save_upload($source, $delta = 0, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) { global $user; static $upload_cache; // Return cached objects without processing since the file will have // already been processed and the paths in _FILES will be invalid. - if (isset($upload_cache[$source])) { - return $upload_cache[$source]; + if (isset($upload_cache[$source][$delta])) { + return $upload_cache[$source][$delta]; } // Make sure there's an upload to process. - if (empty($_FILES['files']['name'][$source])) { + if (empty($_FILES['files']['name'][$source][$delta])) { return NULL; } // Check for file upload errors and return FALSE if a lower level system // error occurred. For a complete list of errors: // See http://php.net/manual/features.file-upload.errors.php. - switch ($_FILES['files']['error'][$source]) { + switch ($_FILES['files']['error'][$source][$delta]) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: - drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$source], '%maxsize' => format_size(file_upload_max_size()))), 'error'); + drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$source][$delta], '%maxsize' => format_size(file_upload_max_size()))), 'error'); return FALSE; case UPLOAD_ERR_PARTIAL: case UPLOAD_ERR_NO_FILE: - drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $_FILES['files']['name'][$source])), 'error'); + drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $_FILES['files']['name'][$source][$delta])), 'error'); return FALSE; case UPLOAD_ERR_OK: // Final check that this is a valid upload, if it isn't, use the // default error handler. - if (is_uploaded_file($_FILES['files']['tmp_name'][$source])) { + if (is_uploaded_file($_FILES['files']['tmp_name'][$source][$delta])) { break; } // Unknown error default: - drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source])), 'error'); + drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source][$delta])), 'error'); return FALSE; } // Begin building file entity. $values = array( 'uid' => $user->uid, 'status' => 0, - 'filename' => trim(drupal_basename($_FILES['files']['name'][$source]), '.'), + 'filename' => trim(drupal_basename($_FILES['files']['name'][$source][$delta]), '.'), 'uri' => $_FILES['files']['tmp_name'][$source], 'filesize' => $_FILES['files']['size'][$source], ); @@ -1204,7 +1206,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE, // directory. This overcomes open_basedir restrictions for future file // operations. $file->uri = $file->destination; - if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->uri)) { + if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source][$delta], $file->uri)) { form_set_error($source, t('File upload error. Could not move uploaded file.')); watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri)); return FALSE; @@ -1225,7 +1227,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE, // If we made it this far it's safe to record this file in the database. $file->save(); // Add file to the cache. - $upload_cache[$source] = $file; + $upload_cache[$source][$delta] = $file; return $file; } diff --git a/core/includes/form.inc b/core/includes/form.inc index af1950a..222b7d4 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -4621,6 +4621,17 @@ function form_pre_render_file($element) { } /** + * Processes a file upload element, make use of #multiple if present. + */ +function form_process_file($element) { + if ($element['#multiple'] == TRUE) { + $element['#attributes'] = array('multiple' => 'multiple'); + } + $element['#name'] .= '[]'; + return $element; +} + +/** * Returns HTML for a form element. * * Each form element is wrapped in a DIV container having the following CSS diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 5f56a9f..9f15500 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -81,6 +81,7 @@ function file_element_info() { '#upload_validators' => array(), '#upload_location' => NULL, '#size' => 22, + '#multiple' => FALSE, '#extended' => FALSE, '#attached' => array( 'library' => array(array('file','drupal.file')), @@ -856,11 +857,15 @@ function file_managed_file_process($element, &$form_state, $form) { // Append the '-upload' to the #id so the field label's 'for' attribute // corresponds with the file element. $element['#id'] .= '-upload'; - $fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0; + + // This is used some times so let's implode it just once. + $parents_prefix = implode('_', $element['#parents']); + + $fids = isset($element['#value']['fids']) ? $element['#value']['fids'] : array(); // Set some default element properties. $element['#progress_indicator'] = empty($element['#progress_indicator']) ? 'none' : $element['#progress_indicator']; - $element['#file'] = $fid ? file_load($fid) : FALSE; + $element['#files'] = !empty($fids) ? file_load_multiple($fids) : FALSE; $element['#tree'] = TRUE; $ajax_settings = array( @@ -875,7 +880,7 @@ function file_managed_file_process($element, &$form_state, $form) { // Set up the buttons first since we need to check if they were clicked. $element['upload_button'] = array( - '#name' => implode('_', $element['#parents']) . '_upload_button', + '#name' => $parents_prefix . '_upload_button', '#type' => 'submit', '#value' => t('Upload'), '#validate' => array(), @@ -884,26 +889,26 @@ function file_managed_file_process($element, &$form_state, $form) { '#ajax' => $ajax_settings, '#weight' => -5, ); - - // Force the progress indicator for the remove button to be either 'none' or - // 'throbber', even if the upload button is using something else. - $ajax_settings['progress']['type'] = ($element['#progress_indicator'] == 'none') ? 'none' : 'throbber'; - $ajax_settings['progress']['message'] = NULL; - $ajax_settings['effect'] = 'none'; $element['remove_button'] = array( - '#name' => implode('_', $element['#parents']) . '_remove_button', + '#name' => $parents_prefix . '_remove_button', '#type' => 'submit', - '#value' => t('Remove'), + '#value' => $element['#multiple'] ? t('Remove selected') : t('Remove'), '#validate' => array(), '#submit' => array('file_managed_file_submit'), '#limit_validation_errors' => array($element['#parents']), '#ajax' => $ajax_settings, - '#weight' => -5, + '#weight' => 1, ); - $element['fid'] = array( + // Force the progress indicator for the remove button to be either 'none' or + // 'throbber', even if the upload button is using something else. + $ajax_settings['progress']['type'] = ($element['#progress_indicator'] == 'none') ? 'none' : 'throbber'; + $ajax_settings['progress']['message'] = NULL; + $ajax_settings['effect'] = 'none'; + + $element['fids'] = array( '#type' => 'hidden', - '#value' => $fid, + '#value' => $fids, ); // Add progress bar support to the upload if possible. @@ -937,21 +942,32 @@ function file_managed_file_process($element, &$form_state, $form) { // The file upload field itself. $element['upload'] = array( - '#name' => 'files[' . implode('_', $element['#parents']) . ']', + '#name' => 'files[' . $parents_prefix . ']', '#type' => 'file', '#title' => t('Choose a file'), '#title_display' => 'invisible', '#size' => $element['#size'], + '#multiple' => $element['#multiple'], '#theme_wrappers' => array(), '#weight' => -10, ); - if ($fid && $element['#file']) { - $element['filename'] = array( - '#type' => 'markup', - '#markup' => theme('file_link', array('file' => $element['#file'])) . ' ', - '#weight' => -10, - ); + if (!empty($fids) && $element['#files']) { + foreach ($element['#files'] as $delta => $file) { + if ($element['#multiple']) { + $element['file_' . $delta]['selected'] = array( + '#type' => 'checkbox', + '#title' => theme('file_link', array('file' => $file)) . ' ', + ); + } + else { + $element['file_' . $delta]['filename'] = array( + '#type' => 'markup', + '#markup' => theme('file_link', array('file' => $file)) . ' ', + '#weight' => -10, + ); + } + } } // Add the extension list to the page as JavaScript settings. @@ -978,28 +994,30 @@ function file_managed_file_process($element, &$form_state, $form) { * This function is assigned as a #value_callback in file_element_info(). */ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) { - $fid = 0; - - // Find the current value of this field from the form state. - $form_state_fid = $form_state['values']; - foreach ($element['#parents'] as $parent) { - $form_state_fid = isset($form_state_fid[$parent]) ? $form_state_fid[$parent] : 0; - } - - if ($element['#extended'] && isset($form_state_fid['fid'])) { - $fid = $form_state_fid['fid']; - } - elseif (is_numeric($form_state_fid)) { - $fid = $form_state_fid; - } + $fids = array(); + + // Find the current value of this field. + $fids = !empty($input['fids']) ? explode(' ', $input['fids']) : array(); + $fids = array_map( + function($item) { + return (int) $item; + }, + $fids + ); // Process any input and save new uploads. if ($input !== FALSE) { + $input = array('fids' => $fids); $return = $input; // Uploads take priority over all other values. - if ($file = file_managed_file_save_upload($element)) { - $fid = $file->fid; + if ($files = file_managed_file_save_upload($element)) { + if ($element['#multiple']) { + $fids = array_merge($fids, array_keys($files)); + } + else { + $fids = array_keys($files); + } } else { // Check for #filefield_value_callback values. @@ -1011,9 +1029,15 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) $callback($element, $input, $form_state); } } - // Load file if the FID has changed to confirm it exists. - if (isset($input['fid']) && $file = file_load($input['fid'])) { - $fid = $file->fid; + + // Load files if the FIDs has changed to confirm they exist. + if (!empty($input['fids'])) { + $fids = array(); + foreach ($input['fids'] as $key => $fid) { + if ($file = file_load($fid)) { + $fids[] = $file->fid; + } + } } } } @@ -1021,22 +1045,26 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) // If there is no input, set the default value. else { if ($element['#extended']) { - $default_fid = isset($element['#default_value']['fid']) ? $element['#default_value']['fid'] : 0; - $return = isset($element['#default_value']) ? $element['#default_value'] : array('fid' => 0); + $default_fids = isset($element['#default_value']['fids']) ? $element['#default_value']['fids'] : array(); + $return = isset($element['#default_value']) ? $element['#default_value'] : array('fids' => array()); } else { - $default_fid = isset($element['#default_value']) ? $element['#default_value'] : 0; - $return = array('fid' => 0); + $default_fids = isset($element['#default_value']) ? $element['#default_value'] : array(); + $return = array('fids' => array()); } // Confirm that the file exists when used as a default value. - if ($default_fid && $file = file_load($default_fid)) { - $fid = $file->fid; + if (!empty($default_fids)) { + $fids = array(); + foreach ($default_fids as $key => $fid) { + if ($file = file_load($fid)) { + $fids[] = $file->fid; + } + } } } - $return['fid'] = $fid; - + $return['fids'] = $fids; return $return; } @@ -1051,28 +1079,41 @@ function file_managed_file_validate(&$element, &$form_state) { // references. This prevents unmanaged files from being deleted if this // item were to be deleted. $clicked_button = end($form_state['triggering_element']['#parents']); - if ($clicked_button != 'remove_button' && !empty($element['fid']['#value'])) { - if ($file = file_load($element['fid']['#value'])) { - if ($file->status == FILE_STATUS_PERMANENT) { - $references = file_usage()->listUsage($file); - if (empty($references)) { - form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title']))); + if ($clicked_button != 'remove_button' && !empty($element['fids']['#value'])) { + // Was here but I do not see any sense in it anymore. Leaving here for reference if any + // strange bugs occur during development of the patch. + //$fids = is_array($element['fids']['#value']) ? $element['fids']['#value'] : explode(' ', $element['fids']['#value']); + $fids = $element['fids']['#value']; + foreach ($fids as $fid) { + if ($file = file_load($fid)) { + if ($file->status == FILE_STATUS_PERMANENT) { + $references = file_ufile_usage()->listUsage($file); + if (empty($references)) { + form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title']))); + } } } - } - else { - form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title']))); + else { + form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title']))); + } } } // Check required property based on the FID. - if ($element['#required'] && empty($element['fid']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) { + if ($element['#required'] && empty($element['fids']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) { form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title']))); } - // Consolidate the array value of this field to a single FID. + // Save entire values to storage + $values = NestedArray::getValue($form_state['values'], $element['#array_parents']); + if (!isset($form_state['storage']['managed_file_values'])) { + $form_state['storage']['managed_file_values'] = array(); + } + NestedArray::setValue($form_state['storage']['managed_file_values'], $element['#array_parents'], $values); + + // Consolidate the array value of this field to array of fids. if (!$element['#extended']) { - form_set_value($element, $element['fid']['#value'], $form_state); + form_set_value($element, $element['fids']['#value'], $form_state); } } @@ -1093,11 +1134,33 @@ function file_managed_file_submit($form, &$form_state) { // button was clicked. Action is needed here for the remove button, because we // only remove a file in response to its remove button being clicked. if ($button_key == 'remove_button') { - // If it's a temporary file we can safely remove it immediately, otherwise - // it's up to the implementing module to remove usages of files to have them - // removed. - if ($element['#file'] && $element['#file']->status == 0) { - file_delete($element['#file']->fid); + // Get files that need to be removed from list. + $fids = array(); + $values = NestedArray::getValue($form_state['storage']['managed_file_values'], $parents); + if (!is_array($values)) { + $values = array('fids' => array()); + } + + if ($element['#multiple']) { + foreach ($values as $name => $value) { + if (strpos($name, 'file_') === 0 && $value['selected']) { + $fids[] = (int) substr($name, 5); + } + } + } + else { + $fids = $values['fids']; + } + + array_diff($values['fids'], $fids); + + foreach ($fids as $fid) { + // If it's a temporary file we can safely remove it immediately, otherwise + // it's up to the implementing module to remove usages of files to have them + // removed. + if ($element['#files'][$fid] && $element['#files'][$fid]->status == 0) { + file_delete($element['#files'][$fid]->fid); + } } // Update both $form_state['values'] and $form_state['input'] to reflect // that the file has been removed, so that the form is rebuilt correctly. @@ -1106,9 +1169,9 @@ function file_managed_file_submit($form, &$form_state) { // when the managed_file element is part of a field widget. // $form_state['input'] must be updated so that file_managed_file_value() // has correct information during the rebuild. - $values_element = $element['#extended'] ? $element['fid'] : $element; - form_set_value($values_element, NULL, $form_state); - NestedArray::setValue($form_state['input'], $values_element['#parents'], NULL); + $values_element = $element['#extended'] ? $element['fids'] : $element['fids']; + form_set_value($values_element, $values['fids'], $form_state); + NestedArray::setValue($form_state['input'], $values_element['#parents'], implode(' ', $values['fids'])); } // Set the form to rebuild so that $form is correctly updated in response to @@ -1143,13 +1206,19 @@ function file_managed_file_save_upload($element) { return FALSE; } - if (!$file = file_save_upload($upload_name, $element['#upload_validators'], $destination)) { - watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name)); - form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title']))); - return FALSE; + // Loop through any attached files and save them to the database. + $files = array(); + $file_count = count(array_filter($_FILES['files']['name'][$upload_name])); + while ($file_count > 0) { + $file_count--; + if (!$file = file_save_upload($upload_name, $file_count, $element['#upload_validators'], $destination)) { + watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name)); + form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title']))); + } + $files[$file->fid] = $file; } - return $file; + return $files; } /** @@ -1204,9 +1273,11 @@ function theme_file_managed_file($variables) { */ function file_managed_file_pre_render($element) { // If we already have a file, we don't want to show the upload controls. - if (!empty($element['#value']['fid'])) { - $element['upload']['#access'] = FALSE; - $element['upload_button']['#access'] = FALSE; + if (!empty($element['#value']['fids'])) { + if (!$element['#multiple']) { + $element['upload']['#access'] = FALSE; + $element['upload_button']['#access'] = FALSE; + } } // If we don't already have a file, there is nothing to remove. else { diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php index 9865314..5b4469c 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php @@ -28,7 +28,7 @@ public static function getInfo() { function testManagedFile() { // Check that $element['#size'] is passed to the child upload element. $this->drupalGet('file/test'); - $this->assertFieldByXpath('//input[@name="files[nested_file]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.'); + $this->assertFieldByXpath('//input[@name="files[nested_file][]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.'); // Perform the tests with all permutations of $form['#tree'] and // $element['#extended']. @@ -40,26 +40,26 @@ function testManagedFile() { // Submit without a file. $this->drupalPost($path, array(), t('Save')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submitted without a file.')); // Submit a new file, without using the Upload button. $last_fid_prior = $this->getLastFileId(); - $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri)); + $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri)); $this->drupalPost($path, $edit, t('Save')); $last_fid = $this->getLastFileId(); $this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Submit handler has correct file info.')); // Submit no new input, but with a default file. $this->drupalPost($path . '/' . $last_fid, array(), t('Save')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Empty submission did not change an existing file.')); // Now, test the Upload and Remove buttons, with and without Ajax. foreach (array(FALSE, TRUE) as $ajax) { // Upload, then Submit. $last_fid_prior = $this->getLastFileId(); $this->drupalGet($path); - $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri)); + $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri)); if ($ajax) { $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button'); } @@ -69,7 +69,7 @@ function testManagedFile() { $last_fid = $this->getLastFileId(); $this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.')); $this->drupalPost(NULL, array(), t('Save')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Submit handler has correct file info.')); // Remove, then Submit. $this->drupalGet($path . '/' . $last_fid); @@ -80,11 +80,11 @@ function testManagedFile() { $this->drupalPost(NULL, array(), t('Remove')); } $this->drupalPost(NULL, array(), t('Save')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submission after file removal was successful.')); // Upload, then Remove, then Submit. $this->drupalGet($path); - $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri)); + $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri)); if ($ajax) { $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button'); $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button'); @@ -94,7 +94,7 @@ function testManagedFile() { $this->drupalPost(NULL, array(), t('Remove')); } $this->drupalPost(NULL, array(), t('Save')); - $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.')); + $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submission after file upload and removal was successful.')); } } } diff --git a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php index c2adbd6..dd55495 100644 --- a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php @@ -53,7 +53,7 @@ function setUp() { // Upload with replace to guarantee there's something there. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), ); $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); @@ -82,7 +82,7 @@ function testNormal() { // Upload a second file. $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); $image2 = current($this->drupalGetTestFiles('image')); - $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri)); + $edit = array('files[file_test_upload][]' => drupal_realpath($image2->uri)); $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); $this->assertRaw(t('You WIN!')); @@ -106,7 +106,7 @@ function testNormal() { $image3_realpath = drupal_realpath($image3->uri); $dir = $this->randomName(); $edit = array( - 'files[file_test_upload]' => $image3_realpath, + 'files[file_test_upload][]' => $image3_realpath, 'file_subdir' => $dir, ); $this->drupalPost('file-test/upload', $edit, t('Submit')); @@ -126,7 +126,7 @@ function testHandleExtension() { $extensions = 'foo'; $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), 'extensions' => $extensions, ); @@ -146,7 +146,7 @@ function testHandleExtension() { // Now tell file_save_upload() to allow the extension of our test image. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), 'extensions' => $extensions, ); @@ -164,7 +164,7 @@ function testHandleExtension() { // Now tell file_save_upload() to allow any extension. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), 'allow_all_extensions' => TRUE, ); $this->drupalPost('file-test/upload', $edit, t('Submit')); @@ -184,7 +184,7 @@ function testHandleDangerousFile() { // safety. Also check to make sure its MIME type was changed. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->phpfile->uri), + 'files[file_test_upload][]' => drupal_realpath($this->phpfile->uri), 'is_image_file' => FALSE, 'extensions' => 'php', ); @@ -231,7 +231,7 @@ function testHandleFileMunge() { $extensions = $this->image_extension; $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), 'extensions' => $extensions, ); @@ -253,7 +253,7 @@ function testHandleFileMunge() { file_test_reset(); $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->uri), + 'files[file_test_upload][]' => drupal_realpath($this->image->uri), 'allow_all_extensions' => TRUE, ); @@ -273,7 +273,7 @@ function testHandleFileMunge() { function testExistingRename() { $edit = array( 'file_test_replace' => FILE_EXISTS_RENAME, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) + 'files[file_test_upload][]' => drupal_realpath($this->image->uri) ); $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); @@ -289,7 +289,7 @@ function testExistingRename() { function testExistingReplace() { $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) + 'files[file_test_upload][]' => drupal_realpath($this->image->uri) ); $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); @@ -305,7 +305,7 @@ function testExistingReplace() { function testExistingError() { $edit = array( 'file_test_replace' => FILE_EXISTS_ERROR, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) + 'files[file_test_upload][]' => drupal_realpath($this->image->uri) ); $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); diff --git a/core/modules/file/tests/file_module_test.module b/core/modules/file/tests/file_module_test.module index b962e2a..bff3041 100644 --- a/core/modules/file/tests/file_module_test.module +++ b/core/modules/file/tests/file_module_test.module @@ -31,7 +31,7 @@ function file_module_test_menu() { * @see file_module_test_form_submit() * @ingroup forms */ -function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FALSE, $default_fid = NULL) { +function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = TRUE, $default_fids = NULL, $multiple = NULL) { $form['#tree'] = (bool) $tree; $form['nested']['file'] = array( @@ -41,9 +41,11 @@ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FA '#progress_message' => t('Please wait...'), '#extended' => (bool) $extended, '#size' => 13, + '#multiple' => (bool) $multiple, ); - if ($default_fid) { - $form['nested']['file']['#default_value'] = $extended ? array('fid' => $default_fid) : $default_fid; + if ($default_fids) { + $default_fids = explode(',', $default_fids); + $form['nested']['file']['#default_value'] = $extended ? array('fids' => $default_fids) : $default_fids; } $form['textfield'] = array( @@ -64,12 +66,22 @@ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FA */ function file_module_test_form_submit($form, &$form_state) { if ($form['#tree']) { - $fid = $form['nested']['file']['#extended'] ? $form_state['values']['nested']['file']['fid'] : $form_state['values']['nested']['file']; + $uploads = $form_state['values']['nested']['file']; } else { - $fid = $form['nested']['file']['#extended'] ? $form_state['values']['file']['fid'] : $form_state['values']['file']; + $uploads = $form_state['values']['file']; } - drupal_set_message(t('The file id is %fid.', array('%fid' => $fid))); + + if ($form['nested']['file']['#extended']) { + $uploads = $uploads['fids']; + } + + $fids = array(); + foreach ($uploads as $fid) { + $fids[] = $fid; + } + + drupal_set_message(t('The file ids are %fids.', array('%fids' => implode(',', $fids)))); } /** diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index 1d924de..caeb037 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -126,7 +126,7 @@ function _file_test_form_submit(&$form, &$form_state) { $validators['file_validate_extensions'] = array($form_state['values']['extensions']); } - $file = file_save_upload('file_test_upload', $validators, $destination, $form_state['values']['file_test_replace']); + $file = file_save_upload('file_test_upload', 0, $validators, $destination, $form_state['values']['file_test_replace']); if ($file) { $form_state['values']['file_test_upload'] = $file; drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri))); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 336f84b..829312e 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -495,6 +495,8 @@ function system_element_info() { ); $types['file'] = array( '#input' => TRUE, + '#multiple' => FALSE, + '#process' => array('form_process_file'), '#size' => 60, '#pre_render' => array('form_pre_render_file'), '#theme' => 'input__file',