diff --git a/core/includes/file.inc b/core/includes/file.inc index 133d64f..020f60d 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1381,6 +1381,8 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) { * * @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. @@ -1411,45 +1413,45 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) { * - 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. @@ -1554,7 +1556,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; @@ -1575,7 +1577,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 395cca7..b5ca9d5 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -4348,6 +4348,17 @@ function theme_file($variables) { } /** + * 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 0ecfcdd..0f99609 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -77,6 +77,7 @@ function file_element_info() { '#upload_validators' => array(), '#upload_location' => NULL, '#size' => 22, + '#multiple' => FALSE, '#extended' => FALSE, '#attached' => array( 'css' => array($file_path . '/file.admin.css'), @@ -362,11 +363,11 @@ function file_file_predelete(File $file) { * This function is assigned as a #process callback in file_element_info(). */ function file_managed_file_process($element, &$form_state, $form) { - $fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0; + $fids = isset($element['#value']) ? $element['#value'] : NULL; // 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'] = $fids ? file_load_multiple($fids) : FALSE; $element['#tree'] = TRUE; $ajax_settings = array( @@ -396,20 +397,10 @@ function file_managed_file_process($element, &$form_state, $form) { $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', - '#type' => 'submit', - '#value' => t('Remove'), - '#validate' => array(), - '#submit' => array('file_managed_file_submit'), - '#limit_validation_errors' => array($element['#parents']), - '#ajax' => $ajax_settings, - '#weight' => -5, - ); - $element['fid'] = array( + $element['fids'] = array( '#type' => 'hidden', - '#value' => $fid, + '#value' => $fids, ); // Add progress bar support to the upload if possible. @@ -448,16 +439,30 @@ function file_managed_file_process($element, &$form_state, $form) { '#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 ($fids && $element['#files']) { + foreach ($element['#files'] as $delta => $file) { + $element['filename_' . $delta] = array( + '#type' => 'markup', + '#markup' => theme('file_link', array('file' => $file)) . ' ', + '#weight' => -10, + ); + $element['remove_button_' . $delta] = array( + '#name' => implode('_', $element['#parents']) . '_remove_button_' . $delta, + '#type' => 'submit', + '#value' => t('Remove'), + '#validate' => array(), + '#submit' => array('file_managed_file_submit'), + '#limit_validation_errors' => array($element['#parents']), + //'#ajax' => $ajax_settings, + '#weight' => -10, + '#file' => $file, + ); + } } // Add the extension list to the page as JavaScript settings. @@ -504,8 +509,8 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) $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)) { + $return = array_keys($files); } else { // Check for #filefield_value_callback values. @@ -528,11 +533,11 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) 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); + $return = isset($element['#default_value']) ? $element['#default_value'] : array(); } else { $default_fid = isset($element['#default_value']) ? $element['#default_value'] : 0; - $return = array('fid' => 0); + $return = array(); } // Confirm that the file exists when used as a default value. @@ -541,8 +546,6 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) } } - $return['fid'] = $fid; - return $return; } @@ -576,10 +579,7 @@ function file_managed_file_validate(&$element, &$form_state) { form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title']))); } - // Consolidate the array value of this field to a single FID. - if (!$element['#extended']) { - form_set_value($element, $element['fid']['#value'], $form_state); - } + form_set_value($element, $element['#value'], $form_state); } /** @@ -649,13 +649,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($_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; } /** diff --git a/core/modules/file/tests/file_module_test.module b/core/modules/file/tests/file_module_test.module index 490ef42..f04b77a 100644 --- a/core/modules/file/tests/file_module_test.module +++ b/core/modules/file/tests/file_module_test.module @@ -28,7 +28,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 = FALSE, $default_fids = NULL) { $form['#tree'] = (bool) $tree; $form['nested']['file'] = array( @@ -38,9 +38,11 @@ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FA '#progress_message' => t('Please wait...'), '#extended' => (bool) $extended, '#size' => 13, + '#multiple' => TRUE, ); - 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('fid' => $default_fids) : $default_fids; } $form['textfield'] = array( @@ -61,12 +63,28 @@ 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']; + if ($form['nested']['file']['#extended']) { + $fids = array(); + foreach ($form_state['values']['nested']['file'] as $fid) { + $fids[] = $fid; + } + } + else { + $fids = $form_state['values']['nested']['file']; + } } else { - $fid = $form['nested']['file']['#extended'] ? $form_state['values']['file']['fid'] : $form_state['values']['file']; + if ($form['nested']['file']['#extended']) { + $fids = array(); + foreach ($form_state['values']['file'] as $file) { + $fids[] = $file['fid']; + } + } + else { + $fids = $form_state['values']['file']; + } } - drupal_set_message(t('The file id is %fid.', array('%fid' => $fid))); + drupal_set_message(t('The file ids are %fid.', array('%fid' => $fid))); } /** diff --git a/core/modules/system/system.module b/core/modules/system/system.module index a76e3ae..3e72717 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -516,6 +516,8 @@ function system_element_info() { ); $types['file'] = array( '#input' => TRUE, + '#multiple' => FALSE, + '#process' => array('form_process_file'), '#size' => 60, '#theme' => 'file', '#theme_wrappers' => array('form_element'),