From 367d40d60a6480e2f630d53de7127b9f96e05160 Mon Sep 17 00:00:00 2001 From: William Hearn Date: Thu, 3 Sep 2015 18:33:34 -0400 Subject: [PATCH] FIx working. --- file_entity.module | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ file_entity.pages.inc | 58 +++++++++++++++++++++++++++-- 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/file_entity.module b/file_entity.module index 641aa2e..9ff4249 100644 --- a/file_entity.module +++ b/file_entity.module @@ -197,6 +197,13 @@ function file_entity_menu() { if (module_exists('plupload') && module_exists('multiform')) { $items['file/add']['page arguments'] = array('file_entity_add_upload_multiple'); } + // Autocomplete path for getting a list of file upload directories. + $items['file/add/path/autocomplete'] = array( + 'title' => 'Autocomplete for upload paths', + 'page callback' => 'file_entity_upload_path_autocomplete', + 'access callback' => 'file_entity_upload_path_access', + 'type' => MENU_CALLBACK, + ); $items['file/add/upload'] = array( 'title' => 'Upload', 'type' => MENU_DEFAULT_LOCAL_TASK, @@ -444,6 +451,9 @@ function file_entity_permission() { 'title' => t('Administer files'), 'restrict access' => TRUE, ), + 'choose file destination' => array( + 'title' => t('Select destination for each uploaded file'), + ), 'create files' => array( 'title' => t('Add and upload new files'), ), @@ -2455,6 +2465,96 @@ function file_entity_match_mimetypes($needle, $haystack) { } /** + * Returns a list of possible directories where files can be uploaded. + * + * This is an autocomplete function. + * + * @see file_entity_menu() + * @see file_entity_add_upload_step_upload() + * + * @param string $typed + * The string used for searching. +* + * @return string + * A list of directories in JSON format. + */ +function file_entity_upload_path_autocomplete($typed = "") { + // Don't do anything if the user hasn't entered any data yet. + if (!strlen($typed)) { + drupal_json_output(array()); + return; + } + // Get the location of the file system based on the scheme we're using. + $default_scheme = file_default_scheme(); + $wrapper = file_stream_wrapper_get_instance_by_scheme($default_scheme); + $directory = $wrapper->getDirectoryPath(); + + // Retrieve array of matched directory paths. + $paths = file_entity_dir_match($typed, $directory); + + // Return the result to the form in JSON. + drupal_json_output($paths); +} + +/** + * Returns an array of directories that match part of a search query. + * + * @param $query + * String that will be searched on. + * @param $directory + * A directory path. + * + * @return string + * An array of matches. + */ +function file_entity_dir_match($query, $directory) { + // Initialize an empty list for the possible paths. + $paths = array(); + // Define an iterator to traverse the file system tree. + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $directory, + RecursiveDirectoryIterator::SKIP_DOTS + ), + RecursiveIteratorIterator::SELF_FIRST + ); + // Traverse it. + while ($iterator->valid()) { + // Only act on directories, not files. + if ($iterator->isDir()) { + // Get the subdirectory name ensuring windows paths are supported as well. + $dir_name = str_replace('\\', '/', $iterator->getSubPathName()); + // Get the number of typed characters. + $chars = strlen($query); + // If the current directory is at least as long as the number of typed + // characters, and the typed characters match the beginning of the + // current directory, add the current directory to the list. + if ((strlen($dir_name) >= $chars) && (substr($dir_name, 0, $chars) === $query)) { + $paths[$dir_name] = check_plain($dir_name); + } + } + // Move to the next item in the iteration. + $iterator->next(); + } + return $paths; +} + +/** + * Determines if the current user has access to work with file upload paths. + * + * Proper access entails being able to view the possible upload paths and + * selecting one of them as a target when uploading one or more files. + * + * @return boolean + * TRUE if access to work with file upload paths is allowed. FALSE otherwise. + */ +function file_entity_upload_path_access() { + return user_access('choose file destination') || + user_access('bypass file access') || + user_access('administer files'); +} + +/** * A wrapper function for the PHP function fnmatch(). * * We include this, because Windows servers do not implement fnmatch() until diff --git a/file_entity.pages.inc b/file_entity.pages.inc index f885d4d..02010b8 100644 --- a/file_entity.pages.inc +++ b/file_entity.pages.inc @@ -112,6 +112,19 @@ function file_entity_add_upload_step_upload($form, &$form_state, array $options '#default_value' => isset($form_state['storage']['upload']) ? $form_state['storage']['upload'] : NULL, ); + if (empty($options['internet_media'])) { + // Add an option to specify the upload path if the user has permission to do so. + $form['upload_path'] = array( + '#type' => 'textfield', + '#title' => t('Upload path'), + '#description' => t('Enter the path within the upload folder where you would like to place the file (e.g. "documents/finance"). Do not add preceding or trailing slashes. Leave blank to keep it at the root, with no subfolders.'), + '#default_value' => file_uri_target($form['upload']['#upload_location']), + '#maxlength' => 255, + '#autocomplete_path' => 'file/add/path/autocomplete', + '#access' => file_entity_upload_path_access(), + ); + } + $form['actions'] = array('#type' => 'actions'); $form['actions']['next'] = array( '#type' => 'submit', @@ -437,6 +450,25 @@ function file_entity_add_upload_submit($form, &$form_state) { // Change the file from temporary to permanent. $file->status = FILE_STATUS_PERMANENT; + // Move the file to the specified target directory if the user is authorized. + if (file_entity_upload_path_access() && file_entity_file_is_writeable($file)) { + // A slash is only needed before the filename if we're dropping the file + // somewhere other than the root of the filesystem. Otherwise, we'll + // end up with too many (e.g. "public:///file.png"). + $separator = strlen($form_state['storage']['upload_path']) ? "/" : ""; + // Get the URI for the target path. + $uri_new = file_entity_upload_destination_uri(array( + 'uri_scheme' => file_uri_scheme($file->uri), + 'file_directory' => $form_state['storage']['upload_path'], + )) . $separator . drupal_basename($file->uri); + // If it changed, move the file to its new location. + if ($file->uri != $uri_new) { + $path = drupal_dirname($uri_new); + file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $file = file_move($file, $uri_new, FILE_EXISTS_RENAME); + } + } + // Save the form fields. // Keep in mind that the values for the Field API fields must be in // $form_state['values'] and not in ['storage']. This is true as long as @@ -488,7 +520,12 @@ function file_entity_upload_destination_uri(array $params, array $data = array() 'file_directory' => '', ); - $destination = trim($params['file_directory'], '/'); + // Sanitize the provided path by removing any slashes and dots from the + // beginning and end, and neutralize any attempts to traverse up the tree. + // We're assuming it's not necessary to run check_url() on it because this is + // usually done on output. + $destination = trim($params['file_directory'], '/.'); + $destination = str_replace('/../', '/', $destination); // Replace tokens. $destination = token_replace($destination, $data); @@ -530,9 +567,22 @@ function file_entity_add_upload_multiple($form, &$form_state, $params = array()) * Submit handler for the multiple upload form. */ function file_entity_add_upload_multiple_submit($form, &$form_state) { - $upload_location = !empty($form['upload']['#upload_location']) ? - $form['upload']['#upload_location'] . '/' : - variable_get('file_default_scheme', 'public') . '://'; + // Set the upload directory where the file should be dropped. + if (file_entity_upload_path_access()) { + // The current user has permission to specify the location. + // Grab it from the submitted form. + $upload_location = file_entity_upload_destination_uri(array( + 'file_directory' => $form_state['values']['path'], + )) . "/"; + } + else if (!empty($form['upload']['#upload_location'])) { + // If the form specfied a directory at the outset, use that. + $upload_location = $form['upload']['#upload_location'] . '/'; + } + else { + // Use the default location for uploaded files. + $upload_location = file_default_scheme() . '://'; + } // Ensure writable destination directory for the files. file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); -- 2.3.2 (Apple Git-55)