diff --git a/core/includes/file.inc b/core/includes/file.inc index c66a2e0..dc98bf7 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1023,14 +1023,14 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { } /** - * Saves a file upload to a new location. + * Saves file uploads to a new location. * - * The file will be added to the {file_managed} table as a temporary file. + * The files will be added to the {file_managed} table as temporary files. * Temporary files are periodically cleaned. Use file_usage()->add() to register * the usage of the file which will automatically mark it as permanent. * * @param $source - * A string specifying the filepath or URI of the uploaded file to save. + * A string specifying the filepath or URI of the uploaded files to save. * @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. @@ -1044,6 +1044,9 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { * A string containing the URI $source should be copied to. * This must be a stream wrapper URI. If this value is omitted, Drupal's * temporary files scheme will be used ("temporary://"). + * @param $position + * Delta of a file of only one file needs to be saved. All files + * will be saved if set to FALSE (default). * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE: Replace the existing file. @@ -1052,181 +1055,203 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { * - FILE_EXISTS_ERROR: Do nothing and return FALSE. * * @return - * An object containing the file information if the upload succeeded, FALSE - * in the event of an error, or NULL if no file was uploaded. The - * documentation for the "File interface" group, which you can find under + * Function returns array of files or a single file object if $position + * != FALSE. Each file object contains the file information if the + * upload succeeded or FALSE in the event of an error. Function + * returns NULL if no file was uploaded. + * + * The documentation for the "File interface" group, which you can find under * Related topics, or the header at the top of this file, documents the * components of a file entity. In addition to the standard components, * this function adds: * - 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, $validators = array(), $destination = FALSE, $position = 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. + // already been processed and the paths in $_FILES will be invalid. if (isset($upload_cache[$source])) { - return $upload_cache[$source]; + if ($position === FALSE) { + return $upload_cache[$source]; + } + else { + return $upload_cache[$source][$position]; + } } // Make sure there's an upload to process. - if (empty($_FILES['files']['name'][$source])) { + if (empty($_FILES['files']['name'][$source][0])) { 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]) { - 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'); - return FALSE; + $files = array(); + foreach ($_FILES['files']['name'][$source] as $delta => $name) { + // Check for file upload errors and return FALSE for this file 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][$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' => $name, '%maxsize' => format_size(file_upload_max_size()))), 'error'); + $files[$delta] = FALSE; + continue; - 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'); - 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' => $name)), 'error'); + $files[$delta] = FALSE; + continue; - 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])) { - break; - } + 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][$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'); - return FALSE; - } - // Begin building file entity. - $values = array( - 'uid' => $user->uid, - 'status' => 0, - 'filename' => trim(drupal_basename($_FILES['files']['name'][$source]), '.'), - 'uri' => $_FILES['files']['tmp_name'][$source], - 'filesize' => $_FILES['files']['size'][$source], - ); - $values['filemime'] = file_get_mimetype($values['filename']); - $file = entity_create('file', $values); - - $extensions = ''; - if (isset($validators['file_validate_extensions'])) { - if (isset($validators['file_validate_extensions'][0])) { - // Build the list of non-munged extensions if the caller provided them. - $extensions = $validators['file_validate_extensions'][0]; + // Unknown error + default: + drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $name)), 'error'); + $files[$delta] = FALSE; + continue; + + } + // Begin building file entity. + $values = array( + 'uid' => $user->uid, + 'status' => 0, + 'filename' => trim(drupal_basename($name, '.')), + 'uri' => $_FILES['files']['tmp_name'][$source][$delta], + 'filesize' => $_FILES['files']['size'][$source][$delta], + ); + $values['filemime'] = file_get_mimetype($values['filename']); + $file = entity_create('file', $values); + + $extensions = ''; + if (isset($validators['file_validate_extensions'])) { + if (isset($validators['file_validate_extensions'][0])) { + // Build the list of non-munged extensions if the caller provided them. + $extensions = $validators['file_validate_extensions'][0]; + } + else { + // If 'file_validate_extensions' is set and the list is empty then the + // caller wants to allow any extension. In this case we have to remove the + // validator or else it will reject all extensions. + unset($validators['file_validate_extensions']); + } } else { - // If 'file_validate_extensions' is set and the list is empty then the - // caller wants to allow any extension. In this case we have to remove the - // validator or else it will reject all extensions. - unset($validators['file_validate_extensions']); + // No validator was provided, so add one using the default list. + // Build a default non-munged safe list for file_munge_filename(). + $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'; + $validators['file_validate_extensions'] = array(); + $validators['file_validate_extensions'][0] = $extensions; } - } - else { - // No validator was provided, so add one using the default list. - // Build a default non-munged safe list for file_munge_filename(). - $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'; - $validators['file_validate_extensions'] = array(); - $validators['file_validate_extensions'][0] = $extensions; - } - - if (!empty($extensions)) { - // Munge the filename to protect against possible malicious extension hiding - // within an unknown file type (ie: filename.html.foo). - $file->filename = file_munge_filename($file->filename, $extensions); - } - - // Rename potentially executable files, to help prevent exploits (i.e. will - // rename filename.php.foo and filename.php to filename.php.foo.txt and - // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads' - // evaluates to TRUE. - if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { - $file->filemime = 'text/plain'; - $file->uri .= '.txt'; - $file->filename .= '.txt'; - // The .txt extension may not be in the allowed list of extensions. We have - // to add it here or else the file upload will fail. + if (!empty($extensions)) { - $validators['file_validate_extensions'][0] .= ' txt'; - drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename))); + // Munge the filename to protect against possible malicious extension hiding + // within an unknown file type (ie: filename.html.foo). + $file->filename = file_munge_filename($file->filename, $extensions); } - } - // If the destination is not provided, use the temporary directory. - if (empty($destination)) { - $destination = 'temporary://'; - } + // Rename potentially executable files, to help prevent exploits (i.e. will + // rename filename.php.foo and filename.php to filename.php.foo.txt and + // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads' + // evaluates to TRUE. + if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { + $file->filemime = 'text/plain'; + $file->uri .= '.txt'; + $file->filename .= '.txt'; + // The .txt extension may not be in the allowed list of extensions. We have + // to add it here or else the file upload will fail. + if (!empty($extensions)) { + $validators['file_validate_extensions'][0] .= ' txt'; + drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename))); + } + } - // Assert that the destination contains a valid stream. - $destination_scheme = file_uri_scheme($destination); - if (!file_stream_wrapper_valid_scheme($destination_scheme)) { - drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error'); - return FALSE; - } + // If the destination is not provided, use the temporary directory. + if (empty($destination)) { + $destination = 'temporary://'; + } - $file->source = $source; - // A URI may already have a trailing slash or look like "public://". - if (substr($destination, -1) != '/') { - $destination .= '/'; - } - $file->destination = file_destination($destination . $file->filename, $replace); - // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and - // there's an existing file so we need to bail. - if ($file->destination === FALSE) { - drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error'); - return FALSE; - } + // Assert that the destination contains a valid stream. + $destination_scheme = file_uri_scheme($destination); + if (!file_stream_wrapper_valid_scheme($destination_scheme)) { + drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error'); + $files[$delta] = FALSE; + continue; + } + + $file->source = $source; + // A URI may already have a trailing slash or look like "public://". + if (substr($destination, -1) != '/') { + $destination .= '/'; + } + $file->destination = file_destination($destination . $file->filename, $replace); + // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and + // there's an existing file so we need to bail. + if ($file->destination === FALSE) { + drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error'); + $files[$delta] = FALSE; + continue; + } - // Add in our check of the the file name length. - $validators['file_validate_name_length'] = array(); + // Add in our check of the the file name length. + $validators['file_validate_name_length'] = array(); - // Call the validation functions specified by this function's caller. - $errors = file_validate($file, $validators); + // Call the validation functions specified by this function's caller. + $errors = file_validate($file, $validators); - // Check for errors. - if (!empty($errors)) { - $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename)); - if (count($errors) > 1) { - $message .= theme('item_list', array('items' => $errors)); + // Check for errors. + if (!empty($errors)) { + $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename)); + if (count($errors) > 1) { + $message .= theme('item_list', array('items' => $errors)); + } + else { + $message .= ' ' . array_pop($errors); + } + form_set_error($source, $message); + $files[$delta] = FALSE; + continue; } - else { - $message .= ' ' . array_pop($errors); + + // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary + // directory. This overcomes open_basedir restrictions for future file + // operations. + $file->uri = $file->destination; + 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)); + $files[$delta] = FALSE; + continue; } - form_set_error($source, $message); - return FALSE; - } - // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary - // 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)) { - 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; - } + // Set the permissions on the new file. + drupal_chmod($file->uri); - // Set the permissions on the new file. - drupal_chmod($file->uri); - - // If we are replacing an existing file re-use its database record. - if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri)); - if (count($existing_files)) { - $existing = reset($existing_files); - $file->fid = $existing->fid; + // If we are replacing an existing file re-use its database record. + if ($replace == FILE_EXISTS_REPLACE) { + $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri)); + if (count($existing_files)) { + $existing = reset($existing_files); + $file->fid = $existing->fid; + } } + + // If we made it this far it's safe to record this file in the database. + $file->save(); + $files[$delta] = $file; } - // 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; - return $file; + // Add files to the cache. + $upload_cache[$source] = $files; + + return $position === FALSE ? $files : $files[$position]; } /** diff --git a/core/includes/form.inc b/core/includes/form.inc index 513615c..9b29a03 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/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc index 3612e0c..ebe509f 100644 --- a/core/modules/aggregator/aggregator.admin.inc +++ b/core/modules/aggregator/aggregator.admin.inc @@ -358,7 +358,7 @@ function aggregator_form_opml_validate($form, &$form_state) { function aggregator_form_opml_submit($form, &$form_state) { $data = ''; $validators = array('file_validate_extensions' => array('opml xml')); - if ($file = file_save_upload('upload', $validators)) { + if ($file = file_save_upload('upload', $validators, FALSE, 0)) { $data = file_get_contents($file->uri); } else { diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index e166b82..c9300c4 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -387,7 +387,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) { // Ensure that all the required properties are returned even if empty. $return += array( - 'fid' => 0, + 'fids' => array(), 'display' => 1, 'description' => '', ); @@ -396,6 +396,39 @@ function file_field_widget_value($element, $input = FALSE, $form_state) { } /** + * 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['#array_parents']; + $values = NestedArray::getValue($form_state['values'], $parents); + + array_pop($parents); + $current = count(element_children(NestedArray::getValue($form, $parents))) - 1; + + $field = field_info_field($element['#field_name']); + $uploaded = count($values['fids']); + $count = $uploaded + $current; + if ($count > $field['cardinality']) { + $keep = $uploaded - $count + $field['cardinality']; + drupal_set_message( + t( + 'Field %field can hold only @max values. Only @count values were uploaded.', + array( + '%field' => $field['field_name'], + '@max' => $field['cardinality'], + '@count' => $keep) + ), + 'warning' + ); + $values['fids'] = array_slice($values['fids'], 0, $keep); + NestedArray::setValue($form_state['values'], $element['#array_parents'], $values); + } +} + +/** * Render API callback: Processes a file_generic field element. * * Expands the file_generic type to include the description and display fields. @@ -404,7 +437,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) { */ function file_field_widget_process($element, &$form_state, $form) { $item = $element['#value']; - $item['fid'] = $element['fid']['#value']; + $item['fids'] = $element['fids']['#value']; $field = field_widget_field($element, $form_state); $instance = field_widget_instance($element, $form_state); @@ -413,9 +446,9 @@ function file_field_widget_process($element, &$form_state, $form) { $element['#theme'] = 'file_widget'; // Add the display field if enabled. - if (!empty($field['settings']['display_field']) && $item['fid']) { + if (!empty($field['settings']['display_field']) && $item['fids']) { $element['display'] = array( - '#type' => empty($item['fid']) ? 'hidden' : 'checkbox', + '#type' => empty($item['fids']) ? 'hidden' : 'checkbox', '#title' => t('Include file in display'), '#value' => isset($item['display']) ? $item['display'] : $field['settings']['display_default'], '#attributes' => array('class' => array('file-display')), @@ -429,7 +462,7 @@ function file_field_widget_process($element, &$form_state, $form) { } // Add the description field if enabled. - if (!empty($instance['settings']['description_field']) && $item['fid']) { + if (!empty($instance['settings']['description_field']) && $item['fids']) { $config = config('file.settings'); $element['description'] = array( '#type' => $config->get('description.type'), @@ -561,13 +594,24 @@ function file_field_widget_submit($form, &$form_state) { $submitted_values = NestedArray::getValue($form_state['values'], array_slice($button['#array_parents'], 0, -2)); foreach ($submitted_values as $delta => $submitted_value) { - if (!$submitted_value['fid']) { + if (empty($submitted_value['fids'])) { unset($submitted_values[$delta]); } } + // If there are more files uploaded via 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) { + foreach ($submitted_value['fids'] as $fid) { + $new_value = $submitted_value; + $new_value['fids'] = array($fid); + $new_values[] = $new_value; + } + } + // Re-index deltas after removing empty items. - $submitted_values = array_values($submitted_values); + $submitted_values = array_values($new_values); // Update form_state values. NestedArray::setValue($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values); @@ -593,9 +637,10 @@ function theme_file_widget($variables) { // The "form-managed-file" class is required for proper Ajax functionality. $output .= '
'; - if ($element['fid']['#value'] != 0) { + if (!empty($element['fids']['#value'])) { // Add the file size after the file name. - $element['filename']['#markup'] .= ' (' . format_size($element['#file']->filesize) . ') '; + $file = reset($element['#files']); + $element['file_' . $file->fid]['filename']['#markup'] .= ' (' . format_size($file->filesize) . ') '; } $output .= drupal_render_children($element); $output .= '
'; @@ -642,7 +687,7 @@ function theme_file_widget_multiple($variables) { $rows = array(); foreach ($widgets as $key => &$widget) { // Save the uploading row for last. - if ($widget['#file'] == FALSE) { + if (empty($widget['#files'])) { $widget['#title'] = $element['#file_upload_title']; $widget['#description'] = $element['#file_upload_description']; continue; diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 913f75a..6981752 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')), @@ -866,11 +867,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( @@ -885,7 +890,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(), @@ -894,26 +899,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. @@ -947,21 +952,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. @@ -988,28 +1004,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['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. @@ -1021,9 +1039,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; + } + } } } } @@ -1031,22 +1055,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; } @@ -1061,28 +1089,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_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); } } @@ -1103,11 +1144,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']; + } + + $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. @@ -1116,9 +1179,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 @@ -1153,13 +1216,25 @@ 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; + // Save attached files to the database. + if(count(array_filter($_FILES['files']['name'][$upload_name])) > 0) { + if (!$files = 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('Files in the !name field were unable to be uploaded.', array('!name' => $element['#title']))); + return array(); + } + + // Value callback expects FIDs to be keys. + $uploads = array(); + $files = array_filter($files); + foreach ($files as $file) { + $uploads[$file->fid] = $file; + } + + return $uploads; } - return $file; + return array(); } /** @@ -1214,9 +1289,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/Plugin/field/widget/FileWidget.php b/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php index 98a7a96..9842ad6 100644 --- a/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php +++ b/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php @@ -166,7 +166,7 @@ protected function formMultipleElements(EntityInterface $entity, array $items, $ */ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { $defaults = array( - 'fid' => 0, + 'fids' => array(), 'display' => !empty($this->field['settings']['display_default']), 'description' => '', ); @@ -186,13 +186,43 @@ public function formElement(array $items, $delta, array $element, $langcode, arr ); $element['#weight'] = $delta; + + // If we just loaded from DB we have to translate value to + // multivalue file widgets. + if (!isset($items[$delta]['fids']) && isset($items[$delta]['fid'])) { + $items[$delta]['fids'][0] = $items[$delta]['fid']; + } $element['#default_value'] = !empty($items[$delta]) ? $items[$delta] : $defaults; - if (empty($element['#default_value']['fid'])) { + if (empty($element['#default_value']['fids'])) { $element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators'])); + $element['#multiple'] = $this->field['cardinality'] != 0 ? TRUE : FALSE; + if ($this->field['cardinality'] != 1 && $this->field['cardinality'] != -1) { + $element['#element_validate'] = array('file_field_widget_multiple_count_validate'); + } } return $element; } + /** + * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::massageFormValues(). + */ + public function massageFormValues(array $values, array $form, array &$form_state) { + // Since file upload widget now supports uploads of more than one file at + // the time it always returns array of fids. We have to translate this to + // a single fid, as field expects single value. + $new_values = array(); + foreach ($values as &$value) { + foreach ($value['fids'] as $fid) { + $new_value = $value; + $new_value['fid'] = $fid; + unset($new_value['fids']); + $new_values[] = $new_value; + } + } + + return $new_values; + } + } 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..5ec0428 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, 0, $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/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 5420ac9..981a447 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -108,7 +108,7 @@ function locale_translate_import_form($form, &$form_state) { */ function locale_translate_import_form_submit($form, &$form_state) { // Ensure we have the file uploaded. - if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://')) { + if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://', 0)) { // Add language, if not yet supported. $language = language_load($form_state['values']['langcode']); diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 5e5b270..5f5b1b9 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -1114,7 +1114,7 @@ function hook_delete(Drupal\node\Node $node) { */ function hook_prepare(Drupal\node\Node $node) { if ($file = file_check_upload($field_name)) { - $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE)); + $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE), FALSE, 0); if ($file) { if (!image_get_info($file->uri)) { form_set_error($field_name, t('Uploaded file is not a valid image')); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 37a2bf2..5cb47d2 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -609,7 +609,7 @@ function system_theme_settings_validate($form, &$form_state) { $validators = array('file_validate_is_image' => array()); // Check for a new uploaded logo. - $file = file_save_upload('logo_upload', $validators); + $file = file_save_upload('logo_upload', $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { @@ -625,7 +625,7 @@ function system_theme_settings_validate($form, &$form_state) { $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg')); // Check for a new uploaded favicon. - $file = file_save_upload('favicon_upload', $validators); + $file = file_save_upload('favicon_upload', $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 106ef14..24f47be 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', diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index 0047a52..bc84058 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -645,7 +645,7 @@ function update_manager_install_form_submit($form, &$form_state) { elseif ($_FILES['files']['name']['project_upload']) { $validators = array('file_validate_extensions' => array(archiver_get_extensions())); $field = 'project_upload'; - if (!($finfo = file_save_upload($field, $validators, NULL, FILE_EXISTS_REPLACE))) { + if (!($finfo = file_save_upload($field, $validators, NULL, 0, FILE_EXISTS_REPLACE))) { // Failed to upload the file. file_save_upload() calls form_set_error() on // failure. return;