diff --git a/INSTALL.txt b/INSTALL.txt index 44eca9c8c7379b19cd98443290d6a0596eb69233..382c93dc4096f79c54c2e0d417233838dffa6249 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -127,10 +127,13 @@ INSTALLATION files directory may be changed after Drupal is installed). In some cases, you may need to create the directory and modify its permissions manually. Use the following commands (from the installation directory) - to create the files directory and grant the web server write privileges to it: + to create the public and private files directories and grant the web server + write privileges to them: mkdir sites/default/files chmod o+w sites/default/files + mkdir sites/default/private + chmod o+w sites/default/private The install script will attempt to write-protect the settings.php file and the sites/default directory after saving your configuration. However, you diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 2b41af76c445a2065f7d615d98a4dda774b30995..5abccfeb0f1f8f6331b3a471f5449dbfbd5e4340 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -656,8 +656,8 @@ function drupal_get_filename($type, $name, $filename = NULL) { if (drupal_function_exists('drupal_system_listing')) { $matches = drupal_system_listing($mask, $dir, 'name', 0); - if (!empty($matches[$name]->filepath)) { - $files[$type][$name] = $matches[$name]->filepath; + if (!empty($matches[$name]->uri)) { + $files[$type][$name] = $matches[$name]->uri; } } } diff --git a/includes/common.inc b/includes/common.inc index a3579c1f803feae4c959ae11acd7732131a99480..f6e64cf47207c55b6645e2b65b3a5fabed3aa85a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2560,8 +2560,8 @@ function drupal_get_css($css = NULL) { } $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); - $directory = file_directory_path(); - $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); + $directory = file_directory_path('public'); + $is_writable = is_dir($directory) && is_writable($directory); // A dummy query-string is added to filenames, to gain control over // browser-caching. The string changes on every update or full cache @@ -2601,7 +2601,7 @@ function drupal_get_css($css = NULL) { case 'file': // Depending on whether aggregation is desired, include the file. if (!$item['preprocess'] || !($is_writable && $preprocess_css)) { - $rendered_css[] = ''; + $rendered_css[] = ''; } else { $preprocess_items[$item['media']][] = $item; @@ -2626,8 +2626,8 @@ function drupal_get_css($css = NULL) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". $filename = 'css_' . md5(serialize($items) . $query_string) . '.css'; - $preprocess_file = drupal_build_css_cache($items, $filename); - $rendered_css['preprocess'] .= '' . "\n"; + $preprocess_file = file_create_url(drupal_build_css_cache($items, $filename)); + $rendered_css['preprocess'] .= '' . "\n"; } } // Enclose the inline CSS with the style tag if required. @@ -2653,9 +2653,8 @@ function drupal_build_css_cache($css, $filename) { $data = ''; // Create the css/ within the files folder. - $csspath = file_create_path('css'); - file_check_directory($csspath, FILE_CREATE_DIRECTORY); - + $csspath = 'public://css'; + file_prepare_directory($csspath, FILE_CREATE_DIRECTORY); if (!file_exists($csspath . '/' . $filename)) { // Build aggregate CSS file. foreach ($css as $stylesheet) { @@ -2797,7 +2796,7 @@ function _drupal_load_stylesheet($matches) { * Delete all cached CSS files. */ function drupal_clear_css_cache() { - file_scan_directory(file_create_path('css'), '/.*/', array('callback' => 'file_unmanaged_delete')); + file_scan_directory('public://css', '/.*/', array('callback' => 'file_unmanaged_delete')); } /** @@ -3039,8 +3038,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { $no_preprocess = ''; $files = array(); $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); - $directory = file_directory_path(); - $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); + $directory = file_directory_path('public'); + $is_writable = is_dir($directory) && is_writable($directory); // A dummy query-string is added to filenames, to gain control over // browser-caching. The string changes on every update or full cache @@ -3072,7 +3071,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { case 'file': if (!$item['preprocess'] || !$is_writable || !$preprocess_js) { - $no_preprocess .= '\n"; + $no_preprocess .= '\n"; } else { $files[$item['data']] = $item; @@ -3091,8 +3090,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". $filename = 'js_' . md5(serialize($files) . $query_string) . '.js'; - $preprocess_file = drupal_build_js_cache($files, $filename); - $preprocessed .= '' . "\n"; + $preprocess_file = file_create_url(drupal_build_js_cache($files, $filename)); + $preprocessed .= '' . "\n"; } // Keep the order of JS files consistent as some are preprocessed and others are not. @@ -3363,8 +3362,8 @@ function drupal_build_js_cache($files, $filename) { $contents = ''; // Create the js/ within the files folder. - $jspath = file_create_path('js'); - file_check_directory($jspath, FILE_CREATE_DIRECTORY); + $jspath = 'public://js'; + file_prepare_directory($jspath, FILE_CREATE_DIRECTORY); if (!file_exists($jspath . '/' . $filename)) { // Build aggregate JS file. @@ -3386,7 +3385,7 @@ function drupal_build_js_cache($files, $filename) { * Delete all cached JS files. */ function drupal_clear_js_cache() { - file_scan_directory(file_create_path('js'), '/.*/', array('callback' => 'file_unmanaged_delete')); + file_scan_directory('public://js', '/.*/', array('callback' => 'file_unmanaged_delete')); variable_set('javascript_parsed', array()); } diff --git a/includes/file.inc b/includes/file.inc index 451778ca5d10cc5da4158e3876d4248ad1926dac..c0ce7c5942cedc68f2e68c4a5150014996e6b709 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -24,7 +24,7 @@ * - filename - Name of the file with no path components. This may differ from * the basename of the filepath if the file is renamed to avoid overwriting * an existing file. - * - filepath - Path of the file relative to Drupal root. + * - uri - URI of the file. * - filemime - The file's MIME type. * - filesize - The size of the file in bytes. * - status - A bitmapped field indicating the status of the file. The first 8 @@ -35,28 +35,12 @@ */ /** - * Flag to indicate that the 'public' file download method is enabled. - * - * When using this method, files are available from a regular HTTP request, - * which provides no additional access restrictions. - */ -define('FILE_DOWNLOADS_PUBLIC', 1); - -/** - * Flag to indicate that the 'private' file download method is enabled. - * - * When using this method, all file requests are served by Drupal, during which - * access-control checking can be performed. - */ -define('FILE_DOWNLOADS_PRIVATE', 2); - -/** - * Flag used by file_check_directory() -- create directory if not present. + * Flag used by file_prepare_directory() -- create directory if not present. */ define('FILE_CREATE_DIRECTORY', 1); /** - * Flag used by file_check_directory() -- file permissions may be changed. + * Flag used by file_prepare_directory() -- file permissions may be changed. */ define('FILE_MODIFY_PERMISSIONS', 2); @@ -220,6 +204,8 @@ function file_uri_target($uri) { * * @param $uri * String reference containing the URI to normalize. + * @return + * The normalized URI. */ function file_stream_wrapper_uri_normalize($uri) { $scheme = file_uri_scheme($uri); @@ -299,56 +285,44 @@ function file_stream_wrapper_get_instance_by_scheme($scheme) { } /** - * Create the download path to a file. + * Creates the web accessible URL to a stream. * - * @param $path A string containing the path of the file to generate URL for. - * @return A string containing a URL that can be used to download the file. - */ -function file_create_url($path) { - // Strip file_directory_path from $path. We only include relative paths in - // URLs. - $path = file_directory_strip($path); - switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) { - case FILE_DOWNLOADS_PUBLIC: - return $GLOBALS['base_url'] . '/' . file_directory_path() . '/' . str_replace('\\', '/', $path); - case FILE_DOWNLOADS_PRIVATE: - return url('system/files/' . $path, array('absolute' => TRUE)); - } -} - -/** - * Make sure the destination is a complete path and resides in the file system - * directory, if it is not prepend the file system directory. + * Compatibility: normal paths and stream wrappers. + * @see http://drupal.org/node/515192 * - * @param $destination - * A string containing the path to verify. If this value is omitted, Drupal's - * 'files' directory will be used. + * @param $uri + * The URI to for which we need an external URL. * @return - * A string containing the path to file, with file system directory appended - * if necessary, or FALSE if the path is invalid (i.e. outside the configured - * 'files' or temp directories). + * A string containing a URL that may be used to access the file. + * If the provided string already contains a preceding 'http', nothing is done + * and the same string is returned. If a valid stream wrapper could not be + * found to generate an external URL, then FALSE will be returned. */ -function file_create_path($destination = NULL) { - $file_path = file_directory_path(); - if (is_null($destination)) { - return $file_path; - } - // file_check_location() checks whether the destination is inside the Drupal - // files directory. - if (file_check_location($destination, $file_path)) { - return $destination; - } - // Check if the destination is instead inside the Drupal temporary files - // directory. - elseif (file_check_location($destination, file_directory_temp())) { - return $destination; - } - // Not found, try again with prefixed directory path. - elseif (file_check_location($file_path . '/' . $destination, $file_path)) { - return $file_path . '/' . $destination; - } - // File not found. - return FALSE; +function file_create_url($uri) { + $scheme = file_uri_scheme($uri); + + if (!$scheme) { + // If this is not a properly formatted stream return the URI with the base + // url prepended. + return $GLOBALS['base_url'] . '/' . $uri; + } + elseif ($scheme == 'http' || $scheme == 'https') { + // Check for http so that we don't have to implement getExternalUrl() for + // the http wrapper. + return $uri; + } + else { + // Attempt to return an external URL using the appropriate wrapper. + if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) { + return $wrapper->getExternalUrl(); + } + else { + return FALSE; + } + } + + // @todo Implement CDN integration hook stuff in this function. + // @see http://drupal.org/node/499156 } /** @@ -357,126 +331,90 @@ function file_create_path($destination = NULL) { * Directories need to have execute permissions to be considered a directory by * FTP servers, etc. * - * @param $directory - * A string containing the name of a directory path. - * @param $mode + * @param &$directory + * A string reference containing the name of a directory path or URI. A + * trailing slash will be trimmed from a path. + * @param $options * A bitmask to indicate if the directory should be created if it does * not exist (FILE_CREATE_DIRECTORY) or made writable if it is read-only * (FILE_MODIFY_PERMISSIONS). - * @param $form_item - * An optional string containing the name of a form item that any errors will - * be attached to. This is useful for settings forms that require the user to - * specify a writable directory. If it can't be made to work, a form error - * will be set preventing them from saving the settings. * @return - * FALSE when directory not found, or TRUE when directory exists. + * TRUE if the directory exists (or was created) and is writable. FALSE + * otherwise. */ -function file_check_directory(&$directory, $mode = 0, $form_item = NULL) { - $directory = rtrim($directory, '/\\'); +function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS) { + if (!file_stream_wrapper_valid_scheme(file_uri_scheme($directory))) { + // Only trim if we're not dealing with a stream. + $directory = rtrim($directory, '/\\'); + } // Check if directory exists. if (!is_dir($directory)) { // Let mkdir() recursively create directories and use the default directory // permissions. - if (($mode & FILE_CREATE_DIRECTORY) && @mkdir($directory, variable_get('file_chmod_directory', 0775), TRUE)) { - drupal_chmod($directory); - } - else { - if ($form_item) { - form_set_error($form_item, t('The directory %directory does not exist.', array('%directory' => $directory))); - watchdog('file system', 'The directory %directory does not exist.', array('%directory' => $directory), WATCHDOG_ERROR); - } - return FALSE; + if (($options & FILE_CREATE_DIRECTORY) && @drupal_mkdir($directory, NULL, TRUE)) { + return drupal_chmod($directory); } + return FALSE; } - - // Check to see if the directory is writable. - if (!is_writable($directory)) { - // If not able to modify permissions, or if able to, but chmod - // fails, return false. - if (!$mode || (($mode & FILE_MODIFY_PERMISSIONS) && !drupal_chmod($directory))) { - if ($form_item) { - form_set_error($form_item, t('The directory %directory is not writable', array('%directory' => $directory))); - watchdog('file system', 'The directory %directory is not writable, because it does not have the correct permissions set.', array('%directory' => $directory), WATCHDOG_ERROR); - } - return FALSE; - } + // The directory exists, so check to see if it is writable. + $writable = is_writable($directory); + if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) { + return drupal_chmod($directory); } - if ((file_directory_path() == $directory || file_directory_temp() == $directory) && !is_file("$directory/.htaccess")) { - $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks"; - if (file_put_contents("$directory/.htaccess", $htaccess_lines)) { - drupal_chmod("$directory/.htaccess"); - } - else { - $variables = array('%directory' => $directory, '!htaccess' => '
' . nl2br(check_plain($htaccess_lines))); - form_set_error($form_item, t("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: !htaccess", $variables)); - watchdog('security', "Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: !htaccess", $variables, WATCHDOG_ERROR); - } - } - - return TRUE; + return $writable; } /** - * Checks path to see if it is a directory, or a directory/file. - * - * @param $path - * A string containing a file path. This will be set to the directory's path. - * @return - * If the directory is not in a Drupal writable directory, FALSE is returned. - * Otherwise, the base name of the path is returned. + * If missing, create a .htaccess file in each Drupal files directory. */ -function file_check_path(&$path) { - // Check if path is a directory. - if (file_check_directory($path)) { - return ''; - } - - // Check if path is a possible dir/file. - $filename = basename($path); - $path = dirname($path); - if (file_check_directory($path)) { - return $filename; - } - - return FALSE; +function file_ensure_htaccess() { + file_create_htaccess('public://', FALSE); + file_create_htaccess('private://', TRUE); + file_create_htaccess('temporary://', TRUE); } /** - * Check if a file is really located inside $directory. - * - * This should be used to make sure a file specified is really located within - * the directory to prevent exploits. Note that the file or path being checked - * does not actually need to exist yet. + * Creates an .htaccess file in the given directory. * - * @code - * // Returns FALSE: - * file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files'); - * @endcode - * - * @param $source - * A string set to the file to check. * @param $directory - * A string where the file should be located. - * @return - * FALSE if the path does not exist in the directory; otherwise, the real - * path of the source. + * The directory. + * @param $private + * FALSE indicates that $directory should be an open and public directory. + * The default is TRUE which indicates a private and protected directory. */ -function file_check_location($source, $directory = '') { - $check = realpath($source); - if ($check) { - $source = $check; +function file_create_htaccess($directory, $private = TRUE) { + if (file_uri_scheme($directory)) { + $directory = file_stream_wrapper_uri_normalize($directory); } else { - // This file does not yet exist. - $source = realpath(dirname($source)) . '/' . basename($source); + $directory = rtrim($directory, '/\\'); } - $directory = realpath($directory); - if ($directory && strpos($source, $directory) !== 0) { - return FALSE; + $htaccess_path = $directory . '/.htaccess'; + + if (file_exists($htaccess_path)) { + // Short circuit if the .htaccess file already exists. + return; + } + + if ($private) { + // Private .htaccess file. + $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nDeny from all\nOptions None\nOptions +FollowSymLinks"; + } + else { + // Public .htaccess file. + $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks"; + } + + // Write the .htaccess file. + if (file_put_contents($htaccess_path, $htaccess_lines)) { + drupal_chmod($htaccess_path, 0444); + } + else { + $variables = array('%directory' => $directory, '!htaccess' => '
' . nl2br(check_plain($htaccess_lines))); + watchdog('security', "Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: !htaccess", $variables, WATCHDOG_ERROR); } - return $source; } /** @@ -498,8 +436,7 @@ function file_load_multiple($fids = array(), $conditions = array()) { if (!$fids && !$conditions) { return array(); } - - $query = db_select('files', 'f')->fields('f'); + $query = db_select('file', 'f')->fields('f'); // If the $fids array is populated, add those to the query. if ($fids) { @@ -558,15 +495,15 @@ function file_load($fid) { function file_save($file) { $file = (object)$file; $file->timestamp = REQUEST_TIME; - $file->filesize = filesize($file->filepath); + $file->filesize = filesize($file->uri); if (empty($file->fid)) { - drupal_write_record('files', $file); + drupal_write_record('file', $file); // Inform modules about the newly added file. module_invoke_all('file_insert', $file); } else { - drupal_write_record('files', $file, 'fid'); + drupal_write_record('file', $file, 'fid'); // Inform modules that the file has been updated. module_invoke_all('file_update', $file); } @@ -592,9 +529,9 @@ function file_save($file) { * @param $source * A file object. * @param $destination - * A string containing the destination that $source should be copied to. This - * can be a complete file path, a directory path or, if this value is omitted, - * Drupal's 'files' directory will be used. + * A string containing the destination that $source should be copied to. + * This should be a stream wrapper URI. If this value is omitted, Drupal's + * public files scheme will be used, "public://". * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with @@ -612,14 +549,14 @@ function file_save($file) { function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { $source = (object)$source; - if ($filepath = file_unmanaged_copy($source->filepath, $destination, $replace)) { + if ($uri = file_unmanaged_copy($source->uri, $destination, $replace)) { $file = clone $source; $file->fid = NULL; - $file->filepath = $filepath; - $file->filename = basename($filepath); + $file->uri = $uri; + $file->filename = basename($uri); // If we are replacing an existing file re-use its database record. if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + $existing_files = file_load_multiple(array(), array('uri' => $uri)); if (count($existing_files)) { $existing = reset($existing_files); $file->fid = $existing->fid; @@ -628,7 +565,7 @@ function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) } // If we are renaming around an existing file (rather than a directory), // use its basename for the filename. - else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) { $file->filename = basename($destination); } @@ -655,11 +592,10 @@ function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) * replace the file or rename the file based on the $replace parameter. * * @param $source - * A string specifying the file location of the original file. + * A string specifying the filepath or URI of the original file. * @param $destination - * A string containing the destination that $source should be copied to. This - * can be a complete file path, a directory path or, if this value is omitted, - * Drupal's 'files' directory will be used. + * A URI containing the destination that $source should be copied to. If + * NULL the default scheme will be used as the destination. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. @@ -675,38 +611,55 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST $original_source = $source; $original_destination = $destination; - $source = realpath($source); + // Assert that the source file actually exists. + $source = drupal_realpath($source); if (!file_exists($source)) { + // @todo Replace drupal_set_message() calls with exceptions instead. drupal_set_message(t('The specified file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error'); return FALSE; } - $proposed_destination = file_create_path($destination); - $directory = $proposed_destination; - $basename = file_check_path($directory); + // Build a destination URI if necessary. + if (!isset($destination)) { + $destination = file_build_uri(basename($source)); + } - // Make sure we at least have a valid directory. - if ($basename === FALSE) { - drupal_set_message(t('The specified file %file could not be copied, because the destination %directory is not properly configured. This is often caused by a problem with file or directory permissions.', array('%file' => $original_source, '%directory' => empty($original_destination) ? $proposed_destination : $original_destination)), 'error'); - return FALSE; + // Assert that the destination contains a valid stream. + $destination_scheme = file_uri_scheme($destination); + if (!$destination_scheme || !file_stream_wrapper_valid_scheme($destination_scheme)) { + drupal_set_message(t('The specified file %file could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_unmanaged_copy() or a missing stream wrapper.', array('%file' => $original_source, '%destination' => $destination)), 'error'); } - // If the destination file is not specified then use the filename of the - // source file. - $basename = $basename ? $basename : basename($source); - $destination = file_destination($directory . '/' . $basename, $replace); + // Prepare the destination directory. + if (file_prepare_directory($destination)) { + // The destination is already a directory, so append the source basename. + $destination = file_stream_wrapper_uri_normalize($destination . '/' . basename($source)); + } + else { + // Perhaps $destination is a dir/file? + $dirname = drupal_dirname($destination); + if (!file_prepare_directory($dirname)) { + // The destination is not valid. + drupal_set_message(t('The specified file %file could not be copied, because the destination %directory is not properly configured. This is often caused by a problem with file or directory permissions.', array('%file' => $original_source, '%directory' => $destination)), 'error'); + return FALSE; + } + } + // Determine whether we can perform this operation based on overwrite rules. + $destination = file_destination($destination, $replace); if ($destination === FALSE) { - drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $source, '%directory' => $proposed_destination)), 'error'); + drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $source, '%directory' => $destination)), 'error'); return FALSE; } - // Make sure source and destination filenames are not the same, makes no - // sense to copy it if they are. In fact copying the file will most likely - // result in a 0 byte file. Which is bad. Real bad. - if ($source == realpath($destination)) { + + // Assert that the source and destination filenames are not the same. + if (drupal_realpath($source) == drupal_realpath($destination)) { drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error'); return FALSE; } + // Make sure the .htaccess files are present. + file_ensure_htaccess(); + // Perform the copy operation. if (!@copy($source, $destination)) { drupal_set_message(t('The specified file %file could not be copied.', array('%file' => $source)), 'error'); return FALSE; @@ -718,12 +671,20 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST return $destination; } +/** + * Given a relative path, construct a URI into Drupal's default files location. + */ +function file_build_uri($path) { + $uri = variable_get('file_scheme_default', 'public') . '://' . $path; + return file_stream_wrapper_uri_normalize($uri); +} + /** * Determines the destination path for a file depending on how replacement of * existing files should be handled. * * @param $destination - * A string specifying the desired path. + * A string specifying the desired final URI or filepath. * @param $replace * Replace behavior when the destination file already exists. * - FILE_EXISTS_REPLACE - Replace the existing file. @@ -731,8 +692,8 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return - * The destination file path or FALSE if the file already exists and - * FILE_EXISTS_ERROR was specified. + * The destination filepath, or FALSE if the file already exists + * and FILE_EXISTS_ERROR is specified. */ function file_destination($destination, $replace) { if (file_exists($destination)) { @@ -743,7 +704,7 @@ function file_destination($destination, $replace) { case FILE_EXISTS_RENAME: $basename = basename($destination); - $directory = dirname($destination); + $directory = drupal_dirname($destination); $destination = file_create_filename($basename, $directory); break; @@ -770,7 +731,7 @@ function file_destination($destination, $replace) { * A file object. * @param $destination * A string containing the destination that $source should be moved to. This - * can be a complete file path, a directory path or, if this value is omitted, + * must be a URI matching a Drupal stream wrapper. If this value is omitted, * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: @@ -791,14 +752,14 @@ function file_destination($destination, $replace) { function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { $source = (object)$source; - if ($filepath = file_unmanaged_move($source->filepath, $destination, $replace)) { + if ($uri = file_unmanaged_move($source->uri, $destination, $replace)) { $delete_source = FALSE; $file = clone $source; - $file->filepath = $filepath; + $file->uri = $uri; // If we are replacing an existing file re-use its database record. if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + $existing_files = file_load_multiple(array(), array('uri' => $uri)); if (count($existing_files)) { $existing = reset($existing_files); $delete_source = TRUE; @@ -807,7 +768,7 @@ function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) } // If we are renaming around an existing file (rather than a directory), // use its basename for the filename. - else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) { $file->filename = basename($destination); } @@ -831,10 +792,10 @@ function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) * changes to the database. * * @param $source - * A string specifying the file location of the original file. + * A string specifying the filepath or URI of the original file. * @param $destination * A string containing the destination that $source should be moved to. This - * can be a complete file path, a directory name or, if this value is omitted, + * must be a URI matching a Drupal stream wrapper. If this value is omitted, * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: @@ -843,7 +804,7 @@ function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return - * The filepath of the moved file, or FALSE in the event of an error. + * The URI of the moved file, or FALSE in the event of an error. * * @see file_move() */ @@ -922,13 +883,21 @@ function file_unmunge_filename($filename) { * @param $basename * String filename * @param $directory - * String directory + * String containing the directory or parent URI. * @return * File path consisting of $directory and a unique filename based off * of $basename. */ function file_create_filename($basename, $directory) { - $destination = $directory . '/' . $basename; + // A URI or path may already have a trailing slash or look like "public://". + if (substr($directory, -1) == '/') { + $separator = ''; + } + else { + $separator = '/'; + } + + $destination = $directory . $separator . $basename; if (file_exists($destination)) { // Destination file already exists, generate an alternative. @@ -944,7 +913,7 @@ function file_create_filename($basename, $directory) { $counter = 0; do { - $destination = $directory . '/' . $name . '_' . $counter++ . $ext; + $destination = $directory . $separator . $name . '_' . $counter++ . $ext; } while (file_exists($destination)); } @@ -987,8 +956,8 @@ function file_delete($file, $force = FALSE) { // Make sure the file is deleted before removing its row from the // database, so UIs can still find the file in the database. - if (file_unmanaged_delete($file->filepath)) { - db_delete('files')->condition('fid', $file->fid)->execute(); + if (file_unmanaged_delete($file->uri)) { + db_delete('file')->condition('fid', $file->fid)->execute(); return TRUE; } return FALSE; @@ -1002,7 +971,7 @@ function file_delete($file, $force = FALSE) { * entry recorded in the files table. * * @param $path - * A string containing a file path. + * A string containing a filepath or URI. * @return * TRUE for success or path does not exist, or FALSE in the event of an * error. @@ -1043,7 +1012,7 @@ function file_unmanaged_delete($path) { * Note that this only deletes visible files with write permission. * * @param $path - * A string containing a file or directory path. + * A string containing a URI, filepath. or directory path. * @return * TRUE for success or path does not exist, or FALSE in the event of an * error. @@ -1079,7 +1048,7 @@ function file_unmanaged_delete_recursive($path) { * An integer containing the number of bytes used. */ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) { - $query = db_select('files', 'f'); + $query = db_select('file', 'f'); // Use separate placeholders for the status to avoid a bug in some versions // of PHP. @see http://drupal.org/node/352956 $query->where('f.status & :status1 = :status2', array(':status1' => $status, ':status2' => $status)); @@ -1093,18 +1062,18 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) { /** * Saves a file upload to a new location. * - * The file will be added to the files table as a temporary file. Temporary - * files are periodically cleaned. To make the file a permanent file call - * assign the status and use file_save() to save it. + * The file will be added to the {file} table as a temporary file. Temporary + * files are periodically cleaned. To make the file a permanent file, assign + * the status and use file_save() to save the changes. * * @param $source - * A string specifying the name of the upload field to save. + * A string specifying the filepath or URI of the upload field 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. * @param $destination - * A string containing the directory $source should be copied to. If this is - * not provided or is not writable, the temporary directory will be used. + * A string containing the URI $source should be copied to. Defaults to + * "temporary://". * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE: Replace the existing file. @@ -1170,25 +1139,31 @@ function file_save_upload($source, $validators = array(), $destination = FALSE, $file->uid = $user->uid; $file->status = 0; $file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions); - $file->filepath = $_FILES['files']['tmp_name'][$source]; + $file->uri = $_FILES['files']['tmp_name'][$source]; $file->filemime = file_get_mimetype($file->filename); $file->filesize = $_FILES['files']['size'][$source]; // Rename potentially executable files, to help prevent exploits. if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { $file->filemime = 'text/plain'; - $file->filepath .= '.txt'; + $file->uri .= '.txt'; $file->filename .= '.txt'; } - // If the destination is not provided, or is not writable, then use the - // temporary directory. - if (empty($destination) || file_check_path($destination) === FALSE) { - $destination = file_directory_temp(); + // If the destination is not provided, use the temporary directory. + if (empty($destination)) { + $destination = 'temporary://'; + } + + // Assert that the destination contains a valid stream. + $destination_scheme = file_uri_scheme($destination); + if (!$destination_scheme || !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; } $file->source = $source; - $file->destination = file_destination(file_create_path($destination . '/' . $file->filename), $replace); + $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) { @@ -1218,19 +1193,19 @@ function file_save_upload($source, $validators = array(), $destination = 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->filepath = $file->destination; - if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) { + $file->uri = $file->destination; + if (!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->filepath)); + 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->filepath); + drupal_chmod($file->uri); // If we are replacing an existing file re-use its database record. if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = file_load_multiple(array(), array('filepath' => $file->filepath)); + $existing_files = file_load_multiple(array(), array('uri' => $file->uri)); if (count($existing_files)) { $existing = reset($existing_files); $file->fid = $existing->fid; @@ -1295,8 +1270,8 @@ function file_validate_name_length($file) { if (empty($file->filename)) { $errors[] = t("The file's name is empty. Please give a name to the file."); } - if (strlen($file->filename) > 255) { - $errors[] = t("The file's name exceeds the 255 characters limit. Please rename the file and try again."); + if (strlen($file->filename) > 240) { + $errors[] = t("The file's name exceeds the 240 characters limit. Please rename the file and try again."); } return $errors; } @@ -1375,7 +1350,7 @@ function file_validate_size($file, $file_limit = 0, $user_limit = 0) { function file_validate_is_image($file) { $errors = array(); - $info = image_get_info($file->filepath); + $info = image_get_info($file->uri); if (!$info || empty($info['extension'])) { $errors[] = t('Only JPEG, PNG and GIF images are allowed.'); } @@ -1411,13 +1386,13 @@ function file_validate_image_resolution($file, $maximum_dimensions = 0, $minimum $errors = array(); // Check first that the file is an image. - if ($info = image_get_info($file->filepath)) { + if ($info = image_get_info($file->uri)) { if ($maximum_dimensions) { // Check that it is smaller than the given dimensions. list($width, $height) = explode('x', $maximum_dimensions); if ($info['width'] > $width || $info['height'] > $height) { // Try to resize the image to fit the dimensions. - if ($image = image_load($file->filepath)) { + if ($image = image_load($file->uri)) { image_scale($image, $width, $height); image_save($image); $file->filesize = $image->info['file_size']; @@ -1447,9 +1422,9 @@ function file_validate_image_resolution($file, $maximum_dimensions = 0, $minimum * @param $data * A string containing the contents of the file. * @param $destination - * A string containing the destination location. If no value is provided - * then a randomly name will be generated and the file saved in Drupal's - * files directory. + * A string containing the destination URI. If no value is provided then a + * randomly name will be generated and the file saved in Drupal's files + * directory. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with @@ -1466,18 +1441,18 @@ function file_validate_image_resolution($file, $maximum_dimensions = 0, $minimum function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) { global $user; - if ($filepath = file_unmanaged_save_data($data, $destination, $replace)) { + if ($uri = file_unmanaged_save_data($data, $destination, $replace)) { // Create a file object. $file = new stdClass(); $file->fid = NULL; - $file->filepath = $filepath; - $file->filename = basename($filepath); - $file->filemime = file_get_mimetype($file->filepath); + $file->uri = $uri; + $file->filename = basename($uri); + $file->filemime = file_get_mimetype($file->uri); $file->uid = $user->uid; $file->status |= FILE_STATUS_PERMANENT; // If we are replacing an existing file re-use its database record. if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + $existing_files = file_load_multiple(array(), array('uri' => $uri)); if (count($existing_files)) { $existing = reset($existing_files); $file->fid = $existing->fid; @@ -1486,7 +1461,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM } // If we are renaming around an existing file (rather than a directory), // use its basename for the filename. - else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) { $file->filename = basename($destination); } @@ -1500,7 +1475,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM * making any changes to the database. * * This function is identical to file_save_data() except the file will not be - * saved to the files table and none of the file_* hooks will be called. + * saved to the {file} table and none of the file_* hooks will be called. * * @param $data * A string containing the contents of the file. @@ -1521,7 +1496,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM */ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) { // Write the data to a temporary file. - $temp_name = tempnam(file_directory_temp(), 'file'); + $temp_name = drupal_tempnam('temporary://', 'file'); if (file_put_contents($temp_name, $data) === FALSE) { drupal_set_message(t('The file could not be created.'), 'error'); return FALSE; @@ -1535,12 +1510,12 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX * Transfer file using HTTP to client. Pipes a file through Drupal to the * client. * - * @param $source - * String specifying the file path to transfer. + * @param $uri + * String specifying the file URI to transfer. * @param $headers * An array of HTTP headers to send along with file. */ -function file_transfer($source, $headers) { +function file_transfer($uri, $headers) { if (ob_get_level()) { ob_end_clean(); } @@ -1549,11 +1524,9 @@ function file_transfer($source, $headers) { drupal_set_header($name, $value); } drupal_send_headers(); - - $source = file_create_path($source); - + $scheme = file_uri_scheme($uri); // Transfer file in 1024 byte chunks to save memory usage. - if ($fd = fopen($source, 'rb')) { + if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) { while (!feof($fd)) { print fread($fd, 1024); } @@ -1579,21 +1552,18 @@ function file_transfer($source, $headers) { function file_download() { // Merge remainder of arguments from GET['q'], into relative file path. $args = func_get_args(); - $filepath = implode('/', $args); + $scheme = array_shift($args); + $target = implode('/', $args); + $uri = $scheme . '://' . $target; - // Maintain compatibility with old ?file=paths saved in node bodies. - if (isset($_GET['file'])) { - $filepath = $_GET['file']; - } - - if (file_exists(file_create_path($filepath))) { + if (file_exists($uri)) { // Let other modules provide headers and controls access to the file. - $headers = module_invoke_all('file_download', $filepath); + $headers = module_invoke_all('file_download', $uri); if (in_array(-1, $headers)) { return drupal_access_denied(); } if (count($headers)) { - file_transfer($filepath, $headers); + file_transfer($uri, $headers); } } return drupal_not_found(); @@ -1608,7 +1578,7 @@ function file_download() { * from being scanned. * * @param $dir - * The base directory for the scan, without trailing slash. + * The base directory or URI for the scan, without trailing slash. * @param $mask * The preg_match() regular expression of the files to find. * @param $options @@ -1634,7 +1604,7 @@ function file_download() { * should not be passed. * @return * An associative array (keyed on the provided key) of objects with - * 'filepath', 'filename', and 'name' members corresponding to the + * 'uri', 'filename', and 'name' members corresponding to the * matching files. */ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { @@ -1643,32 +1613,33 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { 'nomask' => '/(\.\.?|CVS)$/', 'callback' => 0, 'recurse' => TRUE, - 'key' => 'filepath', + 'key' => 'uri', 'min_depth' => 0, ); - $options['key'] = in_array($options['key'], array('filepath', 'filename', 'name')) ? $options['key'] : 'filepath'; + $options['key'] = in_array($options['key'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri'; $files = array(); if (is_dir($dir) && $handle = opendir($dir)) { while (FALSE !== ($filename = readdir($handle))) { if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') { - $filepath = "$dir/$filename"; - if (is_dir($filepath) && $options['recurse']) { + $uri = "$dir/$filename"; + $uri = file_stream_wrapper_uri_normalize($uri); + if (is_dir($uri) && $options['recurse']) { // Give priority to files in this folder by merging them in after any subdirectory files. - $files = array_merge(file_scan_directory($filepath, $mask, $options, $depth + 1), $files); + $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files); } elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) { // Always use this match over anything already set in $files with the // same $$options['key']. $file = (object) array( - 'filepath' => $filepath, + 'uri' => $uri, 'filename' => $filename, 'name' => pathinfo($filename, PATHINFO_FILENAME), ); $key = $options['key']; $files[$file->$key] = $file; if ($options['callback']) { - $options['callback']($filepath); + $options['callback']($uri); } } } @@ -1681,70 +1652,29 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { } /** - * Determine the default temporary directory. + * Determines the local directory path of a given wrapper. * - * @return - * A string containing a temp directory. - */ -function file_directory_temp() { - $temporary_directory = variable_get('file_directory_temp'); - - if (is_null($temporary_directory)) { - $directories = array(); - - // Has PHP been set with an upload_tmp_dir? - if (ini_get('upload_tmp_dir')) { - $directories[] = ini_get('upload_tmp_dir'); - } - - // Operating system specific dirs. - if (substr(PHP_OS, 0, 3) == 'WIN') { - $directories[] = 'c:/windows/temp'; - $directories[] = 'c:/winnt/temp'; - } - else { - $directories[] = '/tmp'; - } - - foreach ($directories as $directory) { - if (!$temporary_directory && is_dir($directory)) { - $temporary_directory = $directory; - } - } - - // if a directory has been found, use it, otherwise default to 'files/tmp' - $temporary_directory = $temporary_directory ? $temporary_directory : file_directory_path() . '/tmp'; - variable_set('file_directory_temp', $temporary_directory); - } - - return $temporary_directory; -} - -/** - * Determine the default 'files' directory. + * This function will return the directory path of a stream wrapper. A stream + * is referenced as: "scheme://target". For example, a scheme of "public" + * might return "sites/default/files" or "temporary" might return "/tmp". * + * @param $scheme + * A string representing the scheme of a stream. The default wrapper is + * is assumed if this is not provided. * @return - * A string containing the path to Drupal's 'files' directory. - */ -function file_directory_path() { - return variable_get('file_directory_path', conf_path() . '/files'); -} - -/** - * Remove a possible leading file directory path from the given path. - * - * @param $path - * Path to a file that may be in Drupal's files directory. - * @return - * String with Drupal's files directory removed from it. + * A string containing the directory path of a stream. FALSE is returned if + * the scheme is invalid or a wrapper could not be instantiated. */ -function file_directory_strip($path) { - // Strip file_directory_path from $path. We only include relative paths in - // URLs. - if (strpos($path, file_directory_path() . '/') === 0) { - $path = trim(substr($path, strlen(file_directory_path())), '\\/'); +function file_directory_path($scheme = NULL) { + if (empty($scheme)) { + $scheme = variable_get('file_default_scheme', 'public'); + } + if ($wrapper = file_stream_wrapper_get_instance_by_scheme($scheme)) { + return $wrapper->getDirectoryPath(); + } + else { + return FALSE; } - return $path; } /** @@ -1768,8 +1698,8 @@ function file_upload_max_size() { /** * Determine an Internet Media Type, or MIME type from a filename. * - * @param $filename - * Name of the file, including extension. + * @param $uri + * A string containing the URI, path, or filename. * @param $mapping * An optional map of extensions to their mimetypes, in the form: * - 'mimetypes': a list of mimetypes, keyed by an identifier, @@ -1783,35 +1713,15 @@ function file_upload_max_size() { * @see * file_default_mimetype_mapping() */ -function file_get_mimetype($filename, $mapping = NULL) { - if (!isset($mapping)) { - $mapping = variable_get('mime_extension_mapping', NULL); - if (!isset($mapping) && drupal_function_exists('file_default_mimetype_mapping')) { - // The default file map, defined in file.mimetypes.inc is quite big. - // We only load it when necessary. - $mapping = file_default_mimetype_mapping(); - } +function file_get_mimetype($uri, $mapping = NULL) { + if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) { + return $wrapper->getMimeType($uri, $mapping); } - - $extension = ''; - $file_parts = explode('.', $filename); - - // Remove the first part: a full filename should not match an extension. - array_shift($file_parts); - - // Iterate over the file parts, trying to find a match. - // For my.awesome.image.jpeg, we try: - // - jpeg - // - image.jpeg, and - // - awesome.image.jpeg - while ($additional_part = array_pop($file_parts)) { - $extension = strtolower($additional_part . ($extension ? '.' . $extension : '')); - if (isset($mapping['extensions'][$extension])) { - return $mapping['mimetypes'][$mapping['extensions'][$extension]]; - } + else { + // getMimeType() is not implementation specific, so we can directly + // call it without an instance. + return DrupalLocalStreamWrapper::getMimeType($uri, $mapping); } - - return 'application/octet-stream'; } /** @@ -1824,17 +1734,21 @@ function file_get_mimetype($filename, $mapping = NULL) { * these files, and give group write permissions so webserver group members * (e.g. a vhost account) can alter files uploaded and owned by the webserver. * - * @param $path - * String containing the path to a file or directory. + * PHP's chmod does not support stream wrappers so we use our wrapper implementation + * which interfaces with chmod() by default. Contrib wrappers may override this + * bahavior in their implementations as needed. + * + * @param $uri + * A string containing a URI file, or directory path. * @param $mode * Integer value for the permissions. Consult PHP chmod() documentation for * more information. * @return * TRUE for success, FALSE in the event of an error. */ -function drupal_chmod($path, $mode = NULL) { +function drupal_chmod($uri, $mode = NULL) { if (!isset($mode)) { - if (is_dir($path)) { + if (is_dir($uri)) { $mode = variable_get('file_chmod_directory', 0775); } else { @@ -1842,14 +1756,168 @@ function drupal_chmod($path, $mode = NULL) { } } - if (@chmod($path, $mode)) { - return TRUE; + // If this URI is a stream, pass it off to the appropriate stream wrapper. + // Otherwise, attempt PHP's chmod. This allows use of drupal_chmod even + // for unmanaged files outside of the stream wrapper interface. + if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) { + if ($wrapper->chmod($mode)) { + return TRUE; + } + } + else { + if (@chmod($uri, $mode)) { + return TRUE; + } } - watchdog('file', 'The file permissions could not be set on %path.', array('%path' => $path), WATCHDOG_ERROR); + watchdog('file', 'The file permissions could not be set on %uri.', array('%uri' => $uri), WATCHDOG_ERROR); return FALSE; } +/** + * Returns the absolute path of a file or directory + * + * PHP's realpath() does not properly support streams, so this function + * fills that gap. If a stream wrapped URI is provided, it will be passed + * to the registered wrapper for handling. If the URI does not contain a + * scheme or the wrapper implementation does not implement realpath, then + * FALSE will be returned. + * + * @see http://php.net/manual/en/function.realpath.php + * + * Compatibility: normal paths and stream wrappers. + * @see http://drupal.org/node/515192 + * + * @param $uri + * A string containing the URI to verify. If this value is omitted, + * Drupal's public files directory will be used [public://]. + * @return + * The absolute pathname, or FALSE on failure. + * + * @see realpath() + */ +function drupal_realpath($uri) { + // If this URI is a stream, pass it off to the appropriate stream wrapper. + // Otherwise, attempt PHP's realpath. This allows use of drupal_realpath even + // for unmanaged files outside of the stream wrapper interface. + if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) { + return $wrapper->realpath(); + } + else { + return realpath($uri); + } +} + +/** + * Gets the name of the directory from a given path. + * + * PHP's dirname() does not properly pass streams, so this function fills + * that gap. It is backwards compatible with normal paths and will use + * PHP's dirname() as a fallback. + * + * Compatibility: normal paths and stream wrappers. + * @see http://drupal.org/node/515192 + * + * @param $uri + * A URI or path. + * @return + * A string containing the directory name. + * + * @see dirname() + */ +function drupal_dirname($uri) { + $scheme = file_uri_scheme($uri); + + if ($scheme && file_stream_wrapper_valid_scheme($scheme)) { + $target = file_uri_target($uri); + $dirname = dirname($target); + + if ($dirname == '.') { + $dirname = ''; + } + + return $scheme . '://' . $dirname; + } + else { + return dirname($uri); + } +} + +/** + * Creates a directory using Drupal's default mode. + * + * PHP's mkdir() does not respect Drupal's default permissions mode. If a mode + * is not provided, this function will make sure that Drupal's is used. + * + * Compatibility: normal paths and stream wrappers. + * @see http://drupal.org/node/515192 + * + * @param $uri + * A URI or pathname. + * @param $mode + * By default the Drupal mode is used. + * @param $recursive + * Default to FALSE. + * @param $context + * Refer to http://php.net/manual/en/ref.stream.php + * @return + * Boolean TRUE on success, or FALSE on failure. + * + * @see mkdir() + */ +function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) { + + if (is_null($mode)) { + $mode = variable_get('file_chmod_directory', 0775); + } + + if (is_null($context)) { + return mkdir($uri, $mode, $recursive); + } + else { + return mkdir($uri, $mode, $recursive, $context); + } +} + +/** + * Creates a file with a unique filename in the specified directory. + * + * PHP's tempnam() does not return a URI like we want. This function + * will return a URI if given a URI, or it will return a filepath if + * given a filepath. + * + * Compatibility: normal paths and stream wrappers. + * @see http://drupal.org/node/515192 + * + * @param $directory + * The directory where the temporary filename will be created. + * @param $prefix + * The prefix of the generated temporary filename. + * Note: Windows uses only the first three characters of prefix. + * @return + * The new temporary fillename, or FALSE on failure. + * + * @see tempnam() + */ +function drupal_tempnam($directory, $prefix) { + $scheme = file_uri_scheme($directory); + + if ($scheme && file_stream_wrapper_valid_scheme($scheme)) { + $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme); + + if ($filename = tempnam($wrapper->getDirectoryPath(), $prefix)) { + return $scheme . '://' . basename($filename); + } + else { + return FALSE; + } + } + else { + // Handle as a normal tempnam() call. + return tempnam($directory, $prefix); + } +} + /** * @} End of "defgroup file". */ diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc index 66e0f5787bbca46dcde8b386bac6e97ca6270436..2b1c18c7e98599b6c9b93b244917f4cf15bbc63d 100644 --- a/includes/filetransfer/filetransfer.inc +++ b/includes/filetransfer/filetransfer.inc @@ -114,7 +114,7 @@ public final function removeFile($destination) { * A path to check against the jail. */ protected final function checkPath($path) { - if (realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) { + if (drupal_realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) { throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $path, '@jail' => $this->jail)); } } diff --git a/includes/filetransfer/ftp.inc b/includes/filetransfer/ftp.inc index ecb164a17b986607626e02014a2127c0d44385ff..008a20c557b2df780181f71fd878ef6202159856 100644 --- a/includes/filetransfer/ftp.inc +++ b/includes/filetransfer/ftp.inc @@ -19,7 +19,7 @@ static function factory($jail, $settings) { } function createDirectoryJailed($directory) { - if (!@mkdir($directory)) { + if (!@drupal_mkdir($directory)) { $exception = new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory)); throw $exception; } diff --git a/includes/form.inc b/includes/form.inc index d6552b04329c05c3573cd9caeb2662516870bb2d..8fffb0f6f078a8bf8da5dd6c01084b926ae72b33 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -2325,7 +2325,7 @@ function theme_image_button($element) { (!empty($element['#value']) ? ('value="' . check_plain($element['#value']) . '" ') : '') . 'id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . - ' src="' . base_path() . $element['#src'] . '" ' . + ' src="' . file_create_url($element['#src']) . '" ' . (!empty($element['#title']) ? 'alt="' . check_plain($element['#title']) . '" title="' . check_plain($element['#title']) . '" ' : '' ) . "/>\n"; } diff --git a/includes/install.inc b/includes/install.inc index db2838aae4f086e5bbb4423922df6ec8362aaf90..5626196b06688ef8dc0a7126d7e803ddd3950ed5 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -216,9 +216,9 @@ function drupal_detect_database_types() { // file for the driver explicitly. require_once DRUPAL_ROOT . '/includes/database/database.inc'; foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) { - include_once "{$file->filepath}/install.inc"; - include_once "{$file->filepath}/database.inc"; - $drivers[$file->filename] = $file->filepath; + include_once "{$file->uri}/install.inc"; + include_once "{$file->uri}/database.inc"; + $drivers[$file->filename] = $file->uri; } foreach ($drivers as $driver => $file) { @@ -810,7 +810,7 @@ function drupal_install_mkdir($file, $mask, $message = TRUE) { } } - if (@mkdir($file, intval("0$mod", 8))) { + if (@drupal_mkdir($file, intval("0$mod", 8))) { return TRUE; } else { @@ -934,7 +934,7 @@ function st($string, $args = array()) { $filename = 'profiles/' . $install_state['parameters']['profile'] . '/translations/' . $install_state['parameters']['locale'] . '.po'; if (file_exists(DRUPAL_ROOT . '/' . $filename)) { require_once DRUPAL_ROOT . '/includes/locale.inc'; - $file = (object) array('filepath' => $filename); + $file = (object) array('uri' => $filename); _locale_import_read_po('mem-store', $file); $locale_strings = _locale_import_one_string('mem-report'); } @@ -986,7 +986,7 @@ function drupal_check_profile($profile) { // Collect requirement testing results $requirements = array(); foreach ($installs as $install) { - require_once DRUPAL_ROOT . '/' . $install->filepath; + require_once DRUPAL_ROOT . '/' . $install->uri; $function = $install->name . '_requirements'; if (function_exists($function)) { $requirements = array_merge($requirements, $function('install')); @@ -1026,7 +1026,7 @@ function drupal_check_module($module) { // Include install file $install = drupal_get_install_files(array($module)); if (isset($install[$module])) { - require_once DRUPAL_ROOT . '/' . $install[$module]->filepath; + require_once DRUPAL_ROOT . '/' . $install[$module]->uri; // Check requirements $requirements = module_invoke($module, 'requirements', 'install'); diff --git a/includes/locale.inc b/includes/locale.inc index 0e42ca358717100619c616a69356cc5db9f71484..29d759e533ebf2dbb424f8d644d55cbfba6a3041 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -1223,7 +1223,7 @@ function _locale_import_po($file, $langcode, $mode, $group = NULL) { */ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group = 'default') { - $fd = fopen($file->filepath, "rb"); // File will get closed by PHP on return + $fd = fopen($file->uri, "rb"); // File will get closed by PHP on return if (!$fd) { _locale_import_message('The translation import failed, because the file %filename could not be read.', $file); return FALSE; @@ -1439,7 +1439,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL $header = _locale_import_parse_header($value['msgstr']); // Get the plural formula and update in database. - if (isset($header["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($header["Plural-Forms"], $file->filepath)) { + if (isset($header["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($header["Plural-Forms"], $file->uri)) { list($nplurals, $plural) = $p; db_update('languages') ->fields(array( @@ -2435,11 +2435,11 @@ function _locale_rebuild_js($langcode = NULL) { // Construct the filepath where JS translation files are stored. // There is (on purpose) no front end to edit that variable. - $dir = file_create_path(variable_get('locale_js_directory', 'languages')); + $dir = 'public://' . variable_get('locale_js_directory', 'languages'); // Delete old file, if we have no translations anymore, or a different file to be saved. if (!empty($language->javascript) && (!$data || $language->javascript != $data_hash)) { - file_unmanaged_delete(file_create_path($dir . '/' . $language->language . '_' . $language->javascript . '.js')); + file_unmanaged_delete($dir . '/' . $language->language . '_' . $language->javascript . '.js'); $language->javascript = ''; $status = 'deleted'; } @@ -2447,7 +2447,7 @@ function _locale_rebuild_js($langcode = NULL) { // Only create a new file if the content has changed. if ($data && $language->javascript != $data_hash) { // Ensure that the directory exists and is writable, if possible. - file_check_directory($dir, TRUE); + file_prepare_directory($dir, FILE_CREATE_DIRECTORY); // Save the file. $dest = $dir . '/' . $language->language . '_' . $data_hash . '.js'; @@ -2650,7 +2650,7 @@ function _locale_batch_build($files, $finished = NULL, $components = array()) { $operations = array(); foreach ($files as $file) { // We call _locale_batch_import for every batch operation. - $operations[] = array('_locale_batch_import', array($file->filepath)); + $operations[] = array('_locale_batch_import', array($file->uri)); } $batch = array( 'operations' => $operations, @@ -2682,7 +2682,7 @@ function _locale_batch_import($filepath, &$context) { // The filename is either {langcode}.po or {prefix}.{langcode}.po, so // we can extract the language code to use for the import from the end. if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) { - $file = (object) array('filename' => basename($filepath), 'filepath' => $filepath); + $file = (object) array('filename' => basename($filepath), 'uri' => $filepath); _locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]); $context['results'][] = $filepath; } diff --git a/includes/module.inc b/includes/module.inc index 6cd246d7bffcedb2e51d7ba40d37944b94911ed3..e741e310f2023204cc42f260e7c84b421e678d74 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -503,7 +503,7 @@ function drupal_required_modules() { $files = drupal_system_listing('/\.info$/', 'modules', 'name', 0); $required = array(); foreach ($files as $name => $file) { - $info = drupal_parse_info_file($file->filepath); + $info = drupal_parse_info_file($file->uri); if (!empty($info) && !empty($info['required']) && $info['required']) { $required[] = $name; } diff --git a/includes/registry.inc b/includes/registry.inc index 450af6c14c0c73571822a469f3772891236ad292..117de98818061499041bc577d2804217ba2f7503 100644 --- a/includes/registry.inc +++ b/includes/registry.inc @@ -42,7 +42,7 @@ function _registry_rebuild() { // Get the list of files we are going to parse. $files = array(); foreach ($modules as &$module) { - $dir = dirname($module->filepath); + $dir = dirname($module->uri); // Store the module directory for use in hook_registry_files_alter(). $module->dir = $dir; diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc index 4648536dcc6260a3485161ee28bfb629774b6233..6841325cc0c09016cb02731a8caf527bf9d6029d 100644 --- a/includes/stream_wrappers.inc +++ b/includes/stream_wrappers.inc @@ -226,14 +226,14 @@ static function getMimeType($uri, $mapping = NULL) { * Base implementation of chmod(). */ function chmod($mode) { - return @chmod($this->realpath(), $mode); + return @chmod($this->getLocalPath(), $mode); } /** - * Base implementaiton of realpath(). + * Base implementation of realpath(). */ function realpath() { - return @realpath($this->getDirectoryPath() . '/' . file_uri_target($this->uri)); + return $this->getLocalPath(); } /** @@ -246,14 +246,24 @@ protected function getLocalPath($uri = NULL) { if (!isset($uri)) { $uri = $this->uri; } - return $this->getDirectoryPath() . '/' . file_uri_target($uri); + $path = $this->getDirectoryPath() . '/' . file_uri_target($uri); + $realpath = realpath($path); + if (!$realpath) { + // This file does not yet exist. + $realpath = realpath(dirname($path)) . '/' . basename($path); + } + $directory = realpath($this->getDirectoryPath()); + if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) { + return FALSE; + } + return $realpath; } /** * Support for fopen(), file_get_contents(), file_put_contents() etc. * * @param $uri - * A string containing the path to the file to open. + * A string containing the URI to the file to open. * @param $mode * The file mode ("r", "wb" etc.). * @param $options @@ -428,7 +438,7 @@ public function rename($from_uri, $to_uri) { * Support for mkdir(). * * @param $uri - * A string containing the url to the directory to create. + * A string containing the URI to the directory to create. * @param $mode * Permission flags - see mkdir(). * @param $options @@ -440,11 +450,19 @@ public function rename($from_uri, $to_uri) { public function mkdir($uri, $mode, $options) { $this->uri = $uri; $recursive = (bool)($options & STREAM_MKDIR_RECURSIVE); + if ($recursive) { + // $this->getLocalPath() fails if $uri has multiple levels of directories + // that do not yet exist. + $localpath = $this->getDirectoryPath() . '/' . file_uri_target($uri); + } + else { + $localpath = $this->getLocalPath($uri); + } if ($options & STREAM_REPORT_ERRORS) { - return mkdir($this->getLocalPath(), $mode, $recursive); + return mkdir($localpath, $mode, $recursive); } else { - return @mkdir($this->getLocalPath(), $mode, $recursive); + return @mkdir($localpath, $mode, $recursive); } } @@ -452,7 +470,7 @@ public function mkdir($uri, $mode, $options) { * Support for rmdir(). * * @param $uri - * A string containing the url to the directory to delete. + * A string containing the URI to the directory to delete. * @param $options * A bit mask of STREAM_REPORT_ERRORS. * @return @@ -473,7 +491,7 @@ public function rmdir($uri, $options) { * Support for stat(). * * @param $uri - * A string containing the url to get information about. + * A string containing the URI to get information about. * @param $flags * A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET. * @return @@ -495,7 +513,7 @@ public function url_stat($uri, $flags) { * Support for opendir(). * * @param $uri - * A string containing the url to the directory to open. + * A string containing the URI to the directory to open. * @param $options * Unknown (parameter is not documented in PHP Manual). * @return @@ -554,7 +572,7 @@ class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper { * Implements abstract public function getDirectoryPath() */ public function getDirectoryPath() { - return variable_get('file_public_path', 'sites/default/files'); + return variable_get('file_public_path', conf_path() . '/files'); } /** @@ -582,7 +600,7 @@ class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper { * Implements abstract public function getDirectoryPath() */ public function getDirectoryPath() { - return variable_get('file_private_path', 'sites/default/files-private'); + return variable_get('file_private_path', conf_path() . '/private/files'); } /** @@ -609,13 +627,14 @@ class DrupalTemporaryStreamWrapper extends DrupalLocalStreamWrapper { * Implements abstract public function getDirectoryPath() */ public function getDirectoryPath() { - return variable_get('file_temporary_path', '/tmp'); + return variable_get('file_temporary_path', conf_path() . '/private/temp'); } /** * Overrides getExternalUrl(). */ public function getExternalUrl() { - return ''; + $path = str_replace('\\', '/', file_uri_target($this->uri)); + return url('system/temporary/' . $path, array('absolute' => TRUE)); } } diff --git a/includes/theme.inc b/includes/theme.inc index 0688e40d69c067d79474baf12ebe2a1ed6fd7f86..ff5f2a9913bcf41ec03034dcd8d644cf72567612 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -967,7 +967,7 @@ function drupal_find_theme_templates($cache, $extension, $path) { $files = drupal_system_listing($regex, $path, 'name', 0); foreach ($files as $template => $file) { // Ignore sub-theme templates for the current theme. - if (strpos($file->filepath, str_replace($subtheme_paths, '', $file->filepath)) !== 0) { + if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) { continue; } // Chop off the remaining extensions if there are any. $template already @@ -982,7 +982,7 @@ function drupal_find_theme_templates($cache, $extension, $path) { if (isset($cache[$hook])) { $templates[$hook] = array( 'template' => $template, - 'path' => dirname($file->filepath), + 'path' => dirname($file->uri), ); } // Ensure that the pattern is maintained from base themes to its sub-themes. @@ -1008,7 +1008,7 @@ function drupal_find_theme_templates($cache, $extension, $path) { // Put the underscores back in for the hook name and register this pattern. $templates[strtr($file, '-', '_')] = array( 'template' => $file, - 'path' => dirname($files[$match]->filepath), + 'path' => dirname($files[$match]->uri), 'arguments' => $info['arguments'], ); } @@ -1099,24 +1099,24 @@ function theme_get_setting($setting_name, $refresh = FALSE) { if ($settings['toggle_logo']) { if ($settings['default_logo']) { - $settings['logo'] = base_path() . dirname($theme_object->filename) . '/logo.png'; + $settings['logo'] = file_create_url(dirname($theme_object->filename) . '/logo.png'); } elseif ($settings['logo_path']) { - $settings['logo'] = base_path() . $settings['logo_path']; + $settings['logo'] = file_create_url($settings['logo_path']); } } if ($settings['toggle_favicon']) { if ($settings['default_favicon']) { if (file_exists($favicon = dirname($theme_object->filename) . '/favicon.ico')) { - $settings['favicon'] = base_path() . $favicon; + $settings['favicon'] = file_create_url($favicon); } else { - $settings['favicon'] = base_path() . 'misc/favicon.ico'; + $settings['favicon'] = file_create_url('misc/favicon.ico'); } } elseif ($settings['favicon_path']) { - $settings['favicon'] = base_path() . $settings['favicon_path']; + $settings['favicon'] = file_create_url($settings['favicon_path']); } else { $settings['toggle_favicon'] = FALSE; @@ -1340,7 +1340,7 @@ function theme_links($links, $attributes = array('class' => 'links')) { function theme_image($path, $alt = '', $title = '', $attributes = array(), $getsize = TRUE) { if (!$getsize || (is_file($path) && (list($width, $height, $type, $image_attributes) = @getimagesize($path)))) { $attributes = drupal_attributes($attributes); - $url = (url($path) == $path) ? $path : (base_path() . $path); + $url = file_create_url($path); return '' . check_plain($alt) . ''; } } diff --git a/install.php b/install.php index 3f89b66615ca86a11e178048a90224322b331efa..d54ab09fe4bbe093e4b32664db5041db73092e96 100644 --- a/install.php +++ b/install.php @@ -1039,6 +1039,8 @@ function _install_select_profile($profiles) { // Don't need to choose profile if only one available. if (sizeof($profiles) == 1) { $profile = array_pop($profiles); + // TODO: is this right? + require_once $profile->uri; return $profile->name; } else { @@ -1063,7 +1065,8 @@ function install_select_profile_form(&$form_state, $profile_files) { $names = array(); foreach ($profile_files as $profile) { - include_once DRUPAL_ROOT . '/' . $profile->filepath; + // TODO: is this right? + include_once DRUPAL_ROOT . '/' . $profile->uri; $details = install_profile_info($profile->name); $profiles[$profile->name] = $details; diff --git a/modules/aggregator/aggregator.admin.inc b/modules/aggregator/aggregator.admin.inc index bc2012c1d152b10613629aa4916ccc9aee5e9ee3..2c971fd146331095037afa3945ede4b9cb5d8aa1 100644 --- a/modules/aggregator/aggregator.admin.inc +++ b/modules/aggregator/aggregator.admin.inc @@ -297,7 +297,7 @@ function aggregator_form_opml_validate($form, &$form_state) { function aggregator_form_opml_submit($form, &$form_state) { $data = ''; if ($file = file_save_upload('upload')) { - $data = file_get_contents($file->filepath); + $data = file_get_contents($file->uri); } else { $response = drupal_http_request($form_state['values']['remote']); diff --git a/modules/aggregator/aggregator.test b/modules/aggregator/aggregator.test index 2c7f9b41dd72773b7b34ab51d195e7d8c04079ae..726fbdc495a716b16495fbc390ad0de9081e4445 100644 --- a/modules/aggregator/aggregator.test +++ b/modules/aggregator/aggregator.test @@ -200,7 +200,7 @@ class AggregatorTestCase extends DrupalWebTestCase { EOF; - $path = file_directory_path() . '/valid-opml.xml'; + $path = 'public://valid-opml.xml'; return file_unmanaged_save_data($opml, $path); } @@ -217,7 +217,7 @@ EOF; EOF; - $path = file_directory_path() . '/invalid-opml.xml'; + $path = 'public://invalid-opml.xml'; return file_unmanaged_save_data($opml, $path); } @@ -239,7 +239,7 @@ EOF; EOF; - $path = file_directory_path() . '/empty-opml.xml'; + $path = 'public://empty-opml.xml'; return file_unmanaged_save_data($opml, $path); } diff --git a/modules/blogapi/blogapi.module b/modules/blogapi/blogapi.module index 17a0071f560f330d0153e3e38f26cdab1f34f1a5..5a6ab9efe5fc5e8ab3231d3d6308196ff35590ec 100644 --- a/modules/blogapi/blogapi.module +++ b/modules/blogapi/blogapi.module @@ -460,19 +460,23 @@ function blogapi_metaweblog_new_media_object($blogid, $username, $password, $fil $filename .= '.' . $final_extension; } + else { + $filename = $name; + } + $uri = file_build_uri($filename); $data = $file['bits']; if (!$data) { return blogapi_error(t('No file sent.')); } - if (!$file = file_unmanaged_save_data($data, $filename)) { + if (!$file = file_unmanaged_save_data($data, $uri)) { return blogapi_error(t('Error storing file.')); } $row = new stdClass(); $row->uid = $user->uid; - $row->filepath = $file; + $row->uri = $file; $row->filesize = $filesize; drupal_write_record('blogapi_files', $row); diff --git a/modules/blogapi/blogapi.test b/modules/blogapi/blogapi.test index 0ee8b4f330cf7ed06a7ba271043760a2947b5f64..9a57be643291cc37b999a5b1fd6544d5ede438a6 100644 --- a/modules/blogapi/blogapi.test +++ b/modules/blogapi/blogapi.test @@ -70,7 +70,7 @@ class BlogAPITestCase extends DrupalWebTestCase { // Upload file. $file = current($this->drupalGetTestFiles('text')); - $file_contents = file_get_contents($file->filepath); + $file_contents = file_get_contents($file->uri); $file = array(); $file['name'] = $this->randomName() . '.txt'; $file['type'] = 'text'; diff --git a/modules/color/color.module b/modules/color/color.module index 965a9fc8958f4b841a77dd8699152ca12f2a1083..3f278a39ce0eaa4ec5b1a07e04b20b0b32fe1ef3 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -7,7 +7,7 @@ function color_help($path, $arg) { switch ($path) { case 'admin/help#color': - $output = '

' . t('The color module allows a site administrator to quickly and easily change the color scheme of certain themes. Although not all themes support color module, both Garland (the default theme) and Minnelli were designed to take advantage of its features. By using color module with a compatible theme, you can easily change the color of links, backgrounds, text, and other theme elements. Color module requires that your file download method be set to public.', array('@url' => url('admin/settings/file-system'))) . '

'; + $output = '

' . t('The color module allows a site administrator to quickly and easily change the color scheme of certain themes. Although not all themes support color module, both Garland (the default theme) and Minnelli were designed to take advantage of its features. By using color module with a compatible theme, you can easily change the color of links, backgrounds, text, and other theme elements.', array('@url' => url('admin/settings/file-system'))) . '

'; $output .= '

' . t("It is important to remember that color module saves a modified copy of the theme's specified stylesheets in the files directory. This means that if you make any manual changes to your theme's stylesheet, you must save your color settings again, even if they haven't changed. This causes the color module generated version of the stylesheets in the files directory to be recreated using the new version of the original file.") . '

'; $output .= '

' . t('To change the color settings for a compatible theme, select the "configure" link for the theme on the themes administration page.', array('@themes' => url('admin/appearance'))) . '

'; $output .= '

' . t('For more information, see the online handbook entry for Color module.', array('@color' => 'http://drupal.org/handbook/modules/color/')) . '

'; @@ -32,22 +32,15 @@ function color_theme() { */ function color_form_system_theme_settings_alter(&$form, &$form_state) { if (color_get_info(arg(4)) && function_exists('gd_info')) { - if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) { - // Disables the color changer when the private download method is used. - // TODO: This should be solved in a different way. See issue #181003. - drupal_set_message(t('The color picker only works if the download method is set to public.', array('@url' => url('admin/settings/file-system'))), 'warning'); - } - else { - $form['color'] = array( - '#type' => 'fieldset', - '#title' => t('Color scheme'), - '#weight' => -1, - '#attributes' => array('id' => 'color_scheme_form'), - '#theme' => 'color_scheme_form', - ); - $form['color'] += color_scheme_form($form_state, arg(4)); - $form['#submit'][] = 'color_scheme_form_submit'; - } + $form['color'] = array( + '#type' => 'fieldset', + '#title' => t('Color scheme'), + '#weight' => -1, + '#attributes' => array('id' => 'color_scheme_form'), + '#theme' => 'color_scheme_form', + ); + $form['color'] += color_scheme_form($form_state, arg(4)); + $form['#submit'][] = 'color_scheme_form_submit'; } } @@ -300,7 +293,7 @@ function color_scheme_form_submit($form, &$form_state) { $paths['color'] = file_directory_path() . '/color'; $paths['target'] = $paths['color'] . '/' . $id; foreach ($paths as $path) { - file_check_directory($path, FILE_CREATE_DIRECTORY); + file_prepare_directory($path, FILE_CREATE_DIRECTORY); } $paths['target'] = $paths['target'] . '/'; $paths['id'] = $id; diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc index 97cc59db9b0d6414ae4a1074966b948df7dadf8c..1702997f751b25bc5e4d174ddb910ff11305006b 100644 --- a/modules/image/image.admin.inc +++ b/modules/image/image.admin.inc @@ -679,11 +679,10 @@ function theme_image_style_preview($style) { // Set up preview file information. $preview_file = image_style_path($style['name'], $original_path); - $preview_path = file_create_path($preview_file); - if (!file_exists($preview_path) && image_style_create_derivative($style, $original_path, $preview_file)) { - $preview_path = file_create_path($preview_file); + if (!file_exists($preview_file)) { + image_style_create_derivative($style, $original_path, $preview_file); } - $preview_image = image_get_info($preview_path); + $preview_image = image_get_info($preview_file); if ($preview_image['width'] > $preview_image['height']) { $preview_width = min($preview_image['width'], $sample_width); $preview_height = round($preview_width / $preview_image['width'] * $preview_image['height']); diff --git a/modules/image/image.install b/modules/image/image.install index b174b13ee5be03445b9c5499d7f4d97ba30d074b..b963605dc24402e23ddd21c5af78a9b0fefd441c 100644 --- a/modules/image/image.install +++ b/modules/image/image.install @@ -14,7 +14,7 @@ function image_install() { // Create the styles directory and ensure it's writable. $path = file_directory_path() . '/styles'; - file_check_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); } /** diff --git a/modules/image/image.module b/modules/image/image.module index 35c44e219a3f7c467599257c2b0eb3c0aabca733..10c6a39ec25cb28f6481a2cd618b81b8973a5222 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -200,7 +200,7 @@ function image_file_download($filepath) { $original_path = implode('/', $args); // Check that the file exists and is an image. - if ($info = image_get_info(file_create_path($filepath))) { + if ($info = image_get_info($filepath)) { // Check the permissions of the original to grant access to this image. $headers = module_invoke_all('file_download', $original_path); if (!in_array(-1, $headers)) { @@ -219,7 +219,7 @@ function image_file_download($filepath) { */ function image_file_move($file, $source) { // Delete any image derivatives at the original image path. - image_path_flush($file->filepath); + image_path_flush($file->uri); } /** @@ -227,7 +227,7 @@ function image_file_move($file, $source) { */ function image_file_delete($file) { // Delete any image derivatives of this image. - image_path_flush($file->filepath); + image_path_flush($file->uri); } /** @@ -237,10 +237,10 @@ function image_file_delete($file) { * The Drupal file path to the original image. */ function image_path_flush($path) { - $path = file_directory_strip($path); $styles = image_styles(); foreach ($styles as $style) { - if ($path = file_create_path('styles/' . $style['name'] . '/' . $path)) { + $path = image_style_path($style['name'], $path); + if (file_exists($path)) { file_unmanaged_delete($path); } } @@ -426,9 +426,10 @@ function image_style_generate() { $args = func_get_args(); $style = array_shift($args); $style_name = $style['name']; + $scheme = array_shift($args); $path = implode('/', $args); - $source = file_create_path($path); + $path = $scheme . '://' . $path; $path_md5 = md5($path); $destination = image_style_path($style['name'], $path); @@ -459,7 +460,7 @@ function image_style_generate() { cache_set($cid, $destination, 'cache_image'); // Try to generate the image. - if (image_style_create_derivative($style, $source, $destination)) { + if (image_style_create_derivative($style, $path, $destination)) { $image = image_load($destination); cache_clear_all($cid, 'cache_image'); file_transfer($image->source, array('Content-Type' => $image->info['mime_type'], 'Content-Length' => $image->info['file_size'])); @@ -481,17 +482,17 @@ function image_style_generate() { * @param $source * Path of the source file. * @param $destination - * Path of the destination file. + * Path or URI of the destination file. * @return * TRUE if an image derivative is generated, FALSE if no image derivative * is generated. NULL if the derivative is being generated. */ function image_style_create_derivative($style, $source, $destination) { // Get the folder for the final location of this style. - $directory = dirname($destination); + $directory = drupal_dirname($destination); // Build the destination folder tree if it doesn't already exist. - if (!file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { watchdog('image', 'Failed to create style directory: %directory', array('%directory' => $directory), WATCHDOG_ERROR); return FALSE; } @@ -521,7 +522,7 @@ function image_style_create_derivative($style, $source, $destination) { * An image style array. */ function image_style_flush($style) { - $style_directory = realpath(file_directory_path() . '/styles/' . $style['name']); + $style_directory = drupal_realpath(variable_get('file_default_scheme', 'public') . '://styles/' . $style['name']); if (is_dir($style_directory)) { file_unmanaged_delete_recursive($style_directory); } @@ -547,9 +548,10 @@ function image_style_flush($style) { * * This function is the default image generation method. It returns a URL for * an image that can be used in an tag. When the browser requests the - * image at image/generate/[style_name]/[path] the image is generated if it does - * not already exist and then served to the browser. This allows each image to - * have its own PHP instance (and memory limit) for generation of the new image. + * image at image/generate/[style_name]/[scheme]/[path] the image is generated + * if it does not already exist and then served to the browser. This allows + * each image to have its own PHP instance (and memory limit) for generation of + * the new image. * * @param $style_name * The name of the style to be used with this image. @@ -575,9 +577,12 @@ function image_style_url($style_name, $path) { // Set a cache entry to grant access to this style/image path. This will be // checked by image_style_generate(). cache_set('access:' . $style_name . ':' . md5($path), 1, 'cache_image', REQUEST_TIME + 600); + + $scheme = file_uri_scheme($path); + $target = file_uri_target($path); // Generate a callback path for the image. - $url = url('image/generate/' . $style_name . '/' . $path, array('absolute' => TRUE)); + $url = url('image/generate/' . $style_name . '/' . $scheme . '/' . $target, array('absolute' => TRUE)); return $url; } @@ -596,7 +601,10 @@ function image_style_url($style_name, $path) { * @see image_style_url() */ function image_style_path($style_name, $path) { - return file_directory_path() . '/styles/' . $style_name . '/' . file_directory_strip($path); + if ($target = file_uri_target($path)) { + $path = $target; + } + return variable_get('file_default_scheme', 'public') . '://styles/' . $style_name . '/' . $path; } /** @@ -795,7 +803,7 @@ function theme_image_style($style_name, $path, $alt = '', $title = '', $attribut if (!file_exists($style_path)) { $style_path = image_style_url($style_name, $path); } - return theme('image', $style_path, $alt, $title, $attributes, $getsize); + return theme('image', file_create_url($style_path), $alt, $title, $attributes, $getsize); } /** diff --git a/modules/image/image.test b/modules/image/image.test index 75aa4e5bddd9a56081ef023a6bcd32625f022e35..31e489cdf30a06f390972269a77b1e8cf51a7a83 100644 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -47,16 +47,17 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { parent::setUp(); $this->style_name = 'style_foo'; + $this->scheme = 'public'; image_style_save(array('name' => $this->style_name)); // Create the directories for the styles. - $status = file_check_directory($d = file_directory_path() . '/styles/' . $this->style_name, FILE_CREATE_DIRECTORY); + $status = file_prepare_directory($d = file_directory_path() . '/styles/' . $this->style_name, FILE_CREATE_DIRECTORY); $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images for the test style.' )); // Create a working copy of the file. $file = reset($this->drupalGetTestFiles('image')); - $this->image_info = image_get_info($file->filepath); - $this->image_filepath = file_unmanaged_copy($file->filepath, NULL, FILE_EXISTS_RENAME); + $this->image_info = image_get_info($file->uri); + $this->image_filepath = file_unmanaged_copy($file->uri, NULL, FILE_EXISTS_RENAME); $this->assertNotIdentical(FALSE, $this->image_filepath, t('Created the without generated image file.')); } @@ -65,7 +66,7 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { */ function testImageStylePath() { $actual = image_style_path($this->style_name, $this->image_filepath); - $expected = file_directory_path() . '/styles/' . $this->style_name . '/' . basename($this->image_filepath); + $expected = $this->scheme . '://styles/' . $this->style_name . '/' . basename($this->image_filepath); $this->assertEqual($actual, $expected, t('Got the path for a file.')); } @@ -75,9 +76,9 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { function testImageStyleUrl() { // Get the URL of a file that has not been generated yet and try to access // it before image_style_url has been called. - $generated_path = file_directory_path() . '/styles/' . $this->style_name . '/' . basename($this->image_filepath); + $generated_path = $this->scheme . '://styles/' . $this->style_name . '/' . basename($this->image_filepath); $this->assertFalse(file_exists($generated_path), t('Generated file does not exist.')); - $expected_generate_url = url('image/generate/' . $this->style_name . '/' . $this->image_filepath, array('absolute' => TRUE)); + $expected_generate_url = url('image/generate/' . $this->style_name . '/' . $this->scheme . '/' . basename($this->image_filepath), array('absolute' => TRUE)); $this->drupalGet($expected_generate_url); $this->assertResponse(403, t('Access to generate URL was denied.')); @@ -261,7 +262,7 @@ class ImageAdminStylesUnitTest extends DrupalWebTestCase { // file directory. Copy over an image on the first run. if (!isset($file_path)) { $file = reset($this->drupalGetTestFiles('image')); - $file_path = file_unmanaged_copy($file->filename); + $file_path = file_unmanaged_copy($file->uri); } return image_style_url($style['name'], $file_path) ? $file_path : FALSE; diff --git a/modules/locale/locale.install b/modules/locale/locale.install index 96b3b8c77088cdae60c977d73331cee04f8dd792..f08b75d442573972f06b05df1fa5811f8535da41 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -54,11 +54,11 @@ function locale_update_7000() { */ function locale_uninstall() { // Delete all JavaScript translation files. - $locale_js_directory = file_create_path(variable_get('locale_js_directory', 'languages')); + $locale_js_directory = 'public://' . variable_get('locale_js_directory', 'languages'); $files = db_query('SELECT language, javascript FROM {languages}'); foreach ($files as $file) { if (!empty($file->javascript)) { - file_unmanaged_delete(file_create_path($locale_js_directory . '/' . $file->language . '_' . $file->javascript . '.js')); + file_unmanaged_delete($locale_js_directory . '/' . $file->language . '_' . $file->javascript . '.js'); } } // Delete the JavaScript translations directory if empty. diff --git a/modules/locale/locale.module b/modules/locale/locale.module index fa134e2f9ee5de0401d698daa32a71ae11702e85..cb37d8cbf32e200307612cca594c625c41c9357a 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -532,7 +532,7 @@ function locale_system_update($components) { function locale_js_alter(&$javascript) { global $language; - $dir = file_create_path(variable_get('locale_js_directory', 'languages')); + $dir = 'public://' . variable_get('locale_js_directory', 'languages'); $parsed = variable_get('javascript_parsed', array()); $files = $new_files = FALSE; diff --git a/modules/locale/locale.test b/modules/locale/locale.test index db73cd98b190d529f9c21ef278c329c310cc4eda..03c4bcf337729a6c4fdd5cf231ffc37721b69f2c 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -696,7 +696,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { * Additional options to pass to the translation import form. */ function importPoFile($contents, array $options = array()) { - $name = tempnam(file_directory_temp(), "po_"); + $name = tempnam(file_directory_path('temporary'), "po_"); file_put_contents($name, $contents); $options['files[file]'] = $name; $this->drupalPost('admin/config/international/translate/import', $options, t('Import')); @@ -843,7 +843,7 @@ class LocaleExportFunctionalTest extends DrupalWebTestCase { function testExportTranslation() { // First import some known translations. // This will also automatically enable the 'fr' language. - $name = tempnam(file_directory_temp(), "po_"); + $name = tempnam(file_directory_path('temporary'), "po_"); file_put_contents($name, $this->getPoFile()); $this->drupalPost('admin/config/international/translate/import', array( 'langcode' => 'fr', @@ -953,7 +953,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/config/international/translate/edit/' . $string->lid, $edit, t('Save translations')); _locale_rebuild_js('fr'); $file = db_query('SELECT javascript FROM {languages} WHERE language = :language', array(':language' => 'fr'))->fetchObject(); - $js_file = file_create_path(variable_get('locale_js_directory', 'languages')) . '/fr_' . $file->javascript . '.js'; + $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $file->javascript . '.js'; $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('none')))); // Disable string caching. diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 24ccf1ffa53d38d1486339c5efb21aa34b0cad42..1fc8d6d3a211e6c15d2b5763a14a5bb84be7db51 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -746,7 +746,7 @@ function hook_prepare($node) { if ($file = file_check_upload($field_name)) { $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE)); if ($file) { - if (!image_get_info($file->filepath)) { + if (!image_get_info($file->uri)) { form_set_error($field_name, t('Uploaded file is not a valid image')); return; } @@ -754,7 +754,7 @@ function hook_prepare($node) { else { return; } - $node->images['_original'] = $file->filepath; + $node->images['_original'] = $file->uri; _image_build_derivatives($node, TRUE); $node->new_file = TRUE; } diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 56636eceb9adcd2efe0cdfe5ce6ddf1bfc76924a..51b983a34c97cb626884c741bec37a4afa2a633a 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -541,7 +541,7 @@ function setUp() { // Generate temporary prefixed database to ensure that tests have a clean starting point. $db_prefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}'); - $conf['file_directory_path'] = $this->originalFileDirectory . '/' . $db_prefix; + $conf['file_public_path'] = $this->originalFileDirectory . '/' . $db_prefix; // If locale is enabled then t() will try to access the database and // subsequently will fail as the database is not accessible. @@ -556,7 +556,7 @@ function setUp() { function tearDown() { global $db_prefix, $conf; if (preg_match('/simpletest\d+/', $db_prefix)) { - $conf['file_directory_path'] = $this->originalFileDirectory; + $conf['file_public_path'] = $this->originalFileDirectory; // Return the database prefix to the original. $db_prefix = $this->originalPrefix; // Restore modules if necessary. @@ -814,9 +814,9 @@ protected function drupalGetTestFiles($type, $size = NULL) { // If size is set then remove any files that are not of that size. if ($size !== NULL) { foreach ($files as $file) { - $stats = stat($file->filepath); + $stats = stat($file->uri); if ($stats['size'] != $size) { - unset($files[$file->filepath]); + unset($files[$file->uri]); } } } @@ -829,7 +829,7 @@ protected function drupalGetTestFiles($type, $size = NULL) { * Compare two files based on size and file name. */ protected function drupalCompareFiles($file1, $file2) { - $compare_size = filesize($file1->filepath) - filesize($file2->filepath); + $compare_size = filesize($file1->uri) - filesize($file2->uri); if ($compare_size) { // Sort by file size. return $compare_size; @@ -1050,7 +1050,7 @@ protected function setUp() { // Create test directory ahead of installation so fatal errors and debug // information can be logged during installation process. $directory = $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10); - file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); // Log fatal errors. ini_set('log_errors', 1); @@ -1113,9 +1113,18 @@ protected function setUp() { // default mail handler. variable_set('smtp_library', drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php'); - // Use temporary files directory with the same prefix as database. The - // directory will have been created already. - variable_set('file_directory_path', $directory); + // Use temporary files directory with the same prefix as the database. + $public_files_directory = $this->originalFileDirectory . '/' . $db_prefix; + $private_files_directory = $public_files_directory . '/private'; + + // Set path variables + variable_set('file_public_path', $public_files_directory); + variable_set('file_private_path', $private_files_directory); + + // Create the directories + $directory = file_directory_path('public'); + file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); drupal_set_time_limit($this->timeLimit); } @@ -1169,9 +1178,8 @@ protected function tearDown() { } if (preg_match('/simpletest\d+/', $db_prefix)) { - // Delete temporary files directory and reset files directory path. + // Delete temporary files directory. file_unmanaged_delete_recursive(file_directory_path()); - variable_set('file_directory_path', $this->originalFileDirectory); // Remove all prefixed tables (all the tables in the schema). $schema = drupal_get_schema(NULL, TRUE); @@ -1451,7 +1459,7 @@ protected function drupalPost($path, $edit, $submit, array $options = array(), a // is broken. This is a less than elegant workaround. Alternatives // are being explored at #253506. foreach ($upload as $key => $file) { - $file = realpath($file); + $file = drupal_realpath($file); if ($file && is_file($file)) { $post[$key] = '@' . $file; } @@ -2548,9 +2556,8 @@ function simpletest_verbose($message, $original_file_directory = NULL, $test_cla $file_directory = $original_file_directory; $class = $test_class; $verbose = variable_get('simpletest_verbose', FALSE); - $directory = $file_directory . '/simpletest/verbose'; - return file_check_directory($directory, FILE_CREATE_DIRECTORY); + return file_prepare_directory($directory, FILE_CREATE_DIRECTORY); } return FALSE; } diff --git a/modules/simpletest/simpletest.install b/modules/simpletest/simpletest.install index 42d625c932c803aee79d5c8c0c1d70c640601440..1ca0bae183ff3a4592cf1fe5c1d538bce4ccff2e 100644 --- a/modules/simpletest/simpletest.install +++ b/modules/simpletest/simpletest.install @@ -12,8 +12,8 @@ function simpletest_install() { drupal_install_schema('simpletest'); // Check for files directory. - $path = file_directory_path() . '/simpletest'; - if (file_check_directory($path, FILE_CREATE_DIRECTORY)) { + $path = 'public://simpletest'; + if (file_prepare_directory($path, FILE_CREATE_DIRECTORY)) { // Generate binary and text test files. $generated = FALSE; if (simpletest_get_file_count($path, 'binary') == 0) { @@ -43,7 +43,7 @@ function simpletest_install() { // the test files back easily. if (count($files) > count(file_scan_directory($path, '/(html|image|javascript|php|sql)-.*/'))) { foreach ($files as $file) { - file_unmanaged_copy($file->filepath, $path, FILE_EXISTS_REPLACE); + file_unmanaged_copy($file->uri, $path, FILE_EXISTS_REPLACE); } $generated = TRUE; } @@ -117,7 +117,7 @@ function simpletest_uninstall() { $path = file_directory_path() . '/simpletest'; $files = file_scan_directory($path, '/.*/'); foreach ($files as $file) { - file_unmanaged_delete($file->filepath); + file_unmanaged_delete($file->uri); } rmdir($path); } diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index 58d289078893095d8f6e76cdfcf1b89c44f49905..cbbd453884561518e3aeb5b11fe09d0e9809626b 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -82,6 +82,19 @@ function simpletest_theme() { ); } +/** + * Implementation of hook_stream_wrappers(). + */ +function simpletest_test_stream_wrappers() { + return array( + 'simpletest' => array( + 'name' => t('Simpletest files'), + 'class' => 'DrupalSimpleTestStreamWrapper', + 'description' => t('Stream Wrapper for Simpletest files.'), + ), + ); +} + /** * Implement hook_js_alter(). */ @@ -260,7 +273,7 @@ function simpletest_last_test_get($test_id) { * Found any entries in log. */ function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) { - $log = file_directory_path() . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log'; + $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log'; $found = FALSE; if (file_exists($log)) { foreach (file($log) as $line) { @@ -418,12 +431,11 @@ function simpletest_clean_database() { * Find all leftover temporary directories and remove them. */ function simpletest_clean_temporary_directories() { - $directory = file_directory_path() . '/simpletest'; - $files = scandir($directory); + $files = scandir('public://'); $count = 0; foreach ($files as $file) { - $path = "$directory/$file"; - if (is_dir($path) && is_numeric($file)) { + $path = 'public://' . $file; + if (is_dir($path) && preg_match('/^simpletest\d+/', $file)) { file_unmanaged_delete_recursive($path); $count++; } diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test index 17ae53bf3d19b6c6487db598abc495f1d2437149..8975957c04b18b4e3b6ed05b77c868d23d239fb1 100644 --- a/modules/simpletest/simpletest.test +++ b/modules/simpletest/simpletest.test @@ -220,7 +220,7 @@ class SimpleTestFunctionalTest extends DrupalWebTestCase { $assertion['file'] = $this->asText($row->td[2]); $assertion['line'] = $this->asText($row->td[3]); $assertion['function'] = $this->asText($row->td[4]); - $ok_url = (url('misc/watchdog-ok.png') == 'misc/watchdog-ok.png') ? 'misc/watchdog-ok.png' : (base_path() . 'misc/watchdog-ok.png'); + $ok_url = file_create_url('misc/watchdog-ok.png'); $assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail'; $results['assertions'][] = $assertion; } diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index cdd10f5eac4c74435ccad3e0b8f321429c6cf997..c302a727aaca87f42d08934948cee26e6cbafdf4 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -403,8 +403,9 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { drupal_get_path('module', 'simpletest') . '/simpletest.css', ); + $styles = drupal_get_css(); - if (preg_match_all('/href="' . preg_quote(base_path(), '/') . '([^?]+)\?/', $styles, $matches)) { + if (preg_match_all('/href="' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) { $result = $matches[1]; } else { diff --git a/modules/simpletest/tests/error.test b/modules/simpletest/tests/error.test index 28d6e08309d87095c375313ec9cd4bca79f8b212..fe4ee4526e38d31a3b7e361344ef976b3d619115 100644 --- a/modules/simpletest/tests/error.test +++ b/modules/simpletest/tests/error.test @@ -26,21 +26,21 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { '%message' => 'Undefined variable: bananas', '%function' => 'error_test_generate_warnings()', '%line' => 44, - '%file' => realpath('modules/simpletest/tests/error_test.module'), + '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_warning = array( '%type' => 'Warning', '%message' => 'Division by zero', '%function' => 'error_test_generate_warnings()', '%line' => 46, - '%file' => realpath('modules/simpletest/tests/error_test.module'), + '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_user_notice = array( '%type' => 'User warning', '%message' => 'Drupal is awesome', '%function' => 'error_test_generate_warnings()', '%line' => 48, - '%file' => realpath('modules/simpletest/tests/error_test.module'), + '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); // Set error reporting to collect notices. @@ -77,14 +77,14 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { '%message' => 'Drupal is awesome', '%function' => 'error_test_trigger_exception()', '%line' => 57, - '%file' => realpath('modules/simpletest/tests/error_test.module'), + '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_pdo_exception = array( '%type' => 'PDOException', '%message' => 'SELECT * FROM bananas_are_awesome', '%function' => 'error_test_trigger_pdo_exception()', '%line' => 65, - '%file' => realpath('modules/simpletest/tests/error_test.module'), + '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $this->drupalGet('error-test/trigger-exception'); diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test index 23c1d1241096750ac36eb0fb4ae67f4c74a5dbf5..26bb051547f297e9e8fa9f35c109542555038672 100644 --- a/modules/simpletest/tests/file.test +++ b/modules/simpletest/tests/file.test @@ -61,7 +61,7 @@ class FileTestCase extends DrupalWebTestCase { $this->assertEqual($before->fid, $after->fid, t('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged'); $this->assertEqual($before->uid, $after->uid, t('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged'); $this->assertEqual($before->filename, $after->filename, t('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged'); - $this->assertEqual($before->filepath, $after->filepath, t('File path is the same: %file1 == %file2.', array('%file1' => $before->filepath, '%file2' => $after->filepath)), 'File unchanged'); + $this->assertEqual($before->uri, $after->uri, t('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged'); $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged'); $this->assertEqual($before->filesize, $after->filesize, t('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged'); $this->assertEqual($before->status, $after->status, t('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged'); @@ -77,7 +77,7 @@ class FileTestCase extends DrupalWebTestCase { */ function assertDifferentFile($file1, $file2) { $this->assertNotEqual($file1->fid, $file2->fid, t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file'); - $this->assertNotEqual($file1->filepath, $file2->filepath, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->filepath, '%file2' => $file2->filepath)), 'Different file'); + $this->assertNotEqual($file1->uri, $file2->uri, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file'); } /** @@ -90,7 +90,7 @@ class FileTestCase extends DrupalWebTestCase { */ function assertSameFile($file1, $file2) { $this->assertEqual($file1->fid, $file2->fid, t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file'); - $this->assertEqual($file1->filepath, $file2->filepath, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->filepath, '%file2' => $file2->filepath)), 'Same file'); + $this->assertEqual($file1->uri, $file2->uri, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file'); } /** @@ -151,7 +151,7 @@ class FileTestCase extends DrupalWebTestCase { if (is_null($path)) { $path = file_directory_path() . '/' . $this->randomName(); } - $this->assertTrue(mkdir($path) && is_dir($path), t('Directory was created successfully.')); + $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.')); return $path; } @@ -165,13 +165,17 @@ class FileTestCase extends DrupalWebTestCase { * @param $contents * Optional contents to save into the file. If a NULL value is provided an * arbitrary string will be used. + * @param $scheme + * Optional string indicating the stream scheme to use. Drupal core includes + * public, private, and temporary. The public wrapper is the default. * @return * File object. */ - function createFile($filepath = NULL, $contents = NULL) { + function createFile($filepath = NULL, $contents = NULL, $scheme = 'public') { if (is_null($filepath)) { - $filepath = file_directory_path() . '/' . $this->randomName(); + $filepath = $this->randomName(); } + $filepath = $scheme . '://' . $filepath; if (is_null($contents)) { $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data."; @@ -181,16 +185,16 @@ class FileTestCase extends DrupalWebTestCase { $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); $file = new stdClass(); - $file->filepath = $filepath; - $file->filename = basename($file->filepath); + $file->uri = $filepath; + $file->filename = basename($file->uri); $file->filemime = 'text/plain'; $file->uid = 1; $file->timestamp = REQUEST_TIME; - $file->filesize = filesize($file->filepath); + $file->filesize = filesize($file->uri); $file->status = 0; // Write the record directly rather than calling file_save() so we don't // invoke the hooks. - $this->assertNotIdentical(drupal_write_record('files', $file), FALSE, t('The file was added to the database.'), 'Create test file'); + $this->assertNotIdentical(drupal_write_record('file', $file), FALSE, t('The file was added to the database.'), 'Create test file'); return $file; } @@ -284,16 +288,16 @@ class FileSpaceUsedTest extends FileTestCase { parent::setUp(); // Create records for a couple of users with different sizes. - drupal_write_record('files', $file = array('uid' => 2, 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT)); - drupal_write_record('files', $file = array('uid' => 2, 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT)); - drupal_write_record('files', $file = array('uid' => 3, 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT)); - drupal_write_record('files', $file = array('uid' => 3, 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT)); + drupal_write_record('file', $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT)); + drupal_write_record('file', $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT)); + drupal_write_record('file', $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT)); + drupal_write_record('file', $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT)); // Now create some with other statuses. These values were chosen arbitrarily // for the sole purpose of testing that bitwise operators were used // correctly on the field. - drupal_write_record('files', $file = array('uid' => 2, 'filesize' => 1, 'status' => 2 | 8)); - drupal_write_record('files', $file = array('uid' => 3, 'filesize' => 3, 'status' => 2 | 4)); + drupal_write_record('file', $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 2 | 8)); + drupal_write_record('file', $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 2 | 4)); } /** @@ -344,12 +348,12 @@ class FileValidatorTest extends DrupalWebTestCase { parent::setUp(); $this->image = new stdClass(); - $this->image->filepath = 'misc/druplicon.png'; - $this->image->filename = basename($this->image->filepath); + $this->image->uri = 'misc/druplicon.png'; + $this->image->filename = basename($this->image->uri); $this->non_image = new stdClass(); - $this->non_image->filepath = 'misc/jquery.js'; - $this->non_image->filename = basename($this->non_image->filepath); + $this->non_image->uri = 'misc/jquery.js'; + $this->non_image->filename = basename($this->non_image->uri); } /** @@ -370,11 +374,11 @@ class FileValidatorTest extends DrupalWebTestCase { * This ensures a specific file is actually an image. */ function testFileValidateIsImage() { - $this->assertTrue(file_exists($this->image->filepath), t('The image being tested exists.'), 'File'); + $this->assertTrue(file_exists($this->image->uri), t('The image being tested exists.'), 'File'); $errors = file_validate_is_image($this->image); $this->assertEqual(count($errors), 0, t('No error reported for our image file.'), 'File'); - $this->assertTrue(file_exists($this->non_image->filepath), t('The non-image being tested exists.'), 'File'); + $this->assertTrue(file_exists($this->non_image->uri), t('The non-image being tested exists.'), 'File'); $errors = file_validate_is_image($this->non_image); $this->assertEqual(count($errors), 1, t('An error reported for our non-image file.'), 'File'); } @@ -403,18 +407,18 @@ class FileValidatorTest extends DrupalWebTestCase { // Maximum size. if (image_get_toolkit()) { // Copy the image so that the original doesn't get resized. - $temp_dir = file_directory_temp(); - copy(realpath('misc/druplicon.png'), realpath($temp_dir) . '/druplicon.png'); - $this->image->filepath = $temp_dir . '/druplicon.png'; + $temp_dir = file_directory_path('temporary'); + copy(drupal_realpath('misc/druplicon.png'), drupal_realpath($temp_dir) . '/druplicon.png'); + $this->image->uri = $temp_dir . '/druplicon.png'; $errors = file_validate_image_resolution($this->image, '10x5'); $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File'); - $info = image_get_info($this->image->filepath); + $info = image_get_info($this->image->uri); $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File'); $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File'); - unlink(realpath($temp_dir . '/druplicon.png')); + unlink(drupal_realpath($temp_dir . '/druplicon.png')); } else { // TODO: should check that the error is returned if no toolkit is available. @@ -431,15 +435,15 @@ class FileValidatorTest extends DrupalWebTestCase { $file = new stdClass(); // Add a filename with an allowed length and test it. - $file->filename = str_repeat('x', 255); - $this->assertEqual(strlen($file->filename), 255); + $file->filename = str_repeat('x', 240); + $this->assertEqual(strlen($file->filename), 240); $errors = file_validate_name_length($file); - $this->assertEqual(count($errors), 0, t('No errors reported for 255 length filename.'), 'File'); + $this->assertEqual(count($errors), 0, t('No errors reported for 240 length filename.'), 'File'); // Add a filename with a length too long and test it. - $file->filename = str_repeat('x', 256); + $file->filename = str_repeat('x', 241); $errors = file_validate_name_length($file); - $this->assertEqual(count($errors), 1, t('An error reported for 256 length filename.'), 'File'); + $this->assertEqual(count($errors), 1, t('An error reported for 241 length filename.'), 'File'); // Add a filename with an empty string and test it. $file->filename = ''; @@ -507,15 +511,15 @@ class FileUnmanagedSaveDataTest extends FileTestCase { // No filename. $filepath = file_unmanaged_save_data($contents); $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path(), dirname($filepath), t("File was placed in Drupal's files directory.")); - $this->assertEqual($contents, file_get_contents(realpath($filepath)), t('Contents of the file are correct.')); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($filepath)), t("File was placed in Drupal's files directory.")); + $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.')); // Provide a filename. - $filepath = file_unmanaged_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE); + $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path(), dirname($filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_uri_scheme($filepath)), t("File was placed in Drupal's files directory.")); $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.')); - $this->assertEqual($contents, file_get_contents(realpath($filepath)), t('Contents of the file are correct.')); + $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.')); $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664)); } } @@ -548,14 +552,14 @@ class FileSaveUploadTest extends FileHookTestCase { $this->drupalLogin($account); $this->image = current($this->drupalGetTestFiles('image')); - $this->assertTrue(is_file($this->image->filepath), t("The file we're going to upload exists.")); + $this->assertTrue(is_file($this->image->uri), t("The file we're going to upload exists.")); - $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {files}')->fetchField(); + $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file}')->fetchField(); // Upload with replace to gurantee there's something there. $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => realpath($this->image->filepath) + '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.')); @@ -571,7 +575,7 @@ class FileSaveUploadTest extends FileHookTestCase { * Test the file_save_upload() function. */ function testNormal() { - $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {files}')->fetchField(); + $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file}')->fetchField(); $this->assertTrue($max_fid_after > $this->maxFidBefore, t('A new file was created.')); $file1 = file_load($max_fid_after); $this->assertTrue($file1, t('Loaded the file.')); @@ -580,13 +584,13 @@ class FileSaveUploadTest extends FileHookTestCase { file_test_reset(); // Upload a second file. - $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {files}')->fetchField(); + $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file}')->fetchField(); $image2 = current($this->drupalGetTestFiles('image')); - $edit = array('files[file_test_upload]' => realpath($image2->filepath)); + $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!')); - $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {files}')->fetchField(); + $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file}')->fetchField(); // Check that the correct hooks were called. $this->assertFileHooksCalled(array('validate', 'insert')); @@ -610,7 +614,7 @@ class FileSaveUploadTest extends FileHookTestCase { function testExistingRename() { $edit = array( 'file_test_replace' => FILE_EXISTS_RENAME, - 'files[file_test_upload]' => realpath($this->image->filepath) + '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.')); @@ -626,7 +630,7 @@ class FileSaveUploadTest extends FileHookTestCase { function testExistingReplace() { $edit = array( 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => realpath($this->image->filepath) + '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.')); @@ -642,7 +646,7 @@ class FileSaveUploadTest extends FileHookTestCase { function testExistingError() { $edit = array( 'file_test_replace' => FILE_EXISTS_ERROR, - 'files[file_test_upload]' => realpath($this->image->filepath) + '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.')); @@ -682,94 +686,54 @@ class FileDirectoryTest extends FileTestCase { $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.')); // Non-existent directory. - $form_element = $this->randomName(); - $this->assertFalse(file_check_directory($directory, 0, $form_element), t('Error reported for non-existing directory.'), 'File'); - - // Check that an error was set for the form element above. - $errors = form_get_errors(); - $this->assertEqual($errors[$form_element], t('The directory %directory does not exist.', array('%directory' => $directory)), t('Properly generated an error for the passed form element.'), 'File'); + $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for non-existing directory.'), 'File'); // Make a directory. - $this->assertTrue(file_check_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File'); + $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File'); // Make sure directory actually exists. $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File'); // Make directory read only. @chmod($directory, 0444); - $form_element = $this->randomName(); - $this->assertFalse(file_check_directory($directory, 0, $form_element), t('Error reported for a non-writeable directory.'), 'File'); - - // Check if form error was set. - $errors = form_get_errors(); - $this->assertEqual($errors[$form_element], t('The directory %directory is not writable', array('%directory' => $directory)), t('Properly generated an error for the passed form element.'), 'File'); + $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File'); // Test directory permission modification. - $this->assertTrue(file_check_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); + $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); // Test directory permission modification actually set correct permissions. $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775)); // Remove .htaccess file to then test that it gets re-created. - @unlink(file_directory_path() . '/.htaccess'); $directory = file_directory_path(); - file_check_directory($directory); - $this->assertTrue(is_file(file_directory_path() . '/.htaccess'), t('Successfully created the .htaccess file in the files directory.'), 'File'); - + @unlink($directory . '/.htaccess'); + $this->assertFalse(is_file($directory . '/.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File'); + file_ensure_htaccess(); + $this->assertTrue(is_file($directory . '/.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File'); // Verify contents of .htaccess file. $file = file_get_contents(file_directory_path() . '/.htaccess'); $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File'); } /** - * Check file_directory_path() and file_directory_temp(). + * Check file_directory_path('public'). */ function testFileDirectoryPath() { // Directory path. - $path = variable_get('file_directory_path', conf_path() . '/files'); - $this->assertEqual($path, file_directory_path(), t('Properly returns the stored file directory path.'), 'File'); + $path = variable_get('file_public_path', ''); + $this->assertEqual($path, file_directory_path('public'), t('Properly returns the stored file directory path.'), 'File'); } /** - * Check file_directory_path() and file_directory_temp(). + * Check file_directory_path() and file_directory_path('temporary'). */ function testFileDirectoryTemp() { // Temporary directory handling. variable_set('file_directory_temp', NULL); - $temp = file_directory_temp(); + $temp = file_directory_path('temporary'); $this->assertTrue(!is_null($temp), t('Properly set and retrieved temp directory %directory.', array('%directory' => $temp)), 'File'); } - /** - * This tests that a file is actually in the specified directory, to prevent - * exploits. - */ - function testFileCheckLocation() { - $source = 'misc/xyz.txt'; - $directory = 'misc'; - $result = file_check_location($source, $directory); - $this->assertTrue($result, t('Non-existent file validates when checked for location in existing directory.'), 'File'); - - $source = 'fake/xyz.txt'; - $directory = 'fake'; - $result = file_check_location($source, $directory); - $this->assertTrue($result, t('Non-existent file validates when checked for location in non-existing directory.'), 'File'); - - $source = 'misc/../install.php'; - $directory = 'misc'; - $result = file_check_location($source, $directory); - $this->assertFalse($result, t('Existing file fails validation when it exists outside the directory path, using a /../ exploit.'), 'File'); - - $source = 'misc/druplicon.png'; - $directory = 'misc'; - $result = file_check_location($source, $directory); - $this->assertTrue($result, t('Existing file passes validation when checked for location in directory path, and filepath contains a subfolder of the checked path.'), 'File'); - - $result = file_check_location($source, $directory); - $this->assertTrue($result, t('Existing file passes validation, returning the source when checked for location in directory.'), 'File'); - } - - /** * This will take a directory and path, and find a valid filepath that is not * taken by another file. @@ -855,15 +819,15 @@ class FileScanDirectoryTest extends FileTestCase { // Check the first file. $file = reset($all_files); - $this->assertEqual(key($all_files), $file->filepath, t('Correct array key was used for the first returned file.')); - $this->assertEqual($file->filepath, $this->path . '/javascript-1.txt', t('First file name was set correctly.')); + $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the first returned file.')); + $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', t('First file name was set correctly.')); $this->assertEqual($file->filename, 'javascript-1.txt', t('First basename was set correctly')); $this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.')); // Check the second file. $file = next($all_files); - $this->assertEqual(key($all_files), $file->filepath, t('Correct array key was used for the second returned file.')); - $this->assertEqual($file->filepath, $this->path . '/javascript-2.script', t('Second file name was set correctly.')); + $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the second returned file.')); + $this->assertEqual($file->uri, $this->path . '/javascript-2.script', t('Second file name was set correctly.')); $this->assertEqual($file->filename, 'javascript-2.script', t('Second basename was set correctly')); $this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.')); } @@ -976,8 +940,8 @@ class FileUnmanagedDeleteTest extends FileTestCase { $file = $this->createFile(); // Delete a regular file - $this->assertTrue(file_unmanaged_delete($file->filepath), t('Deleted worked.')); - $this->assertFalse(file_exists($file->filepath), t('Test file has actually been deleted.')); + $this->assertTrue(file_unmanaged_delete($file->uri), t('Deleted worked.')); + $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); } /** @@ -1099,16 +1063,16 @@ class FileUnmanagedMoveTest extends FileTestCase { $file = $this->createFile(); // Moving to a new name. - $desired_filepath = file_directory_path() . '/' . $this->randomName(); - $new_filepath = file_unmanaged_move($file->filepath, $desired_filepath, FILE_EXISTS_ERROR); + $desired_filepath = 'public://' . $this->randomName(); + $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR); $this->assertTrue($new_filepath, t('Move was successful.')); $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); $this->assertTrue(file_exists($new_filepath), t('File exists at the new location.')); - $this->assertFalse(file_exists($file->filepath), t('No file remains at the old location.')); + $this->assertFalse(file_exists($file->uri), t('No file remains at the old location.')); $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); // Moving with rename. - $desired_filepath = file_directory_path() . '/' . $this->randomName(); + $desired_filepath = 'public://' . $this->randomName(); $this->assertTrue(file_exists($new_filepath), t('File exists before moving.')); $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME); @@ -1119,6 +1083,7 @@ class FileUnmanagedMoveTest extends FileTestCase { $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); // TODO: test moving to a directory (rather than full directory/file path) + // TODO: test creating and moving normal files (rather than streams) } /** @@ -1138,14 +1103,14 @@ class FileUnmanagedMoveTest extends FileTestCase { $file = $this->createFile(); // Move the file onto itself without renaming shouldn't make changes. - $new_filepath = file_unmanaged_move($file->filepath, $file->filepath, FILE_EXISTS_REPLACE); + $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE); $this->assertFalse($new_filepath, t('Moving onto itself without renaming fails.')); - $this->assertTrue(file_exists($file->filepath), t('File exists after moving onto itself.')); + $this->assertTrue(file_exists($file->uri), t('File exists after moving onto itself.')); // Move the file onto itself with renaming will result in a new filename. - $new_filepath = file_unmanaged_move($file->filepath, $file->filepath, FILE_EXISTS_RENAME); + $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME); $this->assertTrue($new_filepath, t('Moving onto itself with renaming works.')); - $this->assertFalse(file_exists($file->filepath), t('Original file has been removed.')); + $this->assertFalse(file_exists($file->uri), t('Original file has been removed.')); $this->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.')); } } @@ -1171,25 +1136,26 @@ class FileUnmanagedCopyTest extends FileTestCase { $file = $this->createFile(); // Copying to a new name. - $desired_filepath = file_directory_path() . '/' . $this->randomName(); - $new_filepath = file_unmanaged_copy($file->filepath, $desired_filepath, FILE_EXISTS_ERROR); + $desired_filepath = 'public://' . $this->randomName(); + $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR); $this->assertTrue($new_filepath, t('Copy was successful.')); $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($file->filepath), t('Original file remains.')); + $this->assertTrue(file_exists($file->uri), t('Original file remains.')); $this->assertTrue(file_exists($new_filepath), t('New file exists.')); $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); // Copying with rename. - $desired_filepath = file_directory_path() . '/' . $this->randomName(); + $desired_filepath = 'public://' . $this->randomName(); $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); - $newer_filepath = file_unmanaged_copy($file->filepath, $desired_filepath, FILE_EXISTS_RENAME); + $newer_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_RENAME); $this->assertTrue($newer_filepath, t('Copy was successful.')); $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($file->filepath), t('Original file remains.')); + $this->assertTrue(file_exists($file->uri), t('Original file remains.')); $this->assertTrue(file_exists($newer_filepath), t('New file exists.')); $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); // TODO: test copying to a directory (rather than full directory/file path) + // TODO: test copying normal files using normal paths (rather than only streams) } /** @@ -1211,28 +1177,28 @@ class FileUnmanagedCopyTest extends FileTestCase { $file = $this->createFile(); // Copy the file onto itself with renaming works. - $new_filepath = file_unmanaged_copy($file->filepath, $file->filepath, FILE_EXISTS_RENAME); + $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_RENAME); $this->assertTrue($new_filepath, t('Copying onto itself with renaming works.')); - $this->assertNotEqual($new_filepath, $file->filepath, t('Copied file has a new name.')); - $this->assertTrue(file_exists($file->filepath), t('Original file exists after copying onto itself.')); + $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); + $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); // Copy the file onto itself without renaming fails. - $new_filepath = file_unmanaged_copy($file->filepath, $file->filepath, FILE_EXISTS_ERROR); + $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_ERROR); $this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.')); - $this->assertTrue(file_exists($file->filepath), t('File exists after copying onto itself.')); + $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); // Copy the file into same directory without renaming fails. - $new_filepath = file_unmanaged_copy($file->filepath, dirname($file->filepath), FILE_EXISTS_ERROR); + $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_ERROR); $this->assertFalse($new_filepath, t('Copying onto itself fails.')); - $this->assertTrue(file_exists($file->filepath), t('File exists after copying onto itself.')); + $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); // Copy the file into same directory with renaming works. - $new_filepath = file_unmanaged_copy($file->filepath, dirname($file->filepath), FILE_EXISTS_RENAME); + $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_RENAME); $this->assertTrue($new_filepath, t('Copying into same directory works.')); - $this->assertNotEqual($new_filepath, $file->filepath, t('Copied file has a new name.')); - $this->assertTrue(file_exists($file->filepath), t('Original file exists after copying onto itself.')); + $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); + $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); } @@ -1257,10 +1223,10 @@ class FileDeleteTest extends FileHookTestCase { $file = $this->createFile(); // Check that deletion removes the file and database record. - $this->assertTrue(is_file($file->filepath), t("File exists.")); + $this->assertTrue(is_file($file->uri), t("File exists.")); $this->assertIdentical(file_delete($file), TRUE, t("Delete worked.")); $this->assertFileHooksCalled(array('references', 'delete')); - $this->assertFalse(file_exists($file->filepath), t("Test file has actually been deleted.")); + $this->assertFalse(file_exists($file->uri), t("Test file has actually been deleted.")); $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); // TODO: implement hook_file_references() in file_test.module and report a @@ -1287,7 +1253,7 @@ class FileMoveTest extends FileHookTestCase { function testNormal() { $contents = $this->randomName(10); $source = $this->createFile(NULL, $contents); - $desired_filepath = file_directory_path() . '/' . $this->randomName(); + $desired_filepath = 'public://' . $this->randomName(); // Clone the object so we don't have to worry about the function changing // our reference copy. @@ -1295,8 +1261,8 @@ class FileMoveTest extends FileHookTestCase { // Check the return status and that the contents changed. $this->assertTrue($result, t('File moved sucessfully.')); - $this->assertFalse(file_exists($source->filepath)); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file correctly written.')); + $this->assertFalse(file_exists($source->uri)); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); // Check that the correct hooks were called. $this->assertFileHooksCalled(array('move', 'update')); @@ -1323,12 +1289,12 @@ class FileMoveTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_move(clone $source, $target->filepath, FILE_EXISTS_RENAME); + $result = file_move(clone $source, $target->uri, FILE_EXISTS_RENAME); // Check the return status and that the contents changed. $this->assertTrue($result, t('File moved sucessfully.')); - $this->assertFalse(file_exists($source->filepath)); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file correctly written.')); + $this->assertFalse(file_exists($source->uri)); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); // Check that the correct hooks were called. $this->assertFileHooksCalled(array('move', 'update')); @@ -1343,7 +1309,7 @@ class FileMoveTest extends FileHookTestCase { // Compare the source and results. $loaded_source = file_load($source->fid, TRUE); $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source.")); - $this->assertNotEqual($loaded_source->filepath, $source->filepath, t("Returned file path has changed from the original.")); + $this->assertNotEqual($loaded_source->uri, $source->uri, t("Returned file path has changed from the original.")); } /** @@ -1358,11 +1324,11 @@ class FileMoveTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_move(clone $source, $target->filepath, FILE_EXISTS_REPLACE); + $result = file_move(clone $source, $target->uri, FILE_EXISTS_REPLACE); // Look at the results. - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were overwritten.')); - $this->assertFalse(file_exists($source->filepath)); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); + $this->assertFalse(file_exists($source->uri)); $this->assertTrue($result, t('File moved sucessfully.')); // Check that the correct hooks were called. @@ -1388,9 +1354,9 @@ class FileMoveTest extends FileHookTestCase { // Copy the file over itself. Clone the object so we don't have to worry // about the function changing our reference copy. - $result = file_move(clone $source, $source->filepath, FILE_EXISTS_REPLACE); + $result = file_move(clone $source, $source->uri, FILE_EXISTS_REPLACE); $this->assertFalse($result, t('File move failed.')); - $this->assertEqual($contents, file_get_contents($source->filepath), t('Contents of file were not altered.')); + $this->assertEqual($contents, file_get_contents($source->uri), t('Contents of file were not altered.')); // Check that no hooks were called while failing. $this->assertFileHooksCalled(array()); @@ -1412,12 +1378,12 @@ class FileMoveTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_move(clone $source, $target->filepath, FILE_EXISTS_ERROR); + $result = file_move(clone $source, $target->uri, FILE_EXISTS_ERROR); // Check the return status and that the contents did not change. $this->assertFalse($result, t('File move failed.')); - $this->assertTrue(file_exists($source->filepath)); - $this->assertEqual($contents, file_get_contents($target->filepath), t('Contents of file were not altered.')); + $this->assertTrue(file_exists($source->uri)); + $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); // Check that no hooks were called while failing. $this->assertFileHooksCalled(array()); @@ -1448,23 +1414,23 @@ class FileCopyTest extends FileHookTestCase { function testNormal() { $contents = $this->randomName(10); $source = $this->createFile(NULL, $contents); - $desired_filepath = file_directory_path() . '/' . $this->randomName(); + $desired_uri = 'public://' . $this->randomName(); // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_copy(clone $source, $desired_filepath, FILE_EXISTS_ERROR); + $result = file_copy(clone $source, $desired_uri, FILE_EXISTS_ERROR); // Check the return status and that the contents changed. $this->assertTrue($result, t('File copied sucessfully.')); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were copied correctly.')); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); // Check that the correct hooks were called. $this->assertFileHooksCalled(array('copy', 'insert')); $this->assertDifferentFile($source, $result); - $this->assertEqual($result->filepath, $desired_filepath, t('The copied file object has the desired filepath.')); - $this->assertTrue(file_exists($source->filepath), t('The original file still exists.')); - $this->assertTrue(file_exists($result->filepath), t('The copied file exists.')); + $this->assertEqual($result->uri, $desired_uri, t('The copied file object has the desired filepath.')); + $this->assertTrue(file_exists($source->uri), t('The original file still exists.')); + $this->assertTrue(file_exists($result->uri), t('The copied file exists.')); // Reload the file from the database and check that the changes were // actually saved. @@ -1483,12 +1449,12 @@ class FileCopyTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_RENAME); + $result = file_copy(clone $source, $target->uri, FILE_EXISTS_RENAME); // Check the return status and that the contents changed. $this->assertTrue($result, t('File copied sucessfully.')); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were copied correctly.')); - $this->assertNotEqual($result->filepath, $source->filepath, t('Returned file path has changed from the original.')); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); + $this->assertNotEqual($result->uri, $source->uri, t('Returned file path has changed from the original.')); // Check that the correct hooks were called. $this->assertFileHooksCalled(array('copy', 'insert')); @@ -1523,11 +1489,11 @@ class FileCopyTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_REPLACE); + $result = file_copy(clone $source, $target->uri, FILE_EXISTS_REPLACE); // Check the return status and that the contents changed. $this->assertTrue($result, t('File copied sucessfully.')); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were overwritten.')); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); $this->assertDifferentFile($source, $result); // Check that the correct hooks were called. @@ -1561,11 +1527,11 @@ class FileCopyTest extends FileHookTestCase { // Clone the object so we don't have to worry about the function changing // our reference copy. - $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_ERROR); + $result = file_copy(clone $source, $target->uri, FILE_EXISTS_ERROR); // Check the return status and that the contents were not changed. $this->assertFalse($result, t('File copy failed.')); - $this->assertEqual($contents, file_get_contents($target->filepath), t('Contents of file were not altered.')); + $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); // Check that the correct hooks were called. $this->assertFileHooksCalled(array()); @@ -1597,10 +1563,10 @@ class FileLoadTest extends FileHookTestCase { } /** - * Try to load a non-existent file by filepath. + * Try to load a non-existent file by URI. */ function testLoadMissingFilepath() { - $this->assertFalse(reset(file_load_multiple(array(), array('filepath' => 'misc/druplicon.png'))), t("Try to load a file that doesn't exist in the database fails.")); + $this->assertFalse(reset(file_load_multiple(array(), array('uri' => 'foobar://misc/druplicon.png'))), t("Try to load a file that doesn't exist in the database fails.")); $this->assertFileHooksCalled(array()); } @@ -1617,21 +1583,13 @@ class FileLoadTest extends FileHookTestCase { */ function testSingleValues() { // Create a new file object from scratch so we know the values. - $file = array( - 'uid' => 1, - 'filename' => 'druplicon.png', - 'filepath' => 'misc/druplicon.png', - 'filemime' => 'image/png', - 'timestamp' => 1, - 'status' => FILE_STATUS_PERMANENT, - ); - $file = file_save($file); + $file = $this->createFile('druplicon.txt', NULL, 'public'); $by_fid_file = file_load($file->fid); $this->assertFileHookCalled('load'); $this->assertTrue(is_object($by_fid_file), t('file_load() returned an object.')); $this->assertEqual($by_fid_file->fid, $file->fid, t("Loading by fid got the same fid."), 'File'); - $this->assertEqual($by_fid_file->filepath, $file->filepath, t("Loading by fid got the correct filepath."), 'File'); + $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); $this->assertEqual($by_fid_file->filename, $file->filename, t("Loading by fid got the correct filename."), 'File'); $this->assertEqual($by_fid_file->filemime, $file->filemime, t("Loading by fid got the correct MIME type."), 'File'); $this->assertEqual($by_fid_file->status, $file->status, t("Loading by fid got the correct status."), 'File'); @@ -1643,19 +1601,11 @@ class FileLoadTest extends FileHookTestCase { */ function testMultiple() { // Create a new file object. - $file = array( - 'uid' => 1, - 'filename' => 'druplicon.png', - 'filepath' => 'misc/druplicon.png', - 'filemime' => 'image/png', - 'timestamp' => 1, - 'status' => FILE_STATUS_PERMANENT, - ); - $file = file_save($file); + $file = $this->createFile('druplicon.txt', NULL, 'public'); // Load by path. file_test_reset(); - $by_path_files = file_load_multiple(array(), array('filepath' => $file->filepath)); + $by_path_files = file_load_multiple(array(), array('uri' => $file->uri)); $this->assertFileHookCalled('load'); $this->assertEqual(1, count($by_path_files), t('file_load_multiple() returned an array of the correct size.')); $by_path_file = reset($by_path_files); @@ -1669,7 +1619,7 @@ class FileLoadTest extends FileHookTestCase { $this->assertEqual(1, count($by_fid_files), t('file_load_multiple() returned an array of the correct size.')); $by_fid_file = reset($by_fid_files); $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); - $this->assertEqual($by_fid_file->filepath, $file->filepath, t("Loading by fid got the correct filepath."), 'File'); + $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); } } @@ -1689,13 +1639,14 @@ class FileSaveTest extends FileHookTestCase { // Create a new file object. $file = array( 'uid' => 1, - 'filename' => 'druplicon.png', - 'filepath' => 'misc/druplicon.png', - 'filemime' => 'image/png', + 'filename' => 'druplicon.txt', + 'uri' => 'public://druplicon.txt', + 'filemime' => 'text/plain', 'timestamp' => 1, 'status' => FILE_STATUS_PERMANENT, ); $file = (object) $file; + file_put_contents($file->uri, 'hello world'); // Save it, inserting a new record. $saved_file = file_save($file); @@ -1705,10 +1656,10 @@ class FileSaveTest extends FileHookTestCase { $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File'); $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File'); - $loaded_file = db_query('SELECT * FROM {files} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); + $loaded_file = db_query('SELECT * FROM {file} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); $this->assertNotNull($loaded_file, t("Record exists in the database.")); $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly.")); - $this->assertEqual($saved_file->filesize, filesize($file->filepath), t("File size was set correctly."), 'File'); + $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File'); $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File'); @@ -1722,7 +1673,7 @@ class FileSaveTest extends FileHookTestCase { $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File'); $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File'); - $loaded_file = db_query('SELECT * FROM {files} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); + $loaded_file = db_query('SELECT * FROM {file} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File'); $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly.")); } @@ -1789,9 +1740,9 @@ class FileSaveDataTest extends FileHookTestCase { $result = file_save_data($contents); $this->assertTrue($result, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); - $this->assertEqual($result->filename, basename($result->filepath), t("Filename was set to the file's basename.")); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename.")); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -1808,12 +1759,12 @@ class FileSaveDataTest extends FileHookTestCase { function testWithFilename() { $contents = $this->randomName(8); - $result = file_save_data($contents, 'asdf.txt'); + $result = file_save_data($contents, 'public://' . 'asdf.txt'); $this->assertTrue($result, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); - $this->assertEqual('asdf.txt', basename($result->filepath), t('File was named correctly.')); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.')); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -1832,12 +1783,12 @@ class FileSaveDataTest extends FileHookTestCase { $existing = $this->createFile(); $contents = $this->randomName(8); - $result = file_save_data($contents, $existing->filepath, FILE_EXISTS_RENAME); + $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME); $this->assertTrue($result, t("File saved sucessfully.")); - $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file.")); - $this->assertEqual($contents, file_get_contents($result->filepath), t("Contents of the file are correct.")); + $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct.")); $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set.")); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -1860,12 +1811,12 @@ class FileSaveDataTest extends FileHookTestCase { $existing = $this->createFile(); $contents = $this->randomName(8); - $result = file_save_data($contents, $existing->filepath, FILE_EXISTS_REPLACE); + $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE); $this->assertTrue($result, t('File saved sucessfully.')); - $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.')); - $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -1887,9 +1838,9 @@ class FileSaveDataTest extends FileHookTestCase { $existing = $this->createFile(NULL, $contents); // Check the overwrite error. - $result = file_save_data('asdf', $existing->filepath, FILE_EXISTS_ERROR); + $result = file_save_data('asdf', $existing->uri, FILE_EXISTS_ERROR); $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.')); - $this->assertEqual($contents, file_get_contents($existing->filepath), t('Contents of existing file were unchanged.')); + $this->assertEqual($contents, file_get_contents($existing->uri), t('Contents of existing file were unchanged.')); // Check that no hooks were called while failing. $this->assertFileHooksCalled(array()); @@ -1920,11 +1871,10 @@ class FileDownloadTest extends FileTestCase { */ function testPrivateFileTransfer() { // Set file downloads to private so handler functions get called. - variable_set('file_downloads', FILE_DOWNLOADS_PRIVATE); // Create a file. - $file = $this->createFile(); - $url = file_create_url($file->filename); + $file = $this->createFile(NULL, NULL, 'private'); + $url = file_create_url($file->uri); // Set file_test access header to allow the download. file_test_set_return('download', array('x-foo' => 'Bar')); @@ -1939,7 +1889,7 @@ class FileDownloadTest extends FileTestCase { $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.')); // Try non-existent file. - $url = file_create_url($this->randomName()); + $url = file_create_url('private://' . $this->randomName()); $this->drupalHead($url); $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.')); } @@ -2022,6 +1972,8 @@ class FileMimeTypeTest extends DrupalWebTestCase { * Test mapping of mimetypes from filenames. */ public function testFileMimeTypeDetection() { + $prefix = 'simpletest://'; + $test_case = array( 'test.jar' => 'application/java-archive', 'test.jpeg' => 'image/jpeg', @@ -2068,6 +2020,11 @@ class FileMimeTypeTest extends DrupalWebTestCase { variable_set('mime_extension_mapping', $mapping); foreach ($test_case as $input => $expected) { + // Test stream [URI]. + $output = file_get_mimetype($prefix . $input); + $this->assertIdentical($output, $expected, t('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); + + // Test normal path equivalent $output = file_get_mimetype($input); $this->assertIdentical($output, $expected, t('Mimetype (using mappings from variable) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); } @@ -2098,8 +2055,8 @@ class StreamWrapperRegistryTest extends DrupalWebTestCase { } function setUp() { - drupal_static_reset('file_get_stream_wrappers'); parent::setUp('file_test'); + drupal_static_reset('file_get_stream_wrappers'); } function tearDown() { @@ -2139,9 +2096,9 @@ class StreamWrapperRegistryTest extends DrupalWebTestCase { $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.')); // Test file_stream_wrapper_uri_normalize. - $uri = 'public:///' . $this->originalFileDirectory . '/foo/bar/'; + $uri = 'public:///' . file_directory_path() . '/foo/bar/'; $uri = file_stream_wrapper_uri_normalize($uri); - $this->assertEqual('public://foo/bar', $uri, t('Got a properly normalized URI')); + $this->assertEqual('public://foo/bar', $uri, t('Got a properly normalized URI @uri', array('@uri' => $uri))); // Test file_uri_taget(). $this->assertEqual('foo/bar.txt', file_uri_target('public://foo/bar.txt'), t('Got a valid stream target from public://foo/bar.txt')); diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module index dc6a56b4ed237953dd94d004b9f71bef9652d2ae..9670d1ab323ec597217c4d9c28462f704d6449cb 100644 --- a/modules/simpletest/tests/file_test.module +++ b/modules/simpletest/tests/file_test.module @@ -70,7 +70,7 @@ function _file_test_form_submit(&$form, &$form_state) { $file = file_save_upload('file_test_upload', array('file_validate_is_image' => array()), FALSE, $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->filepath))); + drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri))); drupal_set_message(t('You WIN!')); } elseif ($file === FALSE) { diff --git a/modules/simpletest/tests/filetransfer.test b/modules/simpletest/tests/filetransfer.test index 4320c1d2a218f69db05a2443896d4cf415c4d4b8..37e14fdd49880a653e1ae9407156b25c4f11a906 100644 --- a/modules/simpletest/tests/filetransfer.test +++ b/modules/simpletest/tests/filetransfer.test @@ -36,7 +36,7 @@ class FileTranferTest extends DrupalWebTestCase { } function _buildFakeModule() { - $location = file_directory_temp() . '/fake'; + $location = file_directory_path('temporary') . '/fake'; if (is_dir($location)) { $ret = 0; $output = array(); diff --git a/modules/simpletest/tests/image.test b/modules/simpletest/tests/image.test index c5fb93fea0c0ea56e46eeda260b3bbc7f87576e1..7047576b636b0427376d893968276268cf695d4e 100644 --- a/modules/simpletest/tests/image.test +++ b/modules/simpletest/tests/image.test @@ -444,7 +444,7 @@ class ImageToolkitGdTestCase extends DrupalWebTestCase { } $directory = file_directory_path() . '/imagetests'; - file_check_directory($directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($directory, FILE_CREATE_DIRECTORY); image_save($image, $directory . '/' . $op . '.' . $image->info['extension']); $this->assertTrue($correct_dimensions_real, t('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op))); diff --git a/modules/simpletest/tests/session.test b/modules/simpletest/tests/session.test index 536f2abf1c766511fac0ab9e325c6296e5d5880c..6a2b4d32d36910225790aed56d16dce60bc25ca6 100644 --- a/modules/simpletest/tests/session.test +++ b/modules/simpletest/tests/session.test @@ -219,7 +219,7 @@ class SessionTestCase extends DrupalWebTestCase { $this->loggedInUser = FALSE; // Change cookie file for user. - $this->cookieFile = file_directory_temp() . '/cookie.' . $uid . '.txt'; + $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt'; $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile; $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE; $this->drupalGet('session-test/get'); diff --git a/modules/system/image.gd.inc b/modules/system/image.gd.inc index ad76defff3008829e1f1237f4ac988a6cae0becb..706395439194af44a5f458900c92a2de2cef0510 100644 --- a/modules/system/image.gd.inc +++ b/modules/system/image.gd.inc @@ -254,6 +254,11 @@ function image_gd_load(stdClass $image) { * @see image_save() */ function image_gd_save(stdClass $image, $destination) { + // Convert URI to a normal path because PHP apparently has some gaps in stream wrapper support. + if ($wrapper = file_stream_wrapper_get_instance_by_uri($destination)) { + $destination = $wrapper->realpath(); + } + $extension = str_replace('jpg', 'jpeg', $image->info['extension']); $function = 'image' . $extension; if (!function_exists($function)) { diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 33b773bcb9f05279c37d50d76877269b9080ebd4..6a0e302dbfc6f2475ad0fdeb88ee66bab4796bda 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -365,7 +365,9 @@ function system_themes_form_submit($form, &$form_state) { */ function system_theme_settings(&$form_state, $key = '') { $directory_path = file_directory_path(); - file_check_directory($directory_path, FILE_CREATE_DIRECTORY, 'file_directory_path'); + if (!file_prepare_directory($directory_path, FILE_CREATE_DIRECTORY)) { + drupal_set_message(t('The directory %directory does not exist or is not writable.', array('%directory' => $directory_path)), 'warning'); + } // Default settings are defined in theme_get_settings() in includes/theme.inc if ($key) { @@ -389,7 +391,7 @@ function system_theme_settings(&$form_state, $key = '') { // The image was saved using file_save_upload() and was added to the // files table as a temporary file. We'll make a copy and let the garbage // collector delete the original upload. - if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) { + if ($filepath = file_unmanaged_copy($file->uri, $filename, FILE_EXISTS_REPLACE)) { $_POST['default_logo'] = 0; $_POST['logo_path'] = $filepath; $_POST['toggle_logo'] = 1; @@ -404,7 +406,7 @@ function system_theme_settings(&$form_state, $key = '') { // The image was saved using file_save_upload() and was added to the // files table as a temporary file. We'll make a copy and let the garbage // collector delete the original upload. - if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) { + if ($filepath = file_unmanaged_copy($file->uri, $filename, FILE_EXISTS_REPLACE)) { $_POST['default_favicon'] = 0; $_POST['favicon_path'] = $filepath; $_POST['toggle_favicon'] = 1; @@ -1379,20 +1381,14 @@ function system_performance_settings() { '#description' => t('The minimum amount of time that will elapse before the caches are recreated.') ); - $directory = file_directory_path(); + $directory = 'public://'; $is_writable = is_dir($directory) && is_writable($directory); - $public_downloads = (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); - $disabled = !$is_writable && !$public_downloads; + $disabled = !$is_writable; $disabled_message = ''; - if(!$is_writable && !$public_downloads) { - $disabled_message = ' ' . t('Please set up the files directory and set the download method to public to make these optimizations available.', array('!file-system' => url('admin/settings/file-system'))); - } - elseif(!$is_writable) { - $disabled_message = ' ' . t('Please set up the files directory to make these optimizations available.', array('!file-system' => url('admin/settings/file-system'))); - } - elseif(!$public_downloads) { - $disabled_message = ' ' . t('Please set the download method to public to make these optimizations available.', array('!file-system' => url('admin/settings/file-system'))); + if(!$is_writable) { + $disabled_message = ' ' . t('Please set up the public files directory to make these optimizations available.', array('!file-system' => url('admin/settings/file-system'))); } + $form['bandwidth_optimization'] = array( '#type' => 'fieldset', '#title' => t('Bandwidth optimization'), @@ -1447,30 +1443,43 @@ function system_clear_cache_submit($form, &$form_state) { */ function system_file_system_settings() { - $form['file_directory_path'] = array( + $form['file_public_path'] = array( '#type' => 'textfield', - '#title' => t('File system path'), + '#title' => t('Public file system path'), '#default_value' => file_directory_path(), '#maxlength' => 255, - '#description' => t('A file system path where the files will be stored. This directory must exist and be writable by Drupal. If the download method is set to public, this directory must be relative to the Drupal installation directory and be accessible over the web. If the download method is set to private, this directory should not be accessible over the web. Changing this location will modify all download paths and may cause unexpected problems on an existing site.'), + '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'), '#after_build' => array('system_check_directory'), ); - $form['file_directory_temp'] = array( + $form['file_private_path'] = array( '#type' => 'textfield', - '#title' => t('Temporary directory'), - '#default_value' => file_directory_temp(), + '#title' => t('Private file system path'), + '#default_value' => file_directory_path('private'), '#maxlength' => 255, - '#description' => t('A file system path where uploaded files will be stored during previews.'), + '#description' => t('A local file system path where private files will be stored. This directory must exist and be writable by Drupal. This directory should not be accessible over the web.'), '#after_build' => array('system_check_directory'), ); - $form['file_downloads'] = array( + $form['file_temporary_path'] = array( + '#type' => 'textfield', + '#title' => t('Temporary directory'), + '#default_value' => file_directory_path('temporary'), + '#maxlength' => 255, + '#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'), + '#after_build' => array('system_check_directory'), + ); + $wrappers = file_get_stream_wrappers(); + $options = array( + 'public' => $wrappers['public']['description'], + 'private' => $wrappers['private']['description'] + ); + $form['file_default_scheme'] = array( '#type' => 'radios', - '#title' => t('Download method'), - '#default_value' => FILE_DOWNLOADS_PUBLIC, - '#options' => array(FILE_DOWNLOADS_PUBLIC => t('Public - files are available using HTTP directly.'), FILE_DOWNLOADS_PRIVATE => t('Private - files are transferred by Drupal.')), - '#description' => t('Choose the Public download method unless you wish to enforce fine-grained access controls over file downloads. Changing the download method will modify all download paths and may cause unexpected problems on an existing site.') + '#title' => t('Default download method'), + '#default_value' => 'public', + '#options' => $options, + '#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'), ); return system_settings_form($form, TRUE); diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 282e2e6c3afab67d9b90481191dccb0d1d70fe5c..05070ae4d4dba42f78890439dbaab0355e650e28 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1315,8 +1315,10 @@ function hook_file_delete($file) { */ function hook_file_download($filepath) { // Check if the file is controlled by the current module. - $filepath = file_create_path($filepath); - $result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = :filepath", array('filepath' => $filepath)); + if (!file_prepare_directory($filepath)) { + $filepath = FALSE; + } + $result = db_query("SELECT f.* FROM {file} f INNER JOIN {upload} u ON f.fid = u.fid WHERE uri = :filepath", array('filepath' => $filepath)); foreach ($result as $file) { if (!user_access('view uploaded files')) { return -1; diff --git a/modules/system/system.install b/modules/system/system.install index 16d7c6b73fcdb8942800444d7c93e7538180bef5..3ff1c5905dc82d643e5a12e25713bec0151fddde 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -176,49 +176,55 @@ function system_requirements($phase) { ); } - // Test files directory - $directory = file_directory_path(); + // Test files directories. + $directories = array( + variable_get('file_public_path', conf_path() . '/files'), + variable_get('file_private_path', conf_path() . '/private/files'), + variable_get('file_temporary_path', conf_path() . '/private/temp'), + ); $requirements['file system'] = array( 'title' => $t('File system'), ); - // For installer, create the directory if possible. - if ($phase == 'install' && !is_dir($directory) && @mkdir($directory)) { - @chmod($directory, 0775); // Necessary for non-webserver users. - } - - $is_writable = is_writable($directory); - $is_directory = is_dir($directory); - if (!$is_writable || !$is_directory) { - $description = ''; - $requirements['file system']['value'] = $t('Not writable'); - if (!$is_directory) { - $error = $t('The directory %directory does not exist.', array('%directory' => $directory)); - } - else { - $error = $t('The directory %directory is not writable.', array('%directory' => $directory)); - } - // The files directory requirement check is done only during install and runtime. - if ($phase == 'runtime') { - $description = $error . ' ' . $t('You may need to set the correct directory at the file system settings page or change the current directory\'s permissions so that it is writable.', array('@admin-file-system' => url('admin/settings/file-system'))); - } - elseif ($phase == 'install') { - // For the installer UI, we need different wording. 'value' will - // be treated as version, so provide none there. - $description = $error . ' ' . $t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually, or ensure that the installer has the permissions to create it automatically. For more information, please see INSTALL.txt or the online handbook.', array('@handbook_url' => 'http://drupal.org/server-permissions')); - $requirements['file system']['value'] = ''; - } - if (!empty($description)) { - $requirements['file system']['description'] = $description; - $requirements['file system']['severity'] = REQUIREMENT_ERROR; + $error = ''; + // For installer, create the directories if possible. + foreach ($directories as $directory) { + if ($phase == 'install') { + file_prepare_directory($directory, FILE_CREATE_DIRECTORY); } - } - else { - if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) { - $requirements['file system']['value'] = $t('Writable (public download method)'); + $is_writable = is_writable($directory); + $is_directory = is_dir($directory); + if (!$is_writable || !$is_directory) { + $description = ''; + $requirements['file system']['value'] = $t('Not writable'); + if (!$is_directory) { + $error .= $t('The directory %directory does not exist.', array('%directory' => $directory)) . ' '; + } + else { + $error .= $t('The directory %directory is not writable.', array('%directory' => $directory)) . ' '; + } + // The files directory requirement check is done only during install and runtime. + if ($phase == 'runtime') { + $description = $error . $t('You may need to set the correct directory at the file system settings page or change the current directory\'s permissions so that it is writable.', array('@admin-file-system' => url('admin/settings/file-system'))); + } + elseif ($phase == 'install') { + // For the installer UI, we need different wording. 'value' will + // be treated as version, so provide none there. + $description = $error . $t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually, or ensure that the installer has the permissions to create it automatically. For more information, please see INSTALL.txt or the online handbook.', array('@handbook_url' => 'http://drupal.org/server-permissions')); + $requirements['file system']['value'] = ''; + } + if (!empty($description)) { + $requirements['file system']['description'] = $description; + $requirements['file system']['severity'] = REQUIREMENT_ERROR; + } } else { - $requirements['file system']['value'] = $t('Writable (private download method)'); + if (variable_get('file_default_scheme', 'public') == 'public') { + $requirements['file system']['value'] = $t('Writable (public download method)'); + } + else { + $requirements['file system']['value'] = $t('Writable (private download method)'); + } } } @@ -673,7 +679,7 @@ function system_schema() { $schema['cache_registry'] = $schema['cache']; $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.'; - $schema['files'] = array( + $schema['file'] = array( 'description' => 'Stores information for uploaded files.', 'fields' => array( 'fid' => array( @@ -696,7 +702,7 @@ function system_schema() { 'not null' => TRUE, 'default' => '', ), - 'filepath' => array( + 'uri' => array( 'description' => 'Path of the file relative to Drupal root.', 'type' => 'varchar', 'length' => 255, @@ -736,6 +742,9 @@ function system_schema() { 'status' => array('status'), 'timestamp' => array('timestamp'), ), + 'unique keys' => array( + 'uri' => array('uri'), + ), 'primary key' => array('fid'), 'foreign keys' => array( 'uid' => array('users' => 'uid'), @@ -2253,6 +2262,126 @@ function system_update_7033() { return array(); } +/** + * Migrate the file_downloads setting and create the new {file} table. + */ +function system_update_7034() { + $ret = array(); + $files_directory = variable_get('file_directory_path', NULL); + if (variable_get('file_downloads', 1) == 1) { + // Our default is public, so we don't need to set anything. + if (!empty($files_directory)) { + variable_set('file_public_path', $files_directory); + } + } + elseif (variable_get('file_downloads', 1) == 2) { + variable_set('file_default_scheme', 'private'); + if (!empty($files_directory)) { + variable_set('file_private_path', $files_directory); + } + } + variable_del('file_downloads'); + + $schema['file'] = array( + 'description' => 'Stores information for uploaded files.', + 'fields' => array( + 'fid' => array( + 'description' => 'File ID.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'description' => 'The {user}.uid of the user who is associated with the file.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'filename' => array( + 'description' => 'Name of the file with no path components. This may differ from the basename of the filepath if the file is renamed to avoid overwriting an existing file.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'uri' => array( + 'description' => 'URI of file.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'filemime' => array( + 'description' => "The file's MIME type.", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'filesize' => array( + 'description' => 'The size of the file in bytes.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'description' => 'A bitmapped field indicating the status of the file the least sigifigant bit indicates temporary (1) or permanent (0). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'timestamp' => array( + 'description' => 'UNIX timestamp for when the file was added.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'uid' => array('uid'), + 'status' => array('status'), + 'timestamp' => array('timestamp'), + ), + 'unique keys' => array( + 'uri' => array('uri'), + ), + 'primary key' => array('fid'), + ); + db_create_table($ret, 'file', $schema['file']); + return $ret; +} + +/** + * Migrate upload module files to the new {file} table. + */ +function system_update_7035() { + $ret = array(); + if (!db_table_exists('upload')) { + return $ret; + } + // The old {files} tables still exists. We migrate core data from upload + // module, but any contrib module using it will need to do its own update. + $result = db_query('SELECT fid, uid, filename, filepath AS uri, filemime, filesize, status, timestamp FROM {files} f INNER JOIN {upload} u ON u.fid = f.fid', array(), array('fetch' => PDO::FETCH_ASSOC)); + + // We will convert filepaths to uri using the default schmeme + // and stripping off the existing file directory path. + $basename = variable_get('file_directory_path', conf_path() . '/files'); + $scheme = variable_get('file_default_scheme', 'public') . '://'; + $fids = array(); + // TODO: does this function need to run in batch mode? + foreach ($result as $file) { + $file['uri'] = $scheme . str_replace($basename, '', $file['uri']); + $file['uri'] = file_stream_wrapper_uri_normalize($file['uri']); + db_insert('file')->fields($file)->execute(); + $fids[] = $file['fid']; + } + // TODO: delete the found fids from {files}? + return $ret; +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/system/system.module b/modules/system/system.module index 001949c0417662ae7d2c3f5af419704a999533e5..5a63d4a65bcd87d0f984ffedeed795e3ed0b05be 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -473,6 +473,14 @@ function system_menu() { $items['system/files'] = array( 'title' => 'File download', 'page callback' => 'file_download', + 'page arguments' => array('private'), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + $items['system/temporary'] = array( + 'title' => 'Temporary files', + 'page callback' => 'file_download', + 'page arguments' => array('temporary'), 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); @@ -1589,16 +1597,37 @@ function system_admin_menu_block($item) { /** * Checks the existence of the directory specified in $form_element. This - * function is called from the system_settings form to check both the - * file_directory_path and file_directory_temp directories. If validation - * fails, the form element is flagged with an error from within the - * file_check_directory function. + * function is called from the system_settings form to check both core file + * directories (file_public_path, file_private_path, file_temporary_path). * * @param $form_element * The form element containing the name of the directory to check. */ function system_check_directory($form_element) { - file_check_directory($form_element['#value'], FILE_CREATE_DIRECTORY, $form_element['#parents'][0]); + $directory = $form_element['#value']; + + if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) { + // If the directory does not exists and cannot be created. + form_set_error($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', array('%directory' => $directory))); + watchdog('file system', 'The directory %directory does not exist and could not be created.', array('%directory' => $directory), WATCHDOG_ERROR); + } + + if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) { + // If the directory is not writable and cannont be made so. + form_set_error($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory))); + watchdog('file system', 'The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory), WATCHDOG_ERROR); + } + else { + if ($form_element['#name'] == 'file_public_path') { + // Create public .htaccess file. + file_create_htaccess($directory, FALSE); + } + else { + // Create private .htaccess file. + file_create_htaccess($directory); + } + } + return $form_element; } @@ -1615,7 +1644,7 @@ function system_get_files_database(&$files, $type) { $result = db_query("SELECT filename, name, type, status, schema_version, weight FROM {system} WHERE type = :type", array(':type' => $type)); foreach ($result as $file) { if (isset($files[$file->name]) && is_object($files[$file->name])) { - $file->filepath = $file->filename; + $file->uri = $file->filename; foreach ($file as $key => $value) { if (!isset($files[$file->name]->key)) { $files[$file->name]->$key = $value; @@ -1691,7 +1720,7 @@ function system_update_files_database(&$files, $type) { } else { $query->values(array( - 'filename' => $file->filepath, + 'filename' => $file->uri, 'name' => $file->name, 'type' => $type, 'owner' => isset($file->owner) ? $file->owner : '', @@ -1729,7 +1758,7 @@ function _system_get_module_data() { // Read info files for each module. foreach ($modules as $key => $module) { // Look for the info file. - $module->info = drupal_parse_info_file(dirname($module->filepath) . '/' . $module->name . '.info'); + $module->info = drupal_parse_info_file(dirname($module->uri) . '/' . $module->name . '.info'); // Skip modules that don't provide info. if (empty($module->info)) { @@ -1811,8 +1840,8 @@ function _system_get_theme_data() { $sub_themes = array(); // Read info files for each theme foreach ($themes as $key => $theme) { - $themes[$key]->filename = $theme->filepath; - $themes[$key]->info = drupal_parse_info_file($theme->filepath) + $defaults; + $themes[$key]->filename = $theme->uri; + $themes[$key]->info = drupal_parse_info_file($theme->uri) + $defaults; // Invoke hook_system_info_alter() to give installed modules a chance to // modify the data in the .info files if necessary. @@ -1822,7 +1851,7 @@ function _system_get_theme_data() { $sub_themes[] = $key; } if (empty($themes[$key]->info['engine'])) { - $filename = dirname($themes[$key]->filepath) . '/' . $themes[$key]->name . '.theme'; + $filename = dirname($themes[$key]->uri) . '/' . $themes[$key]->name . '.theme'; if (file_exists($filename)) { $themes[$key]->owner = $filename; $themes[$key]->prefix = $key; @@ -1831,7 +1860,7 @@ function _system_get_theme_data() { else { $engine = $themes[$key]->info['engine']; if (isset($engines[$engine])) { - $themes[$key]->owner = $engines[$engine]->filepath; + $themes[$key]->owner = $engines[$engine]->uri; $themes[$key]->prefix = $engines[$engine]->name; $themes[$key]->template = TRUE; } @@ -1842,7 +1871,7 @@ function _system_get_theme_data() { if (isset($themes[$key]->info['stylesheets'])) { foreach ($themes[$key]->info['stylesheets'] as $media => $stylesheets) { foreach ($stylesheets as $stylesheet) { - $pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->filepath) . '/' . $stylesheet; + $pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->uri) . '/' . $stylesheet; } } } @@ -1852,13 +1881,13 @@ function _system_get_theme_data() { $scripts = array(); if (isset($themes[$key]->info['scripts'])) { foreach ($themes[$key]->info['scripts'] as $script) { - $scripts[$script] = dirname($themes[$key]->filepath) . '/' . $script; + $scripts[$script] = dirname($themes[$key]->uri) . '/' . $script; } } $themes[$key]->info['scripts'] = $scripts; // Give the screenshot proper path information. if (!empty($themes[$key]->info['screenshot'])) { - $themes[$key]->info['screenshot'] = dirname($themes[$key]->filepath) . '/' . $themes[$key]->info['screenshot']; + $themes[$key]->info['screenshot'] = dirname($themes[$key]->uri) . '/' . $themes[$key]->info['screenshot']; } } @@ -2225,7 +2254,7 @@ function system_cron() { // Remove temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE. // Use separate placeholders for the status to avoid a bug in some versions // of PHP. See http://drupal.org/node/352956 - $result = db_query('SELECT fid FROM {files} WHERE status & :permanent1 <> :permanent2 AND timestamp < :timestamp', array( + $result = db_query('SELECT fid FROM {file} WHERE status & :permanent1 <> :permanent2 AND timestamp < :timestamp', array( ':permanent1' => FILE_STATUS_PERMANENT, ':permanent2' => FILE_STATUS_PERMANENT, ':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE @@ -2233,7 +2262,7 @@ function system_cron() { foreach ($result as $row) { if ($file = file_load($row->fid)) { if (!file_delete($file)) { - watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath), WATCHDOG_ERROR); + watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->uri), WATCHDOG_ERROR); } } } @@ -3006,7 +3035,7 @@ function system_image_toolkits() { */ function system_retrieve_file($url, $destination = NULL, $overwrite = TRUE) { if (!$destination) { - $destination = file_directory_temp(); + $destination = file_directory_path('temporary'); } $parsed_url = parse_url($url); $local = is_dir(file_directory_path() . '/' . $destination) ? $destination . '/' . basename($parsed_url['path']) : $destination; diff --git a/modules/system/system.tar.inc b/modules/system/system.tar.inc index be0e1b89ec6355d3e3c8386cd62e643df0e8fb1d..948a793d4bda455530418baee952757844fdf844 100644 --- a/modules/system/system.tar.inc +++ b/modules/system/system.tar.inc @@ -1582,7 +1582,7 @@ function _extractList($p_path, &$p_list_detail, $p_mode, if ($v_extract_file) { if ($v_header['typeflag'] == "5") { if (!@file_exists($v_header['filename'])) { - if (!@mkdir($v_header['filename'], 0777)) { + if (!@drupal_mkdir($v_header['filename'], 0777)) { $this->_error('Unable to create directory {' .$v_header['filename'].'}'); return false; @@ -1795,7 +1795,7 @@ function _dirCheck($p_dir) (!$this->_dirCheck($p_parent_dir))) return false; - if (!@mkdir($p_dir, 0777)) { + if (!@drupal_mkdir($p_dir, 0777)) { $this->_error("Unable to create directory '$p_dir'"); return false; } diff --git a/modules/system/system.test b/modules/system/system.test index b128671754925bdca7f49c31b065d84eaa3417ff..ba33835e25e71723170d26f3b5350c2479f69a21 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -400,41 +400,41 @@ class CronRunTestCase extends DrupalWebTestCase { // Temporary file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE. $temp_old = file_save_data(''); - db_update('files') + db_update('file') ->fields(array( 'status' => 0, 'timestamp' => 1, )) ->condition('fid', $temp_old->fid) ->execute(); - $this->assertTrue(file_exists($temp_old->filepath), t('Old temp file was created correctly.')); + $this->assertTrue(file_exists($temp_old->uri), t('Old temp file was created correctly.')); // Temporary file that is less than DRUPAL_MAXIMUM_TEMP_FILE_AGE. $temp_new = file_save_data(''); - db_update('files') + db_update('file') ->fields(array('status' => 0)) ->condition('fid', $temp_new->fid) ->execute(); - $this->assertTrue(file_exists($temp_new->filepath), t('New temp file was created correctly.')); + $this->assertTrue(file_exists($temp_new->uri), t('New temp file was created correctly.')); // Permanent file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE. $perm_old = file_save_data(''); - db_update('files') + db_update('file') ->fields(array('timestamp' => 1)) ->condition('fid', $temp_old->fid) ->execute(); - $this->assertTrue(file_exists($perm_old->filepath), t('Old permanent file was created correctly.')); + $this->assertTrue(file_exists($perm_old->uri), t('Old permanent file was created correctly.')); // Permanent file that is newer than DRUPAL_MAXIMUM_TEMP_FILE_AGE. $perm_new = file_save_data(''); - $this->assertTrue(file_exists($perm_new->filepath), t('New permanent file was created correctly.')); + $this->assertTrue(file_exists($perm_new->uri), t('New permanent file was created correctly.')); // Run cron and then ensure that only the old, temp file was deleted. $this->assertTrue(drupal_cron_run(), t('Cron ran successfully.')); - $this->assertFalse(file_exists($temp_old->filepath), t('Old temp file was correctly removed.')); - $this->assertTrue(file_exists($temp_new->filepath), t('New temp file was correctly ignored.')); - $this->assertTrue(file_exists($perm_old->filepath), t('Old permanent file was correctly ignored.')); - $this->assertTrue(file_exists($perm_new->filepath), t('New permanent file was correctly ignored.')); + $this->assertFalse(file_exists($temp_old->uri), t('Old temp file was correctly removed.')); + $this->assertTrue(file_exists($temp_new->uri), t('New temp file was correctly ignored.')); + $this->assertTrue(file_exists($perm_old->uri), t('Old permanent file was correctly ignored.')); + $this->assertTrue(file_exists($perm_new->uri), t('New permanent file was correctly ignored.')); } } diff --git a/modules/update/update.compare.inc b/modules/update/update.compare.inc index 886f83b1a0682fbbbb9120f564eb3e24c55095e9..0cfc83775aafbcaddd0aa8f213e8cac029df7469 100644 --- a/modules/update/update.compare.inc +++ b/modules/update/update.compare.inc @@ -80,7 +80,7 @@ function _update_process_info_list(&$projects, $list, $project_type) { // which is left alone by tar and correctly set to the time the .info file // was unpacked. if (!isset($file->info['_info_file_ctime'])) { - $info_filename = dirname($file->filepath) . '/' . $file->name . '.info'; + $info_filename = dirname($file->uri) . '/' . $file->name . '.info'; $file->info['_info_file_ctime'] = filectime($info_filename); } diff --git a/modules/upload/upload.install b/modules/upload/upload.install index a4d22bea6247d2ff1d750d49dcf7fea6078e9746..395abfebf127647335c70be2ed98de83b3b38eb7 100644 --- a/modules/upload/upload.install +++ b/modules/upload/upload.install @@ -43,7 +43,7 @@ function upload_schema() { 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, - 'description' => 'Primary Key: The {files}.fid.', + 'description' => 'Primary Key: The {file}.fid.', ), 'nid' => array( 'type' => 'int', @@ -98,3 +98,58 @@ function upload_schema() { } +/** + * Migrate upload module files from {files} to {file}. + */ +function upload_update_7000(&$sandbox) { + $ret = array(); + + /* + TODO: Fix the updates. This is broken. See http://drupal.org/node/329301#comment-1404336 + Also note new DB structure http://drupal.org/node/227232#comment-1683976 + */ + + if (!isset($sandbox['progress'])) { + // Initialize batch update information. + $sandbox['progress'] = 0; + $sandbox['last_fid_processed'] = -1; + $sandbox['max'] = db_query("SELECT COUNT(DISTINCT u.fid) FROM {upload} u")->fetchField(); + } + + // As a batch operation move records from {files} into the {file} table. + $limit = 500; + $result = db_query_range("SELECT DISTINCT u.fid FROM {upload} u ORDER BY u.vid", array(), 0, $limit); + foreach ($result as $record) { + $old_file = db_query('SELECT f.* FROM {files} f WHERE f.fid = :fid', array(':fid' => $record->fid))->fetch(PDO::FETCH_OBJ); + if (!$old_file) { + continue; + } + + $new_file = db_query('SELECT f.* FROM {files} f WHERE f.filepath = :filepath', array(':filepath' => $old_file->uri))->fetch(PDO::FETCH_OBJ); + if (!$new_file) { + // Re-save the file into the new {file} table. + $new_file = clone $old_file; + drupal_write_record('file', $new_file); + } + + // If the fid has changed we need to update the {upload} record to use the + // new id. + if (!empty($new_file->fid) && ($new_file->fid != $old_file->fid)) { + db_update('upload') + ->fields(array('fid' => $new_file->fid)) + ->condition('fid', $old_file->fid) + ->execute(); + } + + // Update our progress information for the batch update. + $sandbox['progress']++; + $sandbox['last_fid_processed'] = $old_file->fid; + } + + // Indicate our current progress to the batch update system. If there's no + // max value then there's nothing to update and we're finished. + $ret['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); + + return $ret; +} + diff --git a/modules/upload/upload.module b/modules/upload/upload.module index 772e329ed94d64d44f11989e578bdc389ff5c389..eec81dbe73cc34675cadfc0da30b1d1b642bdafc 100644 --- a/modules/upload/upload.module +++ b/modules/upload/upload.module @@ -150,8 +150,7 @@ function _upload_file_limits($user) { * Implement hook_file_download(). */ function upload_file_download($filepath) { - $filepath = file_create_path($filepath); - $file = db_query("SELECT f.*, u.nid FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = :path", array(':path' => $filepath))->fetchObject(); + $file = db_query("SELECT f.*, u.nid FROM {file} f INNER JOIN {upload} u ON f.fid = u.fid WHERE uri = :path", array(':path' => $filepath))->fetchObject(); if ($file && user_access('view uploaded files') && ($node = node_load($file->nid)) && node_access('view', $node)) { return array( @@ -182,7 +181,7 @@ function upload_node_form_submit(&$form, &$form_state) { ); // Save new file uploads. - if (user_access('upload files') && ($file = file_save_upload('upload', $validators, file_directory_path()))) { + if (user_access('upload files') && ($file = file_save_upload('upload', $validators, 'public://'))) { $file->list = variable_get('upload_list_default', 1); $file->description = $file->filename; $file->weight = 0; @@ -245,9 +244,9 @@ function upload_form_alter(&$form, $form_state, $form_id) { // Make sure necessary directories for upload.module exist and are // writable before displaying the attachment form. $path = file_directory_path(); - $temp = file_directory_temp(); + $temp = file_directory_path('temporary'); // Note: pass by reference - if (!file_check_directory($path, FILE_CREATE_DIRECTORY) || !file_check_directory($temp, FILE_CREATE_DIRECTORY)) { + if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY) || !file_prepare_directory($temp, FILE_CREATE_DIRECTORY)) { $form['attachments']['#description'] = t('File attachments are disabled. The file directories have not been properly configured.'); if (user_access('administer site configuration')) { $form['attachments']['#description'] .= ' ' . t('Please visit the file system configuration page.', array('@admin-file-system' => url('admin/settings/file-system'))); @@ -372,7 +371,7 @@ function upload_node_view($node, $build_mode) { $node->rss_elements[] = array( 'key' => 'enclosure', 'attributes' => array( - 'url' => file_create_url($file->filepath), + 'url' => file_create_url($file->uri), 'length' => $file->filesize, 'type' => $file->filemime ) @@ -443,7 +442,7 @@ function theme_upload_attachments($elements) { foreach ($elements['#files'] as $file) { $file = (object)$file; if ($file->list && empty($file->remove)) { - $href = file_create_url($file->filepath); + $href = file_create_url($file->uri); $text = $file->description ? $file->description : $file->filename; $rows[] = array(l($text, $href), format_size($file->filesize)); } @@ -472,7 +471,7 @@ function upload_space_used($uid) { * The amount of disk space used by uploaded files in bytes. */ function upload_total_space_used() { - return db_query('SELECT SUM(f.filesize) FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid')->fetchField(); + return db_query('SELECT SUM(f.filesize) FROM {file} f INNER JOIN {upload} u ON f.fid = u.fid')->fetchField(); } function upload_save($node) { @@ -543,13 +542,13 @@ function _upload_form($node) { $file = (object)$file; $key = $file->fid; - $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => '' . file_create_url($file->filepath) . ''); + $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => '' . file_create_url($file->uri) . ''); $form['files'][$key]['size'] = array('#markup' => format_size($file->filesize)); $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => !empty($file->remove)); $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); $form['files'][$key]['weight'] = array('#type' => 'weight', '#delta' => count($node->files), '#default_value' => $file->weight); $form['files'][$key]['filename'] = array('#type' => 'value', '#value' => $file->filename); - $form['files'][$key]['filepath'] = array('#type' => 'value', '#value' => $file->filepath); + $form['files'][$key]['uri'] = array('#type' => 'value', '#value' => $file->uri); $form['files'][$key]['filemime'] = array('#type' => 'value', '#value' => $file->filemime); $form['files'][$key]['filesize'] = array('#type' => 'value', '#value' => $file->filesize); $form['files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid); diff --git a/modules/upload/upload.test b/modules/upload/upload.test index bf9b22bbb0aa892f91dd238845c899eeee44c627..da0aafe6a817de56884e85fb3c05e3b5bb16dbde 100644 --- a/modules/upload/upload.test +++ b/modules/upload/upload.test @@ -45,7 +45,7 @@ class UploadTestCase extends DrupalWebTestCase { // Create a node and attempt to attach files. $node = $this->drupalCreateNode(); $text_files = $this->drupalGetTestFiles('text'); - $files = array(current($text_files)->filepath, next($text_files)->filepath); + $files = array(current($text_files)->uri, next($text_files)->uri); $this->uploadFile($node, $files[0]); $this->uploadFile($node, $files[1]); @@ -58,7 +58,7 @@ class UploadTestCase extends DrupalWebTestCase { $this->checkUploadedFile(basename($files[1])); // Check that files are also accessible when using private files. - variable_set('file_downloads', FILE_DOWNLOADS_PRIVATE); + variable_set('file_default_scheme', 'private'); $this->checkUploadedFile(basename($files[0])); $this->checkUploadedFile(basename($files[1])); @@ -86,7 +86,9 @@ class UploadTestCase extends DrupalWebTestCase { $this->assertRaw(t('Page %title has been updated.', array('%title' => $node->title)), 'File deleted successfully.'); $this->assertNoText($new_name, $new_name . ' not found on node.'); - $this->drupalGet($base_url . '/' . file_directory_path() . '/' . $upload->description, array('external' => TRUE)); + $uri = 'public://' . $upload->description; + $external_uri = file_stream_wrapper_get_instance_by_uri($uri)->getExternalUrl(); + $this->drupalGet($external_uri, array('external' => TRUE)); $this->assertResponse(array(404), 'Uploaded ' . $upload->description . ' is not accessible.'); } else { @@ -119,17 +121,17 @@ class UploadTestCase extends DrupalWebTestCase { $text_file = current($this->drupalGetTestFiles('text')); // Select a file that's less than the 1MB upload limit so we only test one // limit at a time. - $this->uploadFile($node, $text_file->filepath, FALSE); + $this->uploadFile($node, $text_file->uri, FALSE); // Test the error message in two steps in case there are additional errors // that change the error message's format. - $this->assertRaw(t('The specified file %name could not be uploaded.', array('%name' => $text_file->filename)), t('File %filepath was not allowed to be uploaded', array('%filepath' => $text_file->filepath))); + $this->assertRaw(t('The specified file %name could not be uploaded.', array('%name' => $text_file->filename)), t('File %filepath was not allowed to be uploaded', array('%filepath' => $text_file->uri))); $this->assertRaw(t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $settings['upload_extensions'])), t('File extension cited as reason for failure')); // Attempt to upload .html file when .html is only extension allowed. $html_files = array_values($this->drupalGetTestFiles('html')); // Use the HTML file with the .html extension, $html_files[0] has a .txt // extension. - $html_file = $html_files[1]->filepath; + $html_file = $html_files[1]->uri; $this->uploadFile($node, $html_file); $this->assertNoRaw(t('The specified file %name could not be uploaded.', array('%name' => basename($html_file))), t('File ' . $html_file . ' was allowed to be uploaded')); } @@ -139,7 +141,7 @@ class UploadTestCase extends DrupalWebTestCase { */ function testLimit() { $files = $this->drupalGetTestFiles('text', 1310720); // 1 MB. - $file = current($files)->filepath; + $file = current($files)->uri; $admin_user = $this->drupalCreateUser(array('administer site configuration')); $web_user = $this->drupalCreateUser(array('access content', 'edit own page content', 'upload files', 'view uploaded files')); @@ -206,7 +208,7 @@ class UploadTestCase extends DrupalWebTestCase { */ function checkUploadedFile($filename) { global $base_url; - $file = file_directory_path() . '/' . $filename; + $file = 'public://' . $filename; $this->drupalGet(file_create_url($file), array('external' => TRUE)); $this->assertResponse(array(200), 'Uploaded ' . $filename . ' is accessible.'); $this->assertTrue(strpos($this->drupalGetHeader('Content-Type'), 'text/plain') === 0, t('MIME type is text/plain.')); diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc index cd316e6ce178e741a0f516955859bd16c0ca62d3..3ab3e79e7169f7ec9976b6c3a14daf6054982f92 100644 --- a/modules/user/user.admin.inc +++ b/modules/user/user.admin.inc @@ -324,8 +324,11 @@ function user_admin_settings() { ); // If picture support is enabled, check whether the picture directory exists. if (variable_get('user_pictures', 0)) { - $picture_path = file_create_path(variable_get('user_picture_path', 'pictures')); - file_check_directory($picture_path, FILE_CREATE_DIRECTORY, 'user_picture_path'); + $picture_path = variable_get('file_default_scheme', 'public') . '://' . variable_get('user_picture_path', 'pictures'); + if (!file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY)) { + form_set_error('user_picture_path', t('The directory %directory does not exist or is not writable.', array('%directory' => $picture_path))); + watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $picture_path), WATCHDOG_ERROR); + } } $picture_support = variable_get('user_pictures', 0); $form['personalization']['user_pictures'] = array( diff --git a/modules/user/user.install b/modules/user/user.install index 5fdc54ecb4d09dee5042e1ee940acb94a0a82739..e70c2950cf5c5d05b382527c082dcaedf4526c07 100644 --- a/modules/user/user.install +++ b/modules/user/user.install @@ -184,7 +184,7 @@ function user_schema() { 'type' => 'int', 'not null' => TRUE, 'default' => 0, - 'description' => "Foreign key: {files}.fid of user's picture.", + 'description' => "Foreign key: {file}.fid of user's picture.", ), 'init' => array( 'type' => 'varchar', @@ -405,7 +405,7 @@ function user_update_7003() { } /** - * Add the user's pictures to the {files} table and make them managed files. + * Add the user's pictures to the {file} table and make them managed files. */ function user_update_7004(&$sandbox) { $ret = array(); @@ -414,7 +414,7 @@ function user_update_7004(&$sandbox) { 'type' => 'int', 'not null' => TRUE, 'default' => 0, - 'description' => t("Foriegn key: {files}.fid of user's picture."), + 'description' => t("Foriegn key: {file}.fid of user's picture."), ); if (!isset($sandbox['progress'])) { @@ -428,13 +428,13 @@ function user_update_7004(&$sandbox) { // Initialize batch update information. $sandbox['progress'] = 0; $sandbox['last_user_processed'] = -1; - $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE picture <> ''")->fetchField(); + $sandbox['max'] = db_query("SELECT COUNT(*) FROM {user} WHERE picture <> ''")->fetchField(); } - // As a batch operation move the photos into the {files} table and update the + // As a batch operation move the photos into the {file} table and update the // {users} records. $limit = 500; - $result = db_query_range("SELECT uid, picture FROM {users} WHERE picture <> '' AND uid > :uid ORDER BY uid", array(':uid' => $sandbox['last_user_processed']), 0, $limit); + $result = db_query_range("SELECT uid, picture FROM {user} WHERE picture <> '' AND uid > :uid ORDER BY uid", array(':uid' => $sandbox['last_user_processed']), 0, $limit); foreach ($result as $user) { // Don't bother adding files that don't exist. if (!file_exists($user->picture)) { @@ -442,16 +442,16 @@ function user_update_7004(&$sandbox) { } // Check if the file already exists. - $files = file_load_multiple(array(), array('filepath' => $user->picture)); + $files = file_load_multiple(array(), array('uri' => $user->picture)); if (count($files)) { $file = reset($files); } else { // Create a file object. $file = new stdClass(); - $file->filepath = $user->picture; - $file->filename = basename($file->filepath); - $file->filemime = file_get_mimetype($file->filepath); + $file->uri = $user->picture; + $file->filename = basename($file->uri); + $file->filemime = file_get_mimetype($file->uri); $file->uid = $user->uid; $file->status = FILE_STATUS_PERMANENT; $file = file_save($file); @@ -474,8 +474,8 @@ function user_update_7004(&$sandbox) { // When we're finished, drop the old picture field and rename the new one to // replace it. if (isset($ret['#finished']) && $ret['#finished'] == 1) { - db_drop_field($ret, 'users', 'picture'); - db_change_field($ret, 'users', 'picture_fid', 'picture', $picture_field); + db_drop_field($ret, 'user', 'picture'); + db_change_field($ret, 'user', 'picture_fid', 'picture', $picture_field); } return $ret; diff --git a/modules/user/user.module b/modules/user/user.module index b6e8dcb751198fd35b17aed31a138284b3cf230c..88a661ef7291a71e3c6f07d01313b21ab1abc9d3 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -404,8 +404,13 @@ function user_save($account, $edit = array(), $category = 'account') { // If the picture is a temporary file move it to its final location and // make it permanent. if (($picture->status & FILE_STATUS_PERMANENT) == 0) { - $info = image_get_info($picture->filepath); - $destination = file_create_path(variable_get('user_picture_path', 'pictures') . '/picture-' . $account->uid . '.' . $info['extension']); + $info = image_get_info($picture->uri); + $picture_directory = variable_get('file_default_scheme', 'public') . '://' . variable_get('user_picture_path', 'pictures'); + + // Prepare the pictures directory. + file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY); + $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '.' . $info['extension']); + if ($picture = file_move($picture, $destination, FILE_EXISTS_REPLACE)) { $picture->status |= FILE_STATUS_PERMANENT; $edit['picture'] = file_save($picture); @@ -796,8 +801,8 @@ function user_permission() { * Ensure that user pictures (avatars) are always downloadable. */ function user_file_download($filepath) { - if (strpos($filepath, variable_get('user_picture_path', 'pictures') . '/picture-') === 0) { - $info = image_get_info(file_create_path($filepath)); + if (strpos(file_uri_target($filepath), variable_get('user_picture_path', 'pictures') . '/picture-') === 0) { + $info = image_get_info($filepath); return array('Content-Type' => $info['mime_type']); } } @@ -1167,8 +1172,8 @@ function template_preprocess_user_picture(&$variables) { if (is_numeric($account->picture)) { $account->picture = file_load($account->picture); } - if (!empty($account->picture->filepath)) { - $filepath = $account->picture->filepath; + if (!empty($account->picture->uri)) { + $filepath = $account->picture->uri; } } elseif (variable_get('user_picture_default', '')) { diff --git a/modules/user/user.test b/modules/user/user.test index 6a999bad30c634ed62dddf1c9f44f94ecaf4e543..0eda8128b9db1ea9926b8dd22b55924c89743314 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -634,13 +634,14 @@ class UserPictureTestCase extends DrupalWebTestCase { $this->user = $this->drupalCreateUser(); // Test if directories specified in settings exist in filesystem. - $file_dir = file_directory_path(); - $file_check = file_check_directory($file_dir, FILE_CREATE_DIRECTORY, 'file_directory_path'); + $file_dir = 'public://'; + $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY); + // TODO: Test public and private methods? $picture_dir = variable_get('user_picture_path', 'pictures'); - $picture_path = $file_dir . '/' . $picture_dir; + $picture_path = $file_dir . $picture_dir; - $pic_check = file_check_directory($picture_path, FILE_CREATE_DIRECTORY, 'user_picture_path'); + $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY); $this->_directory_test = is_writable($picture_path); $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made."); } @@ -667,7 +668,7 @@ class UserPictureTestCase extends DrupalWebTestCase { $this->drupalLogin($this->user); $image = current($this->drupalGetTestFiles('image')); - $info = image_get_info($image->filepath); + $info = image_get_info($image->uri); // Set new variables: invalid dimensions, valid filesize (0 = no limit). $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10); @@ -704,7 +705,7 @@ class UserPictureTestCase extends DrupalWebTestCase { // Images are sorted first by size then by name. We need an image // bigger than 1 KB so we'll grab the last one. $image = end($this->drupalGetTestFiles('image')); - $info = image_get_info($image->filepath); + $info = image_get_info($image->uri); // Set new variables: valid dimensions, invalid filesize. $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); @@ -717,7 +718,7 @@ class UserPictureTestCase extends DrupalWebTestCase { // Test that the upload failed and that the correct reason was cited. $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename)); $this->assertRaw($text, t('Upload failed.')); - $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->filepath)), '%maxsize' => format_size($test_size * 1024))); + $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->uri)), '%maxsize' => format_size($test_size * 1024))); $this->assertRaw($text, t('File size cited as reason for failure.')); // Check if file is not uploaded. @@ -739,7 +740,7 @@ class UserPictureTestCase extends DrupalWebTestCase { $this->drupalLogin($this->user); $image = current($this->drupalGetTestFiles('image')); - $info = image_get_info($image->filepath); + $info = image_get_info($image->uri); // Set new variables: invalid dimensions, valid filesize (0 = no limit). $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10); @@ -772,7 +773,7 @@ class UserPictureTestCase extends DrupalWebTestCase { $this->drupalLogin($this->user); $image = current($this->drupalGetTestFiles('image')); - $info = image_get_info($image->filepath); + $info = image_get_info($image->uri); // Set new variables: valid dimensions, invalid filesize. $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); @@ -785,7 +786,7 @@ class UserPictureTestCase extends DrupalWebTestCase { // Test that the upload failed and that the correct reason was cited. $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename)); $this->assertRaw($text, t('Upload failed.')); - $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->filepath)), '%maxsize' => format_size($test_size * 1024))); + $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->uri)), '%maxsize' => format_size($test_size * 1024))); $this->assertRaw($text, t('File size cited as reason for failure.')); // Check if file is not uploaded. @@ -804,7 +805,7 @@ class UserPictureTestCase extends DrupalWebTestCase { $this->drupalLogin($this->user); $image = current($this->drupalGetTestFiles('image')); - $info = image_get_info($image->filepath); + $info = image_get_info($image->uri); // Set new variables: valid dimensions, valid filesize (0 = no limit). $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); @@ -815,7 +816,7 @@ class UserPictureTestCase extends DrupalWebTestCase { // Check if image is displayed in user's profile page. $this->drupalGet('user'); - $this->assertRaw($pic_path, t("Image is displayed in user's profile page")); + $this->assertRaw(file_uri_target($pic_path), t("Image is displayed in user's profile page")); // Check if file is located in proper directory. $this->assertTrue(is_file($pic_path), t('File is located in proper directory')); @@ -823,12 +824,12 @@ class UserPictureTestCase extends DrupalWebTestCase { } function saveUserPicture($image) { - $edit = array('files[picture_upload]' => realpath($image->filepath)); + $edit = array('files[picture_upload]' => drupal_realpath($image->uri)); $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); - $img_info = image_get_info($image->filepath); + $img_info = image_get_info($image->uri); $picture_dir = variable_get('user_picture_path', 'pictures'); - $pic_path = file_directory_path() . '/' . $picture_dir . '/picture-' . $this->user->uid . '.' . $img_info['extension']; + $pic_path = 'public://' . $picture_dir . '/picture-' . $this->user->uid . '.' . $img_info['extension']; return $pic_path; } diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 9b056b773db4ff3b6e10704015d493efded45be9..a5f8b67a83e7af8e98343cddc351e6c65850fd47 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -401,7 +401,7 @@ function simpletest_script_get_test_list() { elseif ($args['file']) { $files = array(); foreach ($args['test_names'] as $file) { - $files[realpath($file)] = 1; + $files[drupal_realpath($file)] = 1; } // Check for valid class names. diff --git a/themes/garland/template.php b/themes/garland/template.php index e1be8efc075e4c89f5b9b77e5284addd052ec981..c83a8069481d9876deceef8f7504da11f79caa05 100644 --- a/themes/garland/template.php +++ b/themes/garland/template.php @@ -79,9 +79,9 @@ function garland_node_submitted($node) { function garland_get_ie_styles() { global $language; - $ie_styles = '' . "\n"; + $ie_styles = '' . "\n"; if ($language->direction == LANGUAGE_RTL) { - $ie_styles .= ' ' . "\n"; + $ie_styles .= ' ' . "\n"; } return $ie_styles;